Pandas - 在数据框中递归查找孩子

问题描述

考虑以下数据框:

    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 更改为列表理解,但没有设法使其工作。

解决方法

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

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
,

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',right_index=True)
    .rename(columns={'id1': 'id_grandchild'})
)

result.dropna()