【问题标题】:Pandas - Recursively look for children in dataframePandas - 在数据框中递归查找子项
【发布时间】:2021-06-03 02:00:48
【问题描述】:

考虑以下数据框:

    id1    id2
0   aaa    111
1   bbb    222
2   333    ccc
3   999    zzz
4   ccc    111
5   888    zzz
6   zzz    222
7   ddd    888
8   eee    888

如何递归获取给定输入的所有孩子及其孙子的每场比赛的数据框,在我的例子中,输入 = [111, 222]

家长 1:111
孩子1:aaa
Child2:ccc(从第 4 行开始)
Child2 的孩子:333(从第 2 行开始)

父 2:222
Child1:bbb
Child2:zzz(从第 6 行开始)
Child2 的 ChildA:888(从第 5 行开始)
Child2 的 ChildB:999(从第 3 行开始)
ChildA 的 Child_i:ddd(从第 8 行开始)
ChildA 的 Child_ii:eee(从第 7 行开始)

每个级别(父级->子级->子级)的预期输出将是:

### for i = 111
# parent level
     id1    id2
0    aaa    111
1    ccc    111

# child level
     id1    id2
0    333    ccc


### for i = 222
# parent level
     id1    id2
0    bbb    222
1    zzz    222

# child level
     id1    id2
0    888    zzz
1    999    zzz

# child of child level
     id1    id2
0    ddd    888    
1    eee    888    

我试过了:

parents = [111, 222]

while len(parents) != 0:
    for i in parents:
        children = df[df['id2'].apply(lambda x: i in str(x))][['id1', 'id2']]
        print(children) #print dataframe of match
    parents = children['id1']

但它并没有完全通过,我想将 lambda 中的 i 更改为列表理解,但没有成功。

【问题讨论】:

  • 预期的数据框是什么?
  • 对于每场比赛,只需 df[['id1', 'id2']],然后我可以将它们连接起来
  • @bloo 请用您的预期输出更新问题..

标签: python pandas children


【解决方案1】:

如果你只想打印一个缩进图,你可以使用一个简单的递归函数:

def desc(i, indent=0):
    print(' '*indent + i)
    for j in df.loc[df['id2'] == i, 'id1']:
        desc(j, indent + 2)

for i in ('111', '222'): desc(i)

使用示例 df,它给出:

111
  aaa
  ccc
    333
222
  bbb
  zzz
    999
    888
      ddd
      eee

【讨论】:

  • 是的,这正是我想要的!我可以改变我的格式。我唯一的问题是它是否会沿着层次结构向下移动,直到没有子级(或子级的子级)为止,因为我的实际数据框有数十万行,尽管我确实在我的打印上看到了一个额外的级别我想知道它是否因为代码或没有其他孩子而停在那里。
  • 这个没有层级限制,所以应该一直下去,直到元素没有子元素。
  • 非常感谢,我现在需要研究这个 :) 我不确定我的逻辑在哪里搞混了,我的代码没有工作
  • @bloo:不同之处在于这段代码是递归的:函数desc调用自己。您的代码仅包含循环。
  • 我现在看到了我是如何尝试使用循环而不是递归函数来实现这一点的,并且正在研究它一段时间并获得了隧道视野。再次感谢!
【解决方案2】:

result 数据框也将包含 NaN,但如果您想删除它们,请使用 result.dropna()

from io import StringIO
d = StringIO("""
ix    id1    id2
0   aaa    111
1   bbb    222
2   333    ccc
3   999    zzz
4   ccc    111
5   888    zzz
6   zzz    222
7   ddd    888
8   eee    888
""")

import pandas as pd

df = pd.read_csv(d, sep='\s+', index_col='ix')

df.columns

result = (
    df.rename(columns={'id2': 'id_parent', 'id1': 'id_child'})
    .merge(df.set_index('id2'), how='left', left_on='id_child', right_index=True)
    .rename(columns={'id1': 'id_grandchild'})
)

result

例如,列出所有孙子的方法如下:

result.dropna().groupby('id_parent')['id_grandchild'].agg(list).reset_index()

这是一种创建查找字典的方法,其中包含个人的所有孙子孙辈:

dict_parents = result.dropna().groupby('id_parent')['id_grandchild'].agg(list).to_dict()
# e.g. try: print(dict_parents['222'])

以下是获取特定个人结果的方法:

specific_ids = ['111', '222']

result = (
    df[df['id2'].isin(specific_ids)].rename(columns={'id2': 'id_parent', 'id1': 'id_child'})
    .merge(df.set_index('id2'), how='left', left_on='id_child', right_index=True)
    .rename(columns={'id1': 'id_grandchild'})
)

result.dropna()

【讨论】:

  • 我编辑了这个问题,以澄清我没有尝试更改数据框的格式。我正在尝试提取输入背面的信息。
  • 嗯,你想得到这个吗? result[['id_parent', 'id_grandchild']].dropna()你的问题仍然提到你想要一个数据框......
  • 这个想法是有一个输入或一个输入列表,然后对于列表中的每个输入,我在数据框中寻找它的孩子,然后寻找那个孩子的孩子,依此类推,直到没有孩子,然后移动到下一个输入。然后我会为每场比赛获得一个数据框,然后我可以在稍后阶段将其连接起来。
  • 如果我正确理解您的流程,在连接后您将收到数据帧result。如果问题是将其子集到特定个人,那么您可以在合并之前施加该限制,请参阅我更新的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 2021-09-23
  • 2017-01-31
  • 1970-01-01
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多