具有到networkx有向图的链接列表的数据帧



我有一个链接文档集合的数据帧,我想将其转换为具有边权重link_weight和节点属性doc_attribute的有向图。要做到这一点,什么是有效的方式?我在这里提供了一个小例子,但实际数据的目标是大约10万个文档,每个文档平均有大约10个链接。

示例:

import pandas as pd
import numpy as np
from string import ascii_lowercase
N = 100
doc_ids = [f"doc_{j}" for j in range(N)]
doc_attrs = np.random.choice(list(ascii_lowercase), N)
link_weights = np.random.choice(10, N)
links = [random.choices(doc_ids, k=np.random.choice(4)) for j in range(N)]

df = pd.DataFrame(data={"doc_attribute": doc_attrs, "link_weight":link_weights, "linked_docs":links}, index=doc_ids) 

通知文档可能不包括链接文档或链接到文档:

doc_attribute  link_weight               linked_docs
doc_0             b            3          [doc_55, doc_67]
doc_1             i            2                        []
doc_2             l            4                  [doc_72]
doc_3             f            1                  [doc_78]
doc_4             e            6                  [doc_50]
doc_5             k            3                  [doc_24]
doc_6             j            6    [doc_3, doc_6, doc_63]
doc_7             g            4  [doc_11, doc_59, doc_59]
doc_8             f            9                        []
doc_9             f            8                  [doc_57]

所需输出:nx。DiGraph对象,具有由df.index给定的节点、指向linked_docs的边、节点属性doc_attribute和链接权重link_weight。Networkx具有from_dataframe功能,但用于不同的输入格式。我不知道创建DiGraph最有效的方法。

您可以使用from_dict_of_dicts,然后使用set_node_attributes:设置节点的属性

dod = {d['index']: {t: {"weight": d['link_weight']} for t in d['linked_docs']} for d in
df[['linked_docs', 'link_weight']].reset_index().to_dict('records')}
dg = nx.from_dict_of_dicts(dod, create_using=nx.DiGraph)
nx.set_node_attributes(dg, df['doc_attribute'].to_dict(), 'doc_attribute')

首先使用panda将链表列扩展为行,然后使用from_pands_edgelist:

df["source_docs"] = df.index 
df = df.explode("linked_docs")
graph = nx.from_pandas_edgelist(
df, 
source="source_docs", 
target="linked_docs",
edge_attr="link_weight",
create_using=nx.DiGraph,
)    
nx.set_node_attributes(graph, df['doc_attribute'].to_dict(), 'doc_attribute')
# remove the node created for source docs without links
graph.remove_node(np.nan)

示例输出:

df.head(10)
"""
doc_attribute  link_weight linked_docs source_docs
doc_0             l            4      doc_62       doc_0
doc_1             k            1      doc_24       doc_1
doc_1             k            1      doc_20       doc_1
doc_2             g            1      doc_25       doc_2
doc_2             g            1      doc_47       doc_2
doc_3             u            6      doc_58       doc_3
doc_4             j            6      doc_83       doc_4
doc_4             j            6      doc_73       doc_4
doc_4             j            6      doc_51       doc_4
doc_5             w            2      doc_75       doc_5
"""
len(graph.nodes) # 100
sorted(graph.edges.data(), key=lambda x: int(x[0].split("_")[-1]))[0:10]
"""
[('doc_0', 'doc_62', {'link_weight': 4}),
('doc_1', 'doc_24', {'link_weight': 1}),
('doc_1', 'doc_20', {'link_weight': 1}),
('doc_2', 'doc_25', {'link_weight': 1}),
('doc_2', 'doc_47', {'link_weight': 1}),
('doc_3', 'doc_58', {'link_weight': 6}),
('doc_4', 'doc_83', {'link_weight': 6}),
('doc_4', 'doc_73', {'link_weight': 6}),
('doc_4', 'doc_51', {'link_weight': 6}),
('doc_5', 'doc_75', {'link_weight': 2})
]
"""

sorted(graph.nodes.data(), key=lambda x: int(x[0].split("_")[-1]))[0:10]
"""
[('doc_0', {'doc_attribute': 'l'}),
('doc_1', {'doc_attribute': 'k'}),
('doc_2', {'doc_attribute': 'g'}),
('doc_3', {'doc_attribute': 'u'}),
('doc_4', {'doc_attribute': 'j'}),
('doc_5', {'doc_attribute': 'w'}),
('doc_6', {'doc_attribute': 'b'}),
('doc_7', {'doc_attribute': 's'}),
('doc_8', {'doc_attribute': 'l'}),
('doc_9', {'doc_attribute': 'e'})
]
"""

然而,在我的机器上,当N=100000,N_links~10时,这比另一个解决方案慢约40%,两个解决方案都超过了一秒。

最新更新