使用 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.apply
和 Series
构造函数的解决方案:
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
使用dfply
的spread
函数。
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
我有一个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.apply
和 Series
构造函数的解决方案:
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
使用dfply
的spread
函数。
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