
在数据处理和分析中,我们经常遇到需要对dataframe进行分组操作的场景。一个常见需求是,在每个分组内部,我们可能只关心第一行的某些特定信息,而希望将后续行的这些信息清空(设置为nan),同时保持其他列的数据不变。
例如,给定以下DataFrame:
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
'a': [
'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b',
],
'b': [
-20, 20, 20, 20,-70, -70,-11, -100, -1, -1, -100, 100
],
'c': [
'f', 'f', 'f', 'f', 'f', 'x', 'x', 'k', 'k', 'k', 'k', 'k'
],
'x': [
'p', 'p', 'p', 'p', 'p', 'x', 'x', 'i', 'i', 'i', 'i', 'i'
],
}
)
print("原始DataFrame:")
print(df)原始DataFrame:
a b c x 0 a -20 f p 1 a 20 f p 2 a 20 f p 3 a 20 f p 4 a -70 f p 5 a -70 x x 6 b -11 x x 7 b -100 k i 8 b -1 k i 9 b -1 k i 10 b -100 k i 11 b 100 k i
我们的目标是:
期望的输出如下:
a b c x 0 a -20.0 f p 1 a NaN NaN p 2 a NaN NaN p 3 a NaN NaN p 4 a NaN NaN p 5 a NaN NaN x 6 b -11.0 x x 7 b NaN NaN i 8 b NaN NaN i 9 b NaN NaN i 10 b NaN NaN i 11 b NaN NaN i
一个常见的初步尝试是使用groupby().apply()结合iloc和get_loc。虽然这种方法对于少量列可行,但当需要处理数百列时,手动指定每一列或在循环中迭代列会变得非常低效且难以维护。
# 低效的尝试(不推荐用于大量列)
def func(g):
# 假设我们知道要处理的列是'b'和'c'
g.iloc[1:, g.columns.get_loc('b')] = np.nan
g.iloc[1:, g.columns.get_loc('c')] = np.nan
return g
# df_modified = df.groupby('a', as_index=False).apply(func)
# print(df_modified)这种方法需要为每个需要NaN化的列单独操作,或者在一个循环中完成,这对于大型DataFrame和大量列来说效率不高。
Pandas提供了强大的矢量化操作,可以更高效地解决这类问题。我们将利用df.duplicated()、df.where()和df.fillna()的组合来实现目标。
import pandas as pd
import numpy as np
# 重新创建原始DataFrame以确保操作的独立性
df = pd.DataFrame(
{
'a': [
'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b',
],
'b': [
-20, 20, 20, 20,-70, -70,-11, -100, -1, -1, -100, 100
],
'c': [
'f', 'f', 'f', 'f', 'f', 'x', 'x', 'k', 'k', 'k', 'k', 'k'
],
'x': [
'p', 'p', 'p', 'p', 'p', 'x', 'x', 'i', 'i', 'i', 'i', 'i'
],
}
)
# 步骤1: 识别每个分组中的重复行(即非首行)
# df['a'].duplicated() 会为每个'a'组的第一个出现返回False,后续重复出现返回True
# ~df['a'].duplicated() 则会为每个'a'组的第一个出现返回True,后续重复出现返回False
mask = ~df['a'].duplicated()
# print("重复行掩码 (~df['a'].duplicated()):")
# print(mask)
# 步骤2: 使用df.where()进行条件性替换
# df.where(condition) 会保留condition为True的元素,将condition为False的元素替换为NaN
# 此时,所有非首行的值(包括'a'、'b'、'c'、'x')都会被替换为NaN
df_temp = df.where(mask)
# print("\n经过df.where(mask)处理后的DataFrame:")
# print(df_temp)
# 步骤3: 使用df.fillna()恢复需要保留原始值的列
# 我们希望保留'a'和'x'列的原始值。
# df.fillna(other_df) 会根据other_df来填充df中的NaN值。
# 只有在df中为NaN且other_df中对应位置有非NaN值时,才会进行填充。
# 并且,填充操作只针对other_df中存在的列进行。
columns_to_preserve = ['a', 'x']
df_final = df_temp.fillna(df[columns_to_preserve])
print("\n最终处理结果:")
print(df_final)输出结果:
最终处理结果:
a b c x
0 a -20.0 f p
1 a NaN NaN p
3 a NaN NaN p
2 a NaN NaN p
4 a NaN NaN p
5 a NaN NaN x
6 b -11.0 x x
7 b NaN NaN i
8 b NaN NaN i
9 b NaN NaN i
10 b NaN NaN i
11 b NaN NaN i注意:输出的行索引顺序可能与原始示例略有不同,这是因为Pandas在处理过程中可能会调整索引,但这不影响数据的逻辑对应关系。如果需要严格的索引顺序,可以在操作后进行sort_index()或reset_index()。
通过巧妙地结合duplicated()、where()和fillna()这三个Pandas函数,我们能够高效且灵活地实现DataFrame分组数据的首行保留与其余值NaN化处理。这种方法不仅代码简洁,而且在处理大规模数据集时表现出卓越的性能,是Pandas数据操作中值得掌握的实用技巧。
以上就是Pandas DataFrame分组数据首行保留与其余值NaN化处理的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号