Xác định cha mẹ gốc và tất cả con cái của họ trên cây


11

Tôi có một khung dữ liệu gấu trúc như vậy:

parent   child   parent_level   child_level
A        B       0              1
B        C       1              2
B        D       1              2
X        Y       0              2
X        D       0              2 
Y        Z       2              3

Điều này đại diện cho một cây trông như thế này

       A  X
      /  / \
     B  /   \
    /\ /     \
   C  D       Y
              |
              Z

Tôi muốn sản xuất một cái gì đó trông như thế này:

root    children
A       [B,C,D]
X       [D,Y,Z]

hoặc là

root   child
A      B
A      C
A      D
X      D
X      Y
X      Z 

Cách nhanh nhất để làm như vậy mà không lặp. Tôi có một khung dữ liệu thực sự lớn.

Câu trả lời:


10

Tôi đề nghị bạn sử dụng networkx , vì đây là một vấn đề về đồ thị. Cụ thể là chức năng con cháu :

import networkx as nx
import pandas as pd

data = [['A', 'B', 0, 1],
        ['B', 'C', 1, 2],
        ['B', 'D', 1, 2],
        ['X', 'Y', 0, 2],
        ['X', 'D', 0, 2],
        ['Y', 'Z', 2, 3]]

df = pd.DataFrame(data=data, columns=['parent', 'child', 'parent_level', 'child_level'])

roots = df.parent[df.parent_level.eq(0)].unique()
dg = nx.from_pandas_edgelist(df, source='parent', target='child', create_using=nx.DiGraph)

result = pd.DataFrame(data=[[root, nx.descendants(dg, root)] for root in roots], columns=['root', 'children'])
print(result)

Đầu ra

  root   children
0    A  {D, B, C}
1    X  {Z, Y, D}

5

Với đệ quy

def find_root(tree, child):
    if child in tree:
        return {p for x in tree[child] for p in find_root(tree, x)}
    else:
        return {child}

tree = {}
for parent, child in zip(df.parent, df.child):
    tree.setdefault(child, set()).add(parent)

descendents = {}
for child in tree:
    for parent in find_root(tree, child):
        descendents.setdefault(parent, set()).add(child)

pd.DataFrame(descendents.items(), columns=['root', 'children'])

  root   children
0    A  {B, D, C}
1    X  {Z, D, Y}

Bạn có thể thay thế thiết lập find_rootnhư một máy phát điện

def find_root(tree, child):
    if child in tree:
        for x in tree[child]:
            yield from find_root(tree, x)
    else:
        yield child

Hơn nữa, nếu bạn muốn tránh các vấn đề về độ sâu đệ quy, bạn có thể sử dụng mẫu "stack of iterators" để xác địnhfind_root

def find_root(tree, child):
    stack = [iter([child])]
    while stack:
        for node in stack[-1]:
            if node in tree:
                stack.append(iter(tree[node]))
            else:
                yield node
            break
        else:  # yes!  that is an `else` clause on a for loop
            stack.pop()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.