场景
我有一个数据框。每行包含一个项目,该项目可以但不一定与父项目或子项目链接,例如双向链接列表。这些行未排序,但父项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条链:
问题
如何找到此数据帧中最长链的长度? (即给定数据框中的3)
答案 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