【问题标题】:Create an adjacency list from a pandas Dataframe containing nodes从包含节点的 pandas Dataframe 创建邻接列表
【发布时间】:2021-05-08 14:25:49
【问题描述】:

我有一个 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", "F", "G", "X", "X", "E"],
                   "cases": [["c1", "c2", "c44"], ["c2", "c1", "c3"], ["c4"], ["c1", ], ["c1", "c7"], ["c4"], ["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 行中的节点,则返回true(这稍后将成为从节点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"])

简而言之,如果节点iend 值与节点jstart 值相同并且它们的cases 重叠,则节点i 后跟节点j

p>

基于这个directly_follows 函数,我想为我的DataFrame df 创建一个额外的列,它充当邻接列表,包含节点i 的列表,其中id 后面的节点的值是@ 987654340@

因此,我想要的结果是:

    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]         []
5   5   F       X       [c4]             []
6   6   B       E       [c44, c7]        []

基本上我想到了首先将列adjacency_list创建为空列表,然后循环遍历Dataframe的行,如果对于行ij直接_follows(row_i,row_j)返回True,添加@的id 987654344@ 到i 的邻接列表。

我是这样做的:

def connect(data):
    data["adjacency_list"] = np.empty((len(data), 0)).tolist()
    
    for i in range(len(data)):
        for j in range(len(data)):
            if i != j:
                if directly_follows(data.iloc[i], data.iloc[j]):
                    data.iloc[i]["adjacency_list"] = data.iloc[i]["adjacency_list"].append(data.iloc[i]["id"])

首先,这会返回一个错误

SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame

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

如何以最省时的方式创建邻接表?有没有比我的更快或更优雅的解决方案?

【问题讨论】:

  • 这是单向的吗? 5 不与 4 相邻?
  • 不,我想将每一行与每一行进行比较。第 4 行的结尾是“X”,第 5 行的开头是“F”,所以 4 后面没有 5
  • 对,但第 5 行以“X”结尾,第 4 行以“X”开头。如果您将每一行与其他行进行比较,那么 5 是否不会连接到 4?
  • 这是真的,如果他们确实在案例中重叠,但他们没有(我的其他连接要求)

标签: python pandas dataframe graph adjacency-list


【解决方案1】:

自加入选项:

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, 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]             []
5   5     F   X           [c4]            [4]
6   6     B   E      [c44, c7]             []

【讨论】:

    【解决方案2】:

    一种选择是应用以下函数 - 它不是完全矢量化的,因为 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]             []
    

    【讨论】:

    • 感谢您的解决方案!我可以用 'x' 替换 'df' 吗?或者你在函数 f 的定义中同时使用了两个原因?
    • @Peter 我将它拆分为一个单独的函数,但实际上 f 应该写成一个 lambda 函数。它正在从函数外部访问df 数据帧,x 是正在检查的当前行。
    • 我了解您编写的函数的一般用途,但我真的很难理解为什么 x 和 df 一起使用以及如何将其转换为 lambda 函数。你能详细说明一下吗?感谢您的努力!
    • @Peter So df 指的是整个数据框,因为我们正在遍历数据框的行,首先将整个数据框 (df) 缩小到仅以当前行 (x) 的末尾。我们将此行子集分配给变量check。现在,我们遍历check 中的行以找到与当前行x 有重叠的行。我将把重写为 lambda 函数的函数添加到答案中。
    【解决方案3】:

    尝试:

    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,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]             []
    5   5     F   X         [c4]             []
    6   6     B   E     [c44,c7]             []
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 2015-03-09
      • 2021-08-31
      • 2010-11-10
      相关资源
      最近更新 更多