
在pandas中,当尝试根据另一个dataframe (df2) 的匹配条件来更新第一个dataframe (df1) 的部分行时,一个常见的错误是使用链式索引操作,例如:
import pandas as pd
df1 = pd.DataFrame({'a':(1,2,3,4),'b':(10,20,30,40),'c':(100,200,300,400)})
df2 = pd.DataFrame({'a':(1,2,3),'b':(10,20,30),'c':(1111,2222,3333)})
print("原始 df1:")
print(df1)
print("\ndf2:")
print(df2)
# 尝试进行更新
df1.set_index(['a', 'b']).loc[df2.set_index(['a', 'b']).index, 'c'] = df2.c
print("\n尝试更新后的 df1 (未成功):")
print(df1)输出结果:
原始 df1: a b c 0 1 10 100 1 2 20 200 2 3 30 300 3 4 40 400 df2: a b c 0 1 10 1111 1 2 20 2222 2 3 30 3333 尝试更新后的 df1 (未成功): a b c 0 1 10 100 1 2 20 200 2 3 30 300 3 4 40 400
如您所见,df1 的 'c' 列并未按照 df2 的值进行更新。这是因为 df1.set_index(['a', 'b']) 操作返回的是一个新的 DataFrame 对象(一个副本),而不是原始 df1 的视图。随后的 .loc[...] = df2.c 赋值操作仅仅作用于这个临时的副本,一旦该语句执行完毕,这个副本就会被丢弃,原始 df1 保持不变。
为了实现预期的更新效果,我们需要采用能够正确引用并修改原始 DataFrame 的方法。以下是两种推荐的解决方案。
当 df1 具有默认的、连续的范围索引时,merge 结合 combine_first 是一个简洁且高效的解决方案。这种方法通过合并两个 DataFrame 来获取更新值,然后用原始值填充未匹配的空值。
import pandas as pd
df1 = pd.DataFrame({'a':(1,2,3,4),'b':(10,20,30,40),'c':(100,200,300,400)})
df2 = pd.DataFrame({'a':(1,2,3),'b':(10,20,30),'c':(1111,2222,3333)})
# 使用 merge 和 combine_first 更新 df1
# 1. 提取 df1 的关键列 'a', 'b',并与 df2 进行左合并
# 这将为匹配的行引入 df2 的 'c' 值,未匹配的行 'c' 值将为 NaN
merged_df = df1[['a', 'b']].merge(df2, on=['a', 'b'], how='left')
# 2. 使用 combine_first 将 merged_df 中的 NaN 值替换为 df1 中对应的原始 'c' 值
# 并将结果赋值给 df1 的 'c' 列
df1['c'] = merged_df['c'].combine_first(df1['c'])
print("\n使用 merge 和 combine_first 更新后的 df1:")
print(df1)代码解释:
预期输出:
使用 merge 和 combine_first 更新后的 df1: a b c 0 1 10 1111.0 1 2 20 2222.0 2 3 30 3333.0 3 4 40 400.0
注意: 这里的 c 列数据类型可能会变为浮点型(float64),因为 NaN 值通常以浮点数表示。如果需要保持整数类型,可能需要后续转换,例如 df1['c'] = df1['c'].astype(int)(但这会要求数据中没有实际的 NaN,或者使用 Pandas 1.0+ 引入的 Int64Dtype)。
当 df1 具有非默认的、自定义的索引,或者需要更精细地控制更新过程时,可以采用以下更通用的 merge 组合方案。此方法通过 reset_index 暂时将索引转换为普通列,进行合并,然后恢复索引并填充 NaN 值。
import pandas as pd
df1 = pd.DataFrame({'a':(1,2,3,4),'b':(10,20,30,40),'c':(100,200,300,400)})
df2 = pd.DataFrame({'a':(1,2,3),'b':(10,20,30),'c':(1111,2222,3333)})
# 为了演示非默认索引,我们先给 df1 设置一个自定义索引
# df1 = df1.set_index('a') # 假设 df1 的索引是 'a' 列
# print("带有自定义索引的 df1:")
# print(df1)
# 通用更新方案
# 1. 重置 df1 的索引,将原始索引保存为名为 'index' 的列
temp_df = df1.reset_index()
# 2. 将 temp_df 与 df2 进行左合并,基于 'a' 和 'b'
# 合并结果中的 'c' 列将包含来自 df2 的更新值(或 NaN)
merged_result = temp_df.merge(df2[['a', 'b', 'c']], on=['a', 'b'], how='left', suffixes=('_df1', '_df2'))
# 3. 重新设置索引为原始索引,并选择来自 df2 的 'c' 列(即 'c_df2')
# 如果 df2 没有匹配,则 'c_df2' 为 NaN
updated_c_series = merged_result.set_index('index')['c_df2']
# 4. 使用 df1 原始的 'c' 值填充 NaN
df1['c'] = updated_c_series.fillna(df1['c'])
print("\n使用通用 merge 方案更新后的 df1:")
print(df1)代码解释:
预期输出:
使用通用 merge 方案更新后的 df1: a b c 0 1 10 1111.0 1 2 20 2222.0 2 3 30 3333.0 3 4 40 400.0
在 Pandas 中,直接对链式索引操作进行赋值通常不会修改原始 DataFrame,因为它操作的是一个临时副本。为了可靠地根据匹配条件更新 DataFrame 的子集行,我们应采用 merge 等方法来构建包含更新值的新 Series,然后将其赋值回原始 DataFrame 的目标列。无论是通过 merge 和 combine_first 的组合,还是通过 reset_index、merge 和 fillna 的通用方案,都能有效且高效地实现这一目标,同时避免常见的陷阱。理解这些方法背后的原理对于编写健壮的 Pandas 数据处理代码至关重要。
以上就是Pandas DataFrame中基于条件更新列值:原理与实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号