
在数据分析实践中,我们经常会遇到需要对dataframe进行分组操作,并根据组内特定条件来生成新列的场景。例如,给定一个包含col1(分组键)、col2(条件列)和col3(取值列)的dataframe,我们的目标是创建一个新列new_col,其填充逻辑如下:
为了更好地理解这一需求,我们以下面的示例数据为例:
| index | Col1 | Col2 | Col3 |
|---|---|---|---|
| 0 | 1 | X | ABC |
| 1 | 1 | Y | XX |
| 2 | 1 | X | QW |
| 3 | 2 | X | VB |
| 4 | 2 | X | AY |
| 5 | 3 | X | MM |
| 6 | 3 | X | YY |
| 7 | 3 | Y | XX |
我们期望得到的输出结果是:
| index | Col1 | Col2 | Col3 | New_Col |
|---|---|---|---|---|
| 0 | 1 | X | ABC | XX |
| 1 | 1 | Y | XX | XX |
| 2 | 1 | X | QW | XX |
| 3 | 2 | X | VB | VB |
| 4 | 2 | X | AY | AY |
| 5 | 3 | X | MM | XX |
| 6 | 3 | X | YY | XX |
| 7 | 3 | Y | XX | XX |
可以看到,对于Col1为1的分组,由于Col2中存在'Y'(在index=1处),其对应的Col3值为'XX',因此该分组所有行的New_Col都被填充为'XX'。对于Col1为2的分组,由于Col2中不存在'Y',所以New_Col直接复制了Col3的值。对于Col1为3的分组,同样因为Col2中存在'Y'(在index=7处),其对应的Col3值为'XX',所以New_Col被填充为'XX'。
首先,我们需要导入Pandas库并创建示例DataFrame:
import pandas as pd
import numpy as np
# 创建示例DataFrame
data = {
'Col1': [1, 1, 1, 2, 2, 3, 3, 3],
'Col2': ['X', 'Y', 'X', 'X', 'X', 'X', 'X', 'Y'],
'Col3': ['ABC', 'XX', 'QW', 'VB', 'AY', 'MM', 'YY', 'XX']
}
df = pd.DataFrame(data)
df.index.name = 'index' # 为索引命名,与示例表格保持一致
print("原始DataFrame:")
print(df)输出:
原始DataFrame:
Col1 Col2 Col3
index
0 1 X ABC
1 1 Y XX
2 1 X QW
3 2 X VB
4 2 X AY
5 3 X MM
6 3 X YY
7 3 Y XX解决此类问题的关键在于巧妙结合Pandas的mask()、groupby().transform()和fillna()方法。
首先,我们需要筛选出Col3中那些与Col2 == 'Y'条件对应的行,而将其他行的Col3值“隐藏”起来(即替换为NaN)。mask()函数非常适合这个任务,它会根据条件将DataFrame或Series中的值替换为指定值(默认为NaN)。
我们希望保留Col2 == 'Y'时的Col3值,因此条件应该是Col2 != 'Y'时进行掩盖。
# 步骤1: 掩盖不符合条件的值
# 只有当Col2为'Y'时,才保留Col3的值,否则替换为NaN
masked_col3 = df['Col3'].mask(df['Col2'] != 'Y')
print("\n步骤1: 掩盖后的Col3 Series:")
print(masked_col3)输出:
步骤1: 掩盖后的Col3 Series: index 0 NaN 1 XX 2 NaN 3 NaN 4 NaN 5 NaN 6 NaN 7 XX Name: Col3, dtype: object
通过这一步,我们得到了一个Series,其中只有Col2为'Y'的行保留了其对应的Col3值,其余都变成了NaN。
接下来,我们需要在每个Col1分组内部,将上一步得到的非NaN值(即Col2 == 'Y'时对应的Col3值)传播到该分组的所有行。groupby().transform('first')是实现这一目标的高效方法。
# 步骤2: 在分组内传播第一个非NaN值
# 对于每个Col1分组,获取第一个非NaN的Col3值,并填充到该分组所有行
propagated_values = masked_col3.groupby(df['Col1']).transform('first')
print("\n步骤2: 分组传播后的值:")
print(propagated_values)输出:
步骤2: 分组传播后的值: index 0 XX 1 XX 2 XX 3 None 4 None 5 XX 6 XX 7 XX Name: Col3, dtype: object
观察输出,对于Col1为1和3的分组,由于它们包含Col2 == 'Y'的行,其对应的Col3值'XX'被成功传播到整个分组。而对于Col1为2的分组,因为原始的masked_col3中所有值都是NaN,所以transform('first')也返回了None。
最后一步是处理那些在步骤二中仍然是NaN(或None)的行。这些行对应的是Col1分组中Col2从未包含'Y'的情况。根据我们的需求,这些行应该直接复制它们原始的Col3值。fillna()函数可以完美地实现这一点。
# 步骤3: 填充剩余的NaN值
# 将步骤2中仍为NaN(或None)的值,用原始的Col3值填充
df['New_Col'] = propagated_values.fillna(df['Col3'])
print("\n最终DataFrame:")
print(df)输出:
最终DataFrame:
Col1 Col2 Col3 New_Col
index
0 1 X ABC XX
1 1 Y XX XX
2 1 X QW XX
3 2 X VB VB
4 2 X AY AY
5 3 X MM XX
6 3 X YY XX
7 3 Y XX XX现在,New_Col列已经按照预期被正确填充。Col1为2的分组,其New_Col值现在是Col3的原始值('VB', 'AY'),而其他分组则保留了'XX'。
将上述三个步骤整合起来,可以得到一个简洁高效的解决方案:
import pandas as pd
import numpy as np
# 创建示例DataFrame
data = {
'Col1': [1, 1, 1, 2, 2, 3, 3, 3],
'Col2': ['X', 'Y', 'X', 'X', 'X', 'X', 'X', 'Y'],
'Col3': ['ABC', 'XX', 'QW', 'VB', 'AY', 'MM', 'YY', 'XX']
}
df = pd.DataFrame(data)
df.index.name = 'index'
# 使用链式操作实现条件填充
df['New_Col'] = (df['Col3']
.mask(df['Col2'] != 'Y') # 步骤1: 掩盖非条件值
.groupby(df['Col1']) # 步骤2: 按Col1分组
.transform('first') # 步骤2: 传播第一个非NaN值
.fillna(df['Col3'])) # 步骤3: 填充剩余的NaN值
print("\n最终结果DataFrame:")
print(df)通过本文的详细讲解,我们掌握了在Pandas中根据分组和复杂条件填充新列的有效方法,这对于处理现实世界中的多样化数据转换需求具有重要的指导意义。
以上就是Pandas高级数据处理:基于分组和条件填充新列的实践指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号