从 Pandas 数据帧创建 Networkx 有向图时如何创建多线节点标签

问题描述

我的问题是我的previous question的延续。

我正在尝试从 Pandas 数据帧创建 networkx 流程图。数据框记录订单如何流经多家公司。数据框中的大多数行都是连接的,并且连接表现在多列中。样本数据如下:

df = pd.DataFrame({'Company': ['A','A','B','C','C'],'event_type':['new','route','receive','execute','execute'],'event_id': ['110','120','200','210','220','300','310'],'prior_event_id': [np.nan,'110',np.nan,'300'],'route_id': [np.nan,'foo','bar',np.nan]}
             )

数据框如下所示:

  Company event_type event_id prior_event_id route_id
0       A        new      110            NaN      NaN
1       A      route      120            110      foo
2       B    receive      200            NaN      foo
3       B    execute      210            120      NaN
4       B      route      220            210      bar
5       C    receive      300            NaN      bar
6       C    execute      310            300      NaN

我能够使用以下代码从示例数据创建源列和目标列:

df['event_sub'] = df.groupby([df.Company,df.event_type]).cumcount()+1
df['event'] = df.Company + ' ' + df.event_type + ' ' + df.event_sub.astype(str)  

replace_dict_event = dict(df[['event_id','event']].values)
df['source'] = df['prior_event_id'].apply(lambda x: replace_dict_event.get(x) if replace_dict_event.get(x) else np.nan )
df['target'] = df['event_id'].apply(lambda x: replace_dict_event.get(x) if replace_dict_event.get(x) else np.nan )

replace_dict_rtd = dict(df[df.event_type == 'route'][['route_id','event']].values)
df.loc[df.event_type == 'receive','source'] = df[df.event_type == 'receive']['route_id'].apply(lambda x: replace_dict_rtd.get(x))

现在数据框看起来像这样:

df

上面的结果与我之前问题中的结果之间的细微差别是我在当前结果中加入了公司名称。我从 sourcetarget 列创建的 networkx 图如下所示:

graph

但是,我面临的问题是,在我的实际数据中,公司名称更长,节点更多。因此,很多时候标签都被挤在一起,基本上变得难以理解。我想到的第一个解决方案是将标签分成多行。我想要的节点如下所示:

node

我尝试的是在相关列中添加“\n”,因此我将最后一个代码块的第二行更改为

df['event'] = df.Company + '\n' + df.event_type + ' ' + df.event_sub.astype(str) 

但这并没有给我想要的。相反,我得到了“KeyError: 'Node A\nnew 1 not in graph.'”我尝试了一些我在 SO 上找到的其他方法,但也没有运气。

有什么办法可以实现吗?

解决方法

# dummy data
a = np.random.randint(0,2,size=(10,10))
G = nx.from_numpy_matrix(a)

pos = nx.spring_layout(G)

# draw without labels,then draw labels separately
nx.draw_networkx(G,pos=pos,with_labels=False)

# draw_networkx_labels takes as keyword argument a dictionary called labels
# which links the id of a node to a name.
# you can create one using dictionary comprehension like so:
nodenames = {n:'firstline \n secondline \n thirdline' for n in G.nodes()}

# and then draw:
nx.draw_networkx_labels(G,labels=nodenames);