У меня есть такой фрейм данных:
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('')
)
Но я получаю пустой фрейм данных. Есть ли элегантный способ добиться этого?
Это номер 10 в дубликате.
@Бруно, это не совсем сводная таблица. Фактические данные не имеют порядка относительно последующих значений дочернего и родительского элементов. Мне нужно просмотреть каждое значение в дочернем столбце и найти соответствующих родителей, которые могут находиться где угодно в родительском столбце.
Один из вариантов — сделать финальный 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"}
Спасибо за это решение! Есть ли способ получить вывод в графическом виде, как вы изобразили?
Конечно, вот полный код (включая часть визуализации).
Используйте 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 Я имел в виду, что все равные дочерние элементы должны располагаться в смежных рядах, но на самом деле это моя ошибка. cumcount
также работает, если они не смежны, так что это не имеет значения. Я отредактировал ответ, чтобы удалить это.
спасибо за этот вариант. Вывод с использованием этого метода дает именно ту таблицу, которая была на скриншоте. Однако я забыл включить строку для I
, где нет родителя. Решение Timeless с использованием networkx
, кажется, также отражает отношения I
.
Похоже, вы стремитесь к сводной таблице, вы пробовали? pd.pivot