Получение иерархии из двух столбцов, которые имеют родительские дочерние отношения

У меня есть такой фрейм данных:

data = {
    'Parent': [None, None,  'A',    'B',    'C',    'I',    'D',    'F',    'G',    'H',    'Z',    'Y',    None,None,None,None,    'AA',   'BB',   'CC',   'EE',   'FF',   None,   None],
    'Child': ['A',  'B',    'D',    'D',    'D',    'C',    'E',    'E',    'F',    'F',    'G',    'H',    'Z',    'Y',    'AA',   'BB',   'CC',   'CC',   'DD',   'DD',   'DD',   'EE',   'FF']
}

df = pd.DataFrame(data)
        
   Parent Child
0    None     A
1    None     B
2       A     D
3       B     D
4       C     D
5       I     C
6       D     E
7       F     E
8       G     F
9       H     F
10      Z     G
11      Y     H
12   None     Z
13   None     Y
14   None    AA
15   None    BB
16     AA    CC
17     BB    CC
18     CC    DD
19     EE    DD
20     FF    DD
21   None    EE
22   None    FF

Мне нужен выходной фрейм данных следующим образом:

Я попробовал использовать пакет networkx, как предложено в этом посте , Это код, который я использовал

df['parent']=df['parent'].fillna('No Parent')

leaves =set(df['parent']).difference(df['child'])
g= nx.from_pandas_edgelist(df, 'parent', 'child', create_using=nx.DiGraph())
ancestors = {
    n: nx.algorithms.dag.ancestors(g, n) for n in leaves
}

df1=(pd.DataFrame.from_dict(ancestors, orient='index')
 .rename(lambda x: 'parent_{}'.format(x+1), axis=1)
 .rename_axis('child')
 .fillna('')
 )

Но я получаю пустой фрейм данных. Есть ли элегантный способ добиться этого?

Похоже, вы стремитесь к сводной таблице, вы пробовали? pd.pivot

Bruno 13.07.2024 00:19

Это номер 10 в дубликате.

mozway 13.07.2024 09:07

@Бруно, это не совсем сводная таблица. Фактические данные не имеют порядка относительно последующих значений дочернего и родительского элементов. Мне нужно просмотреть каждое значение в дочернем столбце и найти соответствующих родителей, которые могут находиться где угодно в родительском столбце.

thentangler 13.07.2024 12:47
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Один из вариантов — сделать финальный DataFrame from_dict из предшественников :

DG = nx.from_pandas_edgelist(
    df.fillna("#"), "parent", "child", create_using=nx.DiGraph
)

DG.remove_node("#") # remove the placeholder

out = (
    pd.DataFrame.from_dict(
        {n: DG.predecessors(n) for n in DG}, orient = "index"
    ).rename(columns=lambda c: f"Parent {c+1}").reset_index(names = "Child")
)

# {"Parent 1": "deepskyblue", "Parent 2": "lightcoral", "Parent 3": "springgreen"}

Спасибо за это решение! Есть ли способ получить вывод в графическом виде, как вы изобразили?

thentangler 14.07.2024 06:08

Конечно, вот полный код (включая часть визуализации).

Timeless 14.07.2024 09:40

Используйте cumcount, чтобы отличить нескольких родителей, затем pivot.

Используйте sort_values, если вам важен порядок вывода, иначе вы можете удалить его.

out = (
    df.assign(rank=df.groupby("Child").cumcount() + 1)
    .pivot(index = "Child", columns = "rank", values = "Parent")
    .add_prefix("Parent ")
    .sort_values(by = "Child", key=lambda col: col.map(lambda x: (len(x), x)))
    .reset_index()
)
out.columns.name = None
   Child Parent 1 Parent 2 Parent 3
0      A     None      NaN      NaN
1      B     None      NaN      NaN
2      C        I      NaN      NaN
3      D        A        B        C
4      E        D        F      NaN
5      F        G        H      NaN
6      G        Z      NaN      NaN
7      H        Y      NaN      NaN
8      Y     None      NaN      NaN
9      Z     None      NaN      NaN
10    AA     None      NaN      NaN
11    BB     None      NaN      NaN
12    CC       AA       BB      NaN
13    DD       CC       EE       FF
14    EE     None      NaN      NaN
15    FF     None      NaN      NaN

Что вы имеете в виду под сгруппированными? В примере я сгруппировал порядок для удобства. Реальная таблица содержит тысячи строк, и значения в дочернем столбце могут быть не в порядке. Единственная связь существует между значением в родительском столбце и значением в дочернем столбце.

thentangler 13.07.2024 12:43

@thentangler Я имел в виду, что все равные дочерние элементы должны располагаться в смежных рядах, но на самом деле это моя ошибка. cumcount также работает, если они не смежны, так что это не имеет значения. Я отредактировал ответ, чтобы удалить это.

e-motta 13.07.2024 13:01

спасибо за этот вариант. Вывод с использованием этого метода дает именно ту таблицу, которая была на скриншоте. Однако я забыл включить строку для I, где нет родителя. Решение Timeless с использованием networkx, кажется, также отражает отношения I.

thentangler 14.07.2024 06:06

Другие вопросы по теме