Pandas Dataframe - 运行 通过两列 'Father' 和 'Son' 逐步重建端到端链接

Pandas Dataframe - running through two columns 'Father' and 'Son' to rebuild end-to-end links step by step

我有一个很长的数据框,我需要对其进行转换以获得一个较宽的数据框。 长的是:

df = pd.DataFrame({
    'key' : ['E', 'E', 'E', 'E', 'J', 'J', 'J', 'J'],
    'father' : ['A', 'D', 'C', 'B', 'F', 'H', 'G', 'I'],
    'son' : ['B', 'E', 'D', 'C', 'G', 'I', 'H', 'J']
})
df

我认为首先要做的是按键分组。然后我们必须找到这些键在列 'son' 中的位置,这是我需要重建的 link 的结尾(也是最后一个儿子)。

要重建 link,我需要寻找他的 'father'。他的 'father' 需要保留为最后一步的父亲,还需要在 'son'.

中找到

我需要重复此操作,直到无法在 'son' 列中找到 'father',因此它将成为 link 的 father_0。

我认为可以将这些步骤迭代到一个递归函数中,其中停止条件 : 'father' 在 'son' 中找不到。

这是我想从中获得的数据框:

df1 = pd.DataFrame({
    'key' : ['E', 'J'],
    'father_1' : ['A', 'F'],
    'son_1' : ['B', 'G'],
    'father_2' : ['B', 'G'],
    'son_2' : ['C', 'H'],
    'father_3' : ['C', 'H'],
    'son_3' : ['D', 'I'],
    'father_4' : ['D', 'I'],
    'son_4' : ['E', 'J'],
})
df1

我在这里用相同深度的 2 个不同的 link 简化了问题,但对于许多不同的键,它们可能从深度 1 到深度 10(可能更多,但很少且不可预测)。 这是 df 的另一个例子,有 2 个不同大小的 links :

df_ = pd.DataFrame({
    'key' : ['E', 'E', 'E', 'E', 'K', 'K', 'K', 'K', 'K'],
    'father' : ['A', 'D', 'C', 'B', 'F', 'H', 'G', 'I', 'J'],
    'son' : ['B', 'E', 'D', 'C', 'G', 'I', 'H', 'J', 'K']
})
df_

df_1 = pd.DataFrame({
    'key' : ['E', 'K'],
    'father_1' : ['A', 'F'],
    'son_1' : ['B', 'G'],
    'father_2' : ['B', 'G'],
    'son_2' : ['C', 'H'],
    'father_3' : ['C', 'H'],
    'son_3' : ['D', 'I'],
    'father_4' : ['D', 'I'],
    'son_4' : ['E', 'J'],
    'father_5' : [np.NaN, 'J'],
    'son_5' : [np.NaN, 'K']
})
df_1 

那么最后一步就简单了,就是把 'father_x' 和 'son_x-1' 变成 'step_x-1' : 因此,这些示例的结果数据帧将是:

df2 = pd.DataFrame({
    'key' : ['E', 'J'],
    'step_0' : ['A', 'F'],
    'step_1' : ['B', 'G'],
    'step_2' : ['C', 'H'],
    'step_3' : ['D', 'I'],
    'step_4' : ['E', 'J'],
})
df2

df_2 = pd.DataFrame({
    'key' : ['E', 'K'],
    'step_0' : ['A', 'F'],
    'step_1' : ['B', 'G'],
    'step_2' : ['C', 'H'],
    'step_3' : ['D', 'I'],
    'step_4' : ['E', 'J'],
    'step_5' : [np.NaN, 'K']
})
df_2

我更关心的是如何按照前面给定的规则将数据从宽到长聚合到递归函数中。

这就像在 groupby.agg 中一样,但我不能只将字典传递给它,因为新列基于每个键上递归函数的迭代次数。

cumcount分配新密钥然后我们可以pivot

out = df.assign(c = df.groupby('key').cumcount().add(1).astype(str)).pivot('key','c').sort_index(level=1,axis=1)
out.columns = out.columns.map('_'.join)
out
Out[34]: 
    father_1 son_1 father_2 son_2 father_3 son_3 father_4 son_4
key                                                            
E          A     B        B     C        C     D        D     E
J          F     G        G     H        H     I        I     J

我为这种特定类型的数据框找到了一个解决方案:除了 root 之外,我们只有 1 个前导值。 它还需要使用 NetworkX。我没有找到只使用 Pandas.

的方法

首先,我们需要从边缘列表构建一个图:

G = nx.from_pandas_edgelist(df, 'father', 'son', create_using=nx.MultiDiGraph, edge_key = 'key')
from networkx.drawing.nx_agraph import write_dot, graphviz_layout
#write_dot(G,'test.dot')
plt.title('draw_networkx')
pos =graphviz_layout(G, prog='dot')
nx.draw(G, pos, with_labels=True, arrows=True)

对于 pygraphviz 安装,请参阅 。 然后 end-to-end 链接数据框是用 :

构建的
num=0
num_max = len(df.key.drop_duplicates())
m_max = 30
dfy = pd.DataFrame(index=range(num_max),columns=range(m_max))
for n in df.key.drop_duplicates() :
    m = 0
    dfy.iloc[num, m] = n
    while len(list(G.predecessors(dfy.iloc[num,m])))!=0 :
        dfy.iloc[num,m+1] = list(G.predecessors(dfy.iloc[num,m]))[0]
        m+=1
    num+=1
print(dfy)

输出:

   0  1  2  3  4    5    6    7    8    9  ...   
0  E  D  C  B  A  NaN  NaN  NaN  NaN  NaN  ...  
1  K  J  I  H  G    F  NaN  NaN  NaN  NaN  ...