Pandas 动态堆栈

Pandas Dynamic Stack

给定以下数据框:

import numpy as np
import pandas as pd

df = pd.DataFrame({'foo':['a','b','c','d'],
                   'bar':['e','f','g','h'],
                       0:['i','j','k',np.nan],
                       1:['m',np.nan,'o','p']})
df=df[['foo','bar',0,1]]
df

    foo   bar    0      1
0   a     e      i      m
1   b     f      j     NaN
2   c     g      k      o
3   d     h     NaN     p

...这是先前生成列 01 的过程的结果(生成的列可能多于或少于 01,具体取决于关于数据): 我想以某种方式堆叠(如果这是正确的术语)数据,以便 01 的每个值(忽略 NaNs)产生一个新行,如下所示:

    foo bar
0   a   e
0   a   i
0   a   m
1   b   f
1   b   j
2   c   g
2   c   k
2   c   o
3   d   h
3   d   p

您可能注意到公共字段是 foo。 很可能会出现我的实际数据集中有更多公共字段的情况。 另外,我不确定索引值在 foo 的值中重复最终结果有多重要。只要数据正确,这就是我最关心的问题。

更新: 如果我有 2 个以上这样的公共字段怎么办:

import numpy as np
import pandas as pd

df = pd.DataFrame({'foo':['a','a','b','b'],
                   'foo2':['a2','b2','c2','d2'],
                   'bar':['e','f','g','h'],
                       0:['i','j','k',np.nan],
                       1:['m',np.nan,'o','p']})
df=df[['foo','foo2','bar',0,1]]
df

    foo     foo2    bar     0   1
0   a       a2      e     i     m
1   a       b2      f     j     NaN
2   b       c2      g     k     o
3   b       d2      h     NaN   p

您可以使用 set_index, stack and reset_index:

print df.set_index('foo').stack().reset_index(level=1, drop=True).reset_index(name='bar')
  foo bar
0   a   e
1   a   i
2   a   m
3   b   f
4   b   j
5   c   g
6   c   k
7   c   o
8   d   h
9   d   p

如果需要索引,使用melt:

print pd.melt(df.reset_index(), 
              id_vars=['index', 'foo'], 
              value_vars=['bar', 0, 1],
              value_name='bar')
        .sort_values('index')
        .set_index('index', drop=True)
        .dropna()
        .drop('variable', axis=1)
        .rename_axis(None)

  foo bar
0   a   e
0   a   i
0   a   m
1   b   f
1   b   j
2   c   g
2   c   k
2   c   o
3   d   h
3   d   p

或者使用不为人知的lreshape:

print pd.lreshape(df.reset_index(), {'bar': ['bar', 0, 1]})
        .sort_values('index')
        .set_index('index', drop=True)
        .rename_axis(None)

  foo bar
0   a   e
0   a   i
0   a   m
1   b   f
1   b   j
2   c   g
2   c   k
2   c   o
3   d   h
3   d   p