有没有办法使用 Pandas / Python 来计算内部 table 参考菊花链的长度?
Is there a way to count the length of a daisy chain of internal table references using Pandas / Python?
我们有一个 table,其中包含一个 ID,并且在同一行中,引用了同一 table 中的另一个 ID。 Id 记录被引用的 Id 记录感染。引用的 Id 本身可能会或可能不会引用另一个 Id,它可能不存在,或者它可能成为循环引用(链接回自身)。放入pandas,问题看起来有点像这样:
import pandas as pd
import numpy as np
# example data frame
inp = [{'Id': 1, 'refId': np.nan},
{'Id': 2, 'refId': 1},
{'Id': 3, 'refId': 2},
{'Id': 4, 'refId': 3},
{'Id': 5, 'refId': np.nan},
{'Id': 6, 'refId': 7},
{'Id': 7, 'refId': 20},
{'Id': 8, 'refId': 9},
{'Id': 9, 'refId': 8},
{'Id': 10, 'refId': 8}
]
df = pd.DataFrame(inp)
print(df.dtypes)
我想做的是计算 table 中每一行的引用回溯到多远。逻辑将:
- 从每行的结果 = 0 开始:
- 如果Ref-Id不是nan,则加1,
- 如果referenced-Id存在,并且这个referenced-Id有引用,并且referenced-Id引用不是反向引用,则将Result加1,然后
重复此步骤,直到不满足其中一个条件,然后转到
否则;
- 否则(没有引用-Id,没有对引用-Id 的引用,或者
引用循环回到之前的引用),return 结果。
示例的结果应如下所示:
Id RefId Result
1 - 0
2 1 1
3 2 2
4 3 3
5 - 0
6 7 2
7 20 1
8 9 1
9 8 1
10 8 2
我尝试过的每一种方法最终都需要一个新的列用于每个对引用的引用,但是 table 非常庞大,我不确定内部 [ 的菊花链有多长=36=]参考文献终究会。我希望有更好的方法,这对我来说不太难学。
这是一个图形问题,所以你可以使用networkx
。
将您的数据框转换为有向图:
import networkx as nx
G = nx.from_pandas_edgelist(df.fillna(-1).astype(int),
source='Id', target='refId', # source -> target
create_using=nx.DiGraph() # directed graph
)
# removing the NaN (replaced by "-1" for enabling indexing)
G.remove_node(-1)
给出这张图:
然后简单算一下children:
nodes = {n: len(nx.descendants(G,n)) for n in G.nodes}
df['Result'] = df['Id'].map(lambda x: nodes.get(x, 0))
输出:
Id refId Result
0 1 NaN 0
1 2 1.0 1
2 3 2.0 2
3 4 3.0 3
4 5 NaN 0
5 6 7.0 2
6 7 20.0 1
7 8 9.0 1
8 9 8.0 1
9 10 8.0 2
注意。结果有点不同,所以也许我没有完全理解你的逻辑,但这给了你一般的想法。请详细说明逻辑。
我们有一个 table,其中包含一个 ID,并且在同一行中,引用了同一 table 中的另一个 ID。 Id 记录被引用的 Id 记录感染。引用的 Id 本身可能会或可能不会引用另一个 Id,它可能不存在,或者它可能成为循环引用(链接回自身)。放入pandas,问题看起来有点像这样:
import pandas as pd
import numpy as np
# example data frame
inp = [{'Id': 1, 'refId': np.nan},
{'Id': 2, 'refId': 1},
{'Id': 3, 'refId': 2},
{'Id': 4, 'refId': 3},
{'Id': 5, 'refId': np.nan},
{'Id': 6, 'refId': 7},
{'Id': 7, 'refId': 20},
{'Id': 8, 'refId': 9},
{'Id': 9, 'refId': 8},
{'Id': 10, 'refId': 8}
]
df = pd.DataFrame(inp)
print(df.dtypes)
我想做的是计算 table 中每一行的引用回溯到多远。逻辑将:
- 从每行的结果 = 0 开始:
- 如果Ref-Id不是nan,则加1,
- 如果referenced-Id存在,并且这个referenced-Id有引用,并且referenced-Id引用不是反向引用,则将Result加1,然后 重复此步骤,直到不满足其中一个条件,然后转到 否则;
- 否则(没有引用-Id,没有对引用-Id 的引用,或者
引用循环回到之前的引用),return 结果。
示例的结果应如下所示:
Id RefId Result
1 - 0
2 1 1
3 2 2
4 3 3
5 - 0
6 7 2
7 20 1
8 9 1
9 8 1
10 8 2
我尝试过的每一种方法最终都需要一个新的列用于每个对引用的引用,但是 table 非常庞大,我不确定内部 [ 的菊花链有多长=36=]参考文献终究会。我希望有更好的方法,这对我来说不太难学。
这是一个图形问题,所以你可以使用networkx
。
将您的数据框转换为有向图:
import networkx as nx
G = nx.from_pandas_edgelist(df.fillna(-1).astype(int),
source='Id', target='refId', # source -> target
create_using=nx.DiGraph() # directed graph
)
# removing the NaN (replaced by "-1" for enabling indexing)
G.remove_node(-1)
给出这张图:
然后简单算一下children:
nodes = {n: len(nx.descendants(G,n)) for n in G.nodes}
df['Result'] = df['Id'].map(lambda x: nodes.get(x, 0))
输出:
Id refId Result
0 1 NaN 0
1 2 1.0 1
2 3 2.0 2
3 4 3.0 3
4 5 NaN 0
5 6 7.0 2
6 7 20.0 1
7 8 9.0 1
8 9 8.0 1
9 10 8.0 2
注意。结果有点不同,所以也许我没有完全理解你的逻辑,但这给了你一般的想法。请详细说明逻辑。