将 pandas 中的一个单元格拆分为多行
Posted
技术标签:
【中文标题】将 pandas 中的一个单元格拆分为多行【英文标题】:Splitting a cell in pandas into multiple rows 【发布时间】:2021-06-26 03:40:56 【问题描述】:解释这个问题有点棘手。 我想将包含多个用逗号分隔的字符串值的单元格拆分为不同的行。下面的 df 是一个小示例,但实际数据集最多包含 15 列和 15 行,每个单元格中有 5 到 6 个非唯一字符串值,我需要将它们分成不同的行。
我们如何将原始df拆分为转换后的df?
原始df
import pandas as pd
df = pd.DataFrame("Privileges":['Type1','Type2','Type3'],"Super_Admin":["A1,A2,A3,A4","A1,B1,B2,B3, A4","C1,B2,C2,C3"], "Admin":["A1,A2","A1,B1,B2","B2, C1,C2"])
Index | Privileges | Super_Admin | Admin |
---|---|---|---|
0 | Type1 | A1,A2,A3,A4 | A1,A2 |
1 | Type2 | A1,B1,B2,B3, A4 | A1,B1,B2 |
2 | Type3 | C1,B2,C2,C3 | B2, C1,C2 |
变换后的df
df = pd.DataFrame("Privileges":['Type1','Type1','Type1','Type1','Type2','Type2','Type2','Type2','Type2','Type3','Type3','Type3','Type3'],"Super_Admin":["A1","A2","A3","A4","A1", "B1","B2","B3", "A4","C1","B2","C2","C3"], "Admin":["A1","A2",'', '',"A1","B1","B2",'', '', "B2", "C1","C2", ''])
Index | Privileges | Super_Admin | Admin |
---|---|---|---|
0 | Type1 | A1 | A1 |
1 | Type1 | A2 | A2 |
2 | Type1 | A3 | -- |
3 | Type1 | A4 | -- |
4 | Type2 | A1 | A1 |
5 | Type2 | B1 | B1 |
6 | Type2 | B2 | B2 |
7 | Type2 | B3 | -- |
8 | Type2 | A4 | -- |
9 | Type3 | C1 | B2 |
10 | Type3 | B2 | C1 |
11 | Type3 | C2 | C2 |
12 | Type3 | C3 | -- |
【问题讨论】:
也感谢您分享数据帧代码。大多数人只是共享图片或文本数据框。你有完整的代码。它可以帮助我们快速解决问题。 Type 3 是否应该有 Super_Admin 和 Admin 匹配的值。例如:它有 C1 B2 和 B2 C1。应该是 C1 C1 和 B2 B2 吗? 查看几年前exploding multiple columns...上的帖子 【参考方案1】:以下步骤的细分:
zip所有列
使用zip_longest 将None
与没有配对的值配对
将两个列表合二为一,chain
在Privileges
列上创建数据框和forward fill
In [541]: step1 = zip(df.Privileges, df.Super_Admin, df.Admin)
In [542]: step2 = (zip_longest([first], second,last)
for first, second, last
in step1)
In [543]: step3 = chain.from_iterable(step2)
In [546]: (pd.DataFrame(step3, columns = df.columns)
.assign(Privileges = lambda df: df.Privileges.ffill())
)
Out[546]:
Privileges Super_Admin Admin
0 Type1 A1 A1
1 Type1 A2 A2
2 Type1 A3 None
3 Type1 A4 None
4 Type2 A1 A1
5 Type2 B1 B1
6 Type2 B2 B2
7 Type2 B3 None
8 Type2 A4 None
9 Type3 C1 B2
10 Type3 B2 C1
11 Type3 C2 C2
12 Type3 C3 None
为了获得更快的速度,您可以将拆分步骤移至原生 python 领域。 pandas 字符串方法是 Python 字符串函数的包装器,因此它们不如 Python 字符串函数快。
【讨论】:
太快了 :) 我刚开始,你已经有了。我的会太复杂。这看起来很整洁。赞成。【参考方案2】:这是选项。
首先将Super_Admin
和Admin
添加到列表中。这对于使用pd.explode()
很有用。
df['Super_Admin'] = df['Super_Admin'].apply(lambda x: x.split(','))
df['Admin'] = df['Admin'].apply(lambda x: x.split(','))
然后在两列上调用explode
,并用空字符串填充缺失值。
a = df.explode('Super_Admin')
b = df.explode('Admin')
for i in range(3):
short = b.loc[i,'Admin'].values
long = a.loc[i,'Admin'].values
a.loc[i,'Admin'] = np.concatenate((short, ['']*(len(long)-len(short))), axis=0)
输出如下所示:
>>> a
Privileges Super_Admin Admin
0 Type1 A1 A1
0 Type1 A2 A2
0 Type1 A3
0 Type1 A4
1 Type2 A1 A1
1 Type2 B1 B1
1 Type2 B2 B2
1 Type2 B3
1 Type2 A4
2 Type3 C1 B2
2 Type3 B2 C1
2 Type3 C2 C2
2 Type3 C3
【讨论】:
【参考方案3】:您可以使用单个虽然基本上是链式的指令来完成这项工作:
result = df.set_index('Privileges').apply(lambda col:
col.str.split(', ?', expand=True).stack())\
.droplevel(1).reset_index().fillna('')
步骤:
df.set_index('Privileges')
- 将 Privileges 设置为索引列。
apply(lambda col:
- 应用于每一列:
col.str.split(', ?', expand=True)
- 将此列分成单独的
列(DataFrame,列名称为连续整数)。
stack()
- 将上面的 DataFrame 转换成带有列的 Series
命名为第二个 MultiIndex 级别。
droplevel(1)
- 删除不必要的索引级别。
reset_index()
- 将 Privileges(索引列)更改为常规列。
fillna('')
- 将每个 NaN 更改为空字符串。
结果是:
Privileges Super_Admin Admin
0 Type1 A1 A1
1 Type1 A2 A2
2 Type1 A3
3 Type1 A4
4 Type2 A1 A1
5 Type2 B1 B1
6 Type2 B2 B2
7 Type2 B3
8 Type2 A4
9 Type3 C1 B2
10 Type3 B2 C1
11 Type3 C2 C2
12 Type3 C3
【讨论】:
以上是关于将 pandas 中的一个单元格拆分为多行的主要内容,如果未能解决你的问题,请参考以下文章