查找数据框中最长的父子链

时间:2019-09-26 08:29:20

标签: python pandas algorithm dataframe

场景

我有一个数据框。每行包含一个项目,该项目可以但不一定与父项目或子项目链接,例如双向链接列表。这些行未排序,但父项ID必须小于子项ID。

import pandas as pd
import numpy as np

df = pd.DataFrame(columns=['Item Id', 'Parent Id', 'Child Id'],
                  data=[[1006, np.nan, np.nan],
                        [1001, np.nan, 1005],
                        [1004, 1003, 1007],
                        [1003, 1002, 1004],
                        [1005, 1001, np.nan],
                        [1002, np.nan, 1003],
                        [1007, 1004, np.nan]
                        ])
print(df)
#    Item Id  Parent Id  Child Id
# 0     1006        NaN       NaN
# 1     1001        NaN    1005.0
# 2     1004     1003.0    1007.0
# 3     1003     1002.0    1004.0
# 4     1005     1001.0       NaN
# 5     1002        NaN    1003.0
# 6     1007     1004.0       NaN

因此数据框包含3条链:

  • 1001 => 1005
  • 1002 => 1003 => 1004 => 1007
  • 1006

问题

如何找到此数据帧中最长链的长度? (即给定数据框中的3)

4 个答案:

答案 0 :(得分:1)

AFAIK,熊猫和底层的numpy都不擅长解决图形问题。

但是您可以用列表表示每个链,构建所有链的列表,然后对其进行排序。我将使用辅助命令将每个项目链接到其链:

chains = []
seen = {}

for _, row in df.sort_values("Item Id").iterrows():
    itemId = row['Item Id']
    childId = row['Child Id']
    if itemId in seen:
        chain = seen[itemId]
    else:                                     # this is a new chain
        chain = seen[itemId] = [itemId]
        chains.append(chain)
    if not np.isnan(childId):                 # add the child to the end of the chain
        seen[childId] = chain
        chain.append(childId)
chains.sort(key=lambda x: len(x))             # and sort the list of chains

(此算法使用的属性是,父项ID必须小于子项ID)

输入数据框为:

>>> print(chains)
[[1006.0], [1001.0, 1005.0], [1002.0, 1003.0, 1004.0, 1007.0]]

答案 1 :(得分:1)

基于@bli的建议,我使用networkx将数据帧转换为有向图,并使用dag_longest_path()dag_longest_path_length()得到了答案。

import networkx as nx
G=nx.from_pandas_edgelist(df[~df['Child Id'].isna()], 'Item Id', 'Child Id', 
                          edge_attr=True, create_using=nx.DiGraph())

输出

>>> print(nx.dag_longest_path(G))
[1002, 1003, 1004, 1007.0]
>>> print(nx.dag_longest_path_length(G))
3

答案 2 :(得分:0)

我会带走所有在其父母ID中带有“ np.nan”的父母。递归检查每个父级,直到找到最长的链。或者也可以做相反的操作,在其子ID中查找带有“ np.nan”的对象,它们是链的最后一部分,然后递归返回直到没有父对象为止。

答案 3 :(得分:0)

这是一种实现方法。根本没有对它进行优化,但是可以得到您想要的东西,而无需递归:

data = [[1006, None, None],
        [1001, None, 1005],
        [1004, 1003, 1007],
        [1003, 1002, 1004],
        [1005, 1001, None],
        [1002, None, 1003],
        [1007, 1004, None]
    ]


class Node:
    def __init__(self, value, parent=None, child=None):
        self.value = value
        self.parent = parent
        self.child = child


nodes = {}
parent_ids = []

for entry in data:
    (itm, parent, child) = entry
    nodes[itm] = Node(itm, parent, child)
    if parent is None:
        parent_ids.append(itm)

for parent_id in parent_ids:
    chain = [str(parent_id)]
    node = nodes[parent_id]
    while node.child is not None:
        chain.append(str(node.child))
        node = nodes[node.child]
    print(" -> ".join(chain))

输出:

1006
1001 -> 1005
1002 -> 1003 -> 1004 -> 1007