使用 Pandas 将列按组重塑为行

Reshaping a column into rows group-wise using Pandas

我有一个df

id    name   value
1      abc     10
1      qwe     23
1      zxc     12
2      sdf     10
2      wed     23
2      abc     12
2      mnb     11

我想将此数据框重塑为:

id    n1    n2    n3    n4
 1    abc   qwe   zxc    0
 2    sdf   wed   abc   mnb

我们可以看到 id=1 有 3 行,id=2 有 4 行。 因此,如果发生这种情况,请替换最后一列 n4=0。

This is test dataframe, it may happen that, for a id there might by 1-2 rows too.

这类似于我们在 R - dcast 中所做的事情。 我们如何在 pandas 中做到这一点?

您可以使用 set_index with cumcount for counts per groups for new columns names and reshape by unstack,最后重命名列:

df = (df.set_index(['id', df.groupby('id').cumcount()])['name']
       .unstack(fill_value=0)
       .rename(columns = lambda x: 'n{}'.format(x + 1))
       .reset_index())
print (df)
   id   n1   n2   n3   n4
0   1  abc  qwe  zxc    0
1   2  sdf  wed  abc  mnb

使用 DataFrame 构造函数的解决方案,原始数据中必须没有 NaN 值:

df1 = df.groupby('id')['name'].apply(list)
print (df1)
id
1         [abc, qwe, zxc]
2    [sdf, wed, abc, mnb]
Name: name, dtype: object

df = (pd.DataFrame(df1.values.tolist(), index=df1.index)
        .fillna(0)
        .rename(columns = lambda x: 'n{}'.format(x + 1))
        .reset_index())
print (df)
   id   n1   n2   n3   n4
0   1  abc  qwe  zxc    0
1   2  sdf  wed  abc  mnb

以及 GroupBy.applySeries 构造函数的解决方案:

df1 = (df.groupby('id')['name'].apply(lambda x: pd.Series(x.values, index=range(1,len(x)+1)))
        .unstack(fill_value=0)
        .add_prefix('n')
        .reset_index())
print (df1)

   id   n1   n2   n3   n4
0   1  abc  qwe  zxc    0
1   2  sdf  wed  abc  mnb

可能矫枉过正

f, u = pd.factorize(df.id.values)
b = np.bincount(f)
n, m = u.size, b.max()
c = np.arange(f.size) - np.arange(n).repeat(b) * (m - 1)

v = np.zeros((n, m), dtype=object)
v[f, c] = df.name.values

pd.DataFrame(
    v, pd.Index(u, name='id'),
    ['n{}'.format(i) for i in range(1, m + 1)]
).reset_index()

   id   n1   n2   n3   n4
0   1  abc  qwe  zxc    0
1   2  sdf  wed  abc  mnb

您可以走 str 路线并在 groupby 之后使用一些正则表达式替换和拆分。

df.groupby('id').name.apply(lambda x: str(list(x)))\
          .str.replace("[\[\],']", "")\
          .str.split(expand=True).fillna(0)\
          .rename(columns = lambda x: 'n{}'.format(x + 1))

     n1   n2   n3   n4
id                    
1   abc  qwe  zxc    0
2   sdf  wed  abc  mnb

通过使用 dfply 包,可以像 R 的 dcast 那样做。

# for Python3 only
pip install dfply

使用dfplyspread函数。

import pandas as pd
from io import StringIO
from dfply import *

csv = StringIO("""id,name,value
1,abc,10
1,qwe,23
1,zxc,12
2,sdf,10
2,wed,23
2,abc,12
2,mnb,11""")
df = pd.read_csv(csv)

df['sequence'] = df.groupby('id').cumcount()
df = df[["id", "sequence", "name"]] >> spread(X.sequence, X.name)
df = df.set_index("id").fillna(0).rename(columns = lambda x: 'n{}'.format(x + 1)).reset_index()
print(df)
#    id   n1   n2   n3   n4
# 0   1  abc  qwe  zxc    0
# 1   2  sdf  wed  abc  mnb