从包含节点的 Pandas Dataframe 创建邻接列表

问题描述

我有一个包含节点行的 Pandas DataFrame,我最终希望将这些节点连接并变成像对象一样的图形。为此,我首先想到将这个 DataFrame 转换为类似于邻接列表的东西,以便稍后轻松地从中创建一个图形。我有以下几点:

熊猫数据框:

df = pd.DataFrame({"id": [0,1,2,3,4,5,6],"start": ["A","B","D","A","X","F","B"],"end": ["B","C","G","E"],"cases": [["c1","c2","c44"],["c2","c1","c3"],["c4"],["c1",],"c7"],["c44","c7"]]})

看起来像这样:

    id  start   end     cases            
0   0   A       B       [c1,c2,c44]    
1   1   B       C       [c2,c1,c3]     
2   2   D       F       [c4]             
3   3   A       G       [c1]             
4   4   X       X       [c1,c7]         
5   5   F       X       [c4]             
6   6   B       E       [c44,c7]        

一个函数 directly_follows(i,j),如果行 i 中的节点后面跟着行 j 中的节点(这将是来自节点 {{ 的图中的有向边) 1}} 到节点 i):

j

简而言之,如果节点 def directly_follows(row1,row2): return close(row1,row2) and case_overlap(row1,row2) def close(row1,row2): return row1["end"] == row2["start"] def case_overlap(row1,row2): return not set(row1["cases"]).isdisjoint(row2["cases"]) i 值与节点 {{1} 的 j 值相同,则节点 end 后跟节点 i 1}} 并且如果它们的 start 重叠

基于此 j 函数,我想为我的 DataFrame cases 创建一个额外的列,该列充当邻接列表,包含节点 directly_follows 的列表,其中 {{1 df

后面的节点的 }} 值

我想要的结果是:

i

基本上我想首先将列 adjacency_list 创建为空列表,然后循环遍历 Dataframe 的行,如果对于行 idi 直接_follows(row_i,row_j) 返回 True,添加 id start end cases adjacency_list 0 0 A B [c1,c44] [1,6] 1 1 B C [c2,c3] [] 2 2 D F [c4] [5] 3 3 A G [c1] [] 4 4 X X [c1,c7] [] 5 5 F X [c4] [] 6 6 B E [c44,c7] [] 的 id 到 i 的邻接列表。

我是这样做的:

j

现在首先,这会返回一个错误

j

其次,我非常怀疑这是解决这个问题的最 Pythonic 和最有效的方法,因为我的实际 DataFrame 包含大约 9000 行,这将进行大约 8100 万次比较。

如何以最少的时间创建邻接表?可能有比我的更快或更优雅的解决方案吗?

解决方法

一种选择是应用以下函数 - 它不是完全矢量化的,因为 Dataframes 并不特别喜欢嵌入像列表这样的可变对象,而且我认为您不能以矢量化的方式应用集合操作。不过,它确实减少了所需的比较次数。

def f(x):
    check = df[(x["end"] == df["start"])]
    return [
        row["id"]
        for i,row in check.iterrows()
        if not set(row["cases"]).isdisjoint(x["cases"])
    ]


df["adjacency_list"] = df.apply(f,axis=1)

或者,作为一个大的 lambda 函数:

df["adjacency_list"] = df.apply(
    lambda x: [
        row["id"]
        for i,row in df[(x["end"] == df["start"])].iterrows()
        if not set(row["cases"]).isdisjoint(x["cases"])
    ],axis=1,)

输出

   id start end          cases adjacency_list
0   0     A   B  [c1,c2,c44]         [1,6]
1   1     B   C   [c2,c1,c3]             []
2   2     D   F           [c4]            [5]
3   3     A   G           [c1]             []
4   4     X   X       [c1,c7]            [4]
5   5     F   X           [c4]             []
6   6     B   E      [c44,c7]             []
,

尝试:

k=0
def test(x):
    global k
    k+=1
    test_df = df[k:]
    return list(test_df[test_df['start'] == x].index)
df['adjancy_matrix'] = df.end.apply(test,1)

输出:

   id start end        cases adjancy_matrix
0   0     A   B  [c1,c3]             []
2   2     D   F         [c4]            [5]
3   3     A   G         [c1]             []
4   4     X   X      [c1,c7]             []
5   5     F   X         [c4]             []
6   6     B   E     [c44,c7]             []
,

自加入选项:

df['adjacency_list'] = df.apply(lambda s: df[(df['start'] == s.end) &
                                             (df['id'] != s.id)].index.tolist(),axis=1)
print(df)

输出:

   id start end          cases adjacency_list
0   0     A   B  [c1,c7]             []
5   5     F   X           [c4]            [4]
6   6     B   E      [c44,c7]             []