从DataFrame中加载带有属性和边的节点到NetworkX。

20

我是新手,正在使用Python处理图形:NetworkX。到目前为止,我一直在使用Gephi。那里的标准步骤(但不是唯一可能的步骤)是:

  1. 从表格/电子表格中加载节点信息;其中一个列应为ID,其余列为有关节点的元数据(节点是人,因此性别、群组等通常用于着色)。例如:

  2. id;NormalizedName;Gender
    per1;Jesús;male
    per2;Abraham;male
    per3;Isaac;male
    per4;Jacob;male
    per5;Judá;male
    per6;Tamar;female
    ...
    
    然后从一个表格/电子表格中也加载边缘,使用节点名称与节点电子表格中ID列中使用的相同名称,通常包含四个列(目标、源、权重和类型):
    Target;Source;Weight;Type
    per1;per2;3;Undirected
    per3;per4;2;Undirected
    ...
    
    这是我拥有并要在Python中加载的两个数据框。阅读了有关 NetworkX 的信息,似乎不太可能将两个表(一个用于节点,一个用于边)加载到同一图形中,我不确定什么是最好的方法:
    1. 我应该只使用来自 DataFrame 的节点信息创建一个图形,然后从另一个 DataFrame 添加(附加)边缘吗?如果是这样,并且因为 nx.from_pandas_dataframe() 需要有关边缘的信息,我想我不应该使用它来创建节点... 我应该只传递信息作为列表吗?
    2. 我应该仅使用来自 DataFrame 的边缘信息创建图形,然后将另一个 DataFrame 中的每个节点信息添加为属性吗?是否有比迭代 DataFrame 和节点更好的方法?
3个回答

40
使用nx.from_pandas_dataframe从边表创建加权图:
import networkx as nx
import pandas as pd

edges = pd.DataFrame({'source' : [0, 1],
                      'target' : [1, 2],
                      'weight' : [100, 50]})

nodes = pd.DataFrame({'node' : [0, 1, 2],
                      'name' : ['Foo', 'Bar', 'Baz'],
                      'gender' : ['M', 'F', 'M']})

G = nx.from_pandas_dataframe(edges, 'source', 'target', 'weight')

然后使用set_node_attributes函数,从字典中添加节点属性:

nx.set_node_attributes(G, 'name', pd.Series(nodes.name, index=nodes.node).to_dict())
nx.set_node_attributes(G, 'gender', pd.Series(nodes.gender, index=nodes.node).to_dict())

或者遍历图形以添加节点属性:

for i in sorted(G.nodes()):
    G.node[i]['name'] = nodes.name[i]
    G.node[i]['gender'] = nodes.gender[i]

更新:

nx 2.0 开始,nx.set_node_attributes 的参数顺序已经更改: (G, values, name=None)

使用上面的例子:

nx.set_node_attributes(G, pd.Series(nodes.gender, index=nodes.node).to_dict(), 'gender')

截至 nx 2.4 版本,G.node[] 被替换为 G.nodes[]详见


1
太棒了,谢谢!我的节点列表还包括有时我不需要的一些节点,所以我做的第一件事是从边缘中列出所有节点名称,并使用此信息过滤节点表:entities_edges = sorted(list(set(edges["Target"].tolist()+edges["Source"].tolist()))); nodes = pd.read_csv(input_folder+file_nodes, encoding="utf-8", sep="\t"); nodes = nodes[nodes['id'].isin(entities_edges)];然后我遵循了你的建议。你觉得怎么样?非常感谢你的回答! - José
如果您使用set_node_attributes的字典方法,您就不需要担心不包含在entities_edges中的节点。未连接的节点可以随后使用G.remove_nodes_from(nx.isolates(G))从图中删除。 - harryscholes
3
@h_s,回答得很好,但我想知道如果我没有有意义的索引,我该如何使用nx.set_node_attributes方法,或者我只能使用for循环吗? - LancelotHolmes
1
很遗憾,NetworkX 2.3没有from_pandas_dataframe - user7851115
如果你遇到值为nan的情况,你可能需要这个: - Wey Gu
显示剩余2条评论

11

这里基本上是相同的答案,但更新了一些详细信息。我们将从基本上相同的设置开始,但这里不会有节点的索引,只有名称来解决@LancelotHolmes的评论并使其更加通用。

import networkx as nx
import pandas as pd

linkData = pd.DataFrame({'source' : ['Amy', 'Bob'],
                  'target' : ['Bob', 'Cindy'],
                  'weight' : [100, 50]})

nodeData = pd.DataFrame({'name' : ['Amy', 'Bob', 'Cindy'],
                  'type' : ['Foo', 'Bar', 'Baz'],
                  'gender' : ['M', 'F', 'M']})

G = nx.from_pandas_edgelist(linkData, 'source', 'target', True, nx.DiGraph())

这里的True参数告诉 NetworkX 保留 linkData 中的所有属性作为链接属性。在这种情况下,我将其设置为 DiGraph 类型,但如果不需要,可以用另一种方式设置它。

现在,由于你需要通过从 linkData 生成的节点名称来匹配 nodeData,因此需要将 nodeData 数据帧的索引设置为 name 属性,然后将其转换为字典,以便 NetworkX 2.x 可以将其加载为节点属性。

nx.set_node_attributes(G, nodeData.set_index('name').to_dict('index'))

这将整个nodeData数据框加载到一个字典中,其中键是名称,其他属性是该键内的键值对(即节点索引为其名称的普通节点属性)。


8
一个小提示:
from_pandas_dataframe在nx 2中不可用,可以使用这个。
G = nx.from_pandas_dataframe(edges, 'source', 'target', 'weight')

我认为在nx 2.0中应该是这样的:

我认为在nx 2.0中应该是这样的:

G = nx.from_pandas_edgelist(edges, source = "Source", target = "Target")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接