Pandas DataFrame差异行识别与来源追踪教程(追踪.识别.差异.来源.教程...)

wufei123 发布于 2025-09-02 阅读(6)

Pandas DataFrame差异行识别与来源追踪教程

本教程详细介绍了如何使用Pandas识别两个DataFrame之间的差异行,并有效追踪这些差异行来源于哪个原始DataFrame。通过利用pd.merge函数的outer连接和indicator=True参数,我们可以生成一个_merge列,明确指示每行的来源(仅左侧、仅右侧或两侧共有)。教程还进一步探讨了如何在需要保留原始索引信息时,通过reset_index方法实现更全面的数据溯源。识别DataFrame差异行及其来源

在数据分析和数据清洗过程中,我们经常需要比较两个结构相似的pandas dataframe,找出它们之间的差异,并了解这些差异行分别来自哪个原始dataframe。例如,比较当前估计数据与历史估计数据,以识别发生变化的条目。pandas提供了强大的合并(merge)功能,结合特定的参数,可以高效地完成这项任务。

核心方法:外连接与指示器

要识别两个DataFrame的差异行并追踪其来源,最有效的方法是使用pd.merge函数进行outer(外连接)操作,并设置indicator=True参数。

  • how='outer': 外连接会保留左侧DataFrame中独有的行、右侧DataFrame中独有的行,以及两个DataFrame中都存在的匹配行。
  • indicator=True: 当设置为True时,pd.merge会在结果DataFrame中添加一个名为_merge的特殊列。这个列会指示每行是来源于左侧DataFrame(left_only)、右侧DataFrame(right_only),还是同时来源于两个DataFrame(both)。

通过过滤_merge列,我们可以轻松地提取出差异行(即_merge不等于'both'的行),并直接获取它们的来源信息。

示例:识别并标记差异行

假设我们有两个DataFrame,df_current_est(当前估计)和df_previous_est(先前估计),它们可能包含相同的列,我们希望找出两者之间不同的行,并标记这些行来自哪个原始DataFrame。

import pandas as pd

# 示例数据
df_current_est = pd.DataFrame({
    'EstimateID': [10061, 10062, 10063],
    'Season': [2023, 2023, 2023],
    'RPIN': ['R1000', 'R2000', 'R3000'],
    'GrowMethodCode': ['FO', 'FO', 'FO'],
    'Variety_code': ['002', '068', '001'],
    'Packout_pc': [0.60, 0.76, 0.80],
    'QtyBins': [320, 1000, 500]
})

df_previous_est = pd.DataFrame({
    'EstimateID': [10061, 10062, 10064],
    'Season': [2023, 2023, 2023],
    'RPIN': ['R1000', 'R2000', 'R4000'],
    'GrowMethodCode': ['FO', 'FO', 'FO'],
    'Variety_code': ['002', '068', '003'],
    'Packout_pc': [0.60, 0.76, 0.90],
    'QtyBins': [9000, 90000, 600] # 注意 QtyBins 差异
})

# 执行外连接并添加指示器
merged_df = pd.merge(df_current_est, df_previous_est, how='outer', indicator=True)

# 筛选出差异行(_merge不为'both'的行)
changed_df = merged_df[merged_df['_merge'] != 'both'].copy()

# 对结果进行排序以便观察
changed_df = changed_df.sort_values(by=['EstimateID', '_merge'], ascending=True)

print("识别出的差异行及其来源:")
print(changed_df)

代码解释:

  1. pd.merge(df_current_est, df_previous_est, how='outer', indicator=True): 这一步是核心。它将两个DataFrame进行外连接,并生成_merge列。
  2. changed_df = merged_df[merged_df['_merge'] != 'both'].copy(): 我们通过筛选_merge列来获取所有不完全匹配的行。copy()操作是为了避免SettingWithCopyWarning,确保我们操作的是一个独立的DataFrame。
  3. changed_df.sort_values(...): 对结果进行排序,便于分析。

输出示例:

识别出的差异行及其来源:
   EstimateID  Season   RPIN GrowMethodCode Variety_code  Packout_pc  QtyBins      _merge
0       10061    2023  R1000             FO          002        0.60      320   left_only
3       10061    2023  R1000             FO          002        0.60     9000  right_only
1       10062    2023  R2000             FO          068        0.76     1000   left_only
4       10062    2023  R2000             FO          068        0.76    90000  right_only
2       10063    2023  R3000             FO          001        0.80      500   left_only
5       10064    2023  R4000             FO          003        0.90      600  right_only

从输出可以看出,_merge列清晰地指示了每行是来自df_current_est(left_only)还是df_previous_est(right_only)。例如,EstimateID为10061的行在两个DataFrame中都有,但QtyBins值不同,因此它们都被标记为差异行,分别指示了各自的来源。

注意事项:不要随意删除_merge列

在获取到包含_merge列的结果后,如果你需要利用该列的信息来判断行的来源,请务必不要将其删除。原始问题中的代码片段就犯了这个错误:

# 错误示例:不应删除_merge列
changed_df.drop('_merge', axis=1, inplace=True)

如果删除了_merge列,你将失去行的来源信息,这与你最初的目标相悖。

处理原始索引的需求

上述方法能够很好地追踪行的来源,但它不会保留原始DataFrame的索引。在某些场景下,你可能不仅关心行来自哪个DataFrame,还需要知道它在原始DataFrame中的具体索引。_merge列并不能保留原始索引信息。

解决方案:将索引转换为普通列

如果需要保留原始索引,你需要在进行merge操作之前,先使用reset_index()方法将每个DataFrame的索引转换为一个普通的数据列。这样,在合并之后,原始索引信息就会作为新的列存在于结果DataFrame中。

import pandas as pd

# 示例数据,包含自定义索引
df1 = pd.DataFrame({'key': ['one', 'two', 'three'], 'value1': [1, 2, 3]},
                   index=[101, 102, 103])
df2 = pd.DataFrame({'key': ['two', 'four', 'three'], 'value2': [20, 40, 30]},
                   index=[201, 202, 203])

print("df1:")
print(df1)
print("\ndf2:")
print(df2)

# 将索引重置为列,然后进行外连接
merged_df_with_index = (
    pd.merge(df1.reset_index(),
             df2.reset_index(),
             on='key',               # 基于'key'列合并
             suffixes=('_df1', '_df2'), # 为重复列添加后缀
             how='outer',
             indicator=True)
    .query('_merge != "both"') # 筛选差异行
)

print("\n带有原始索引的差异行:")
print(merged_df_with_index)

代码解释:

  1. df1.reset_index() 和 df2.reset_index(): 这一步将每个DataFrame的当前索引转换为一个名为index的列。
  2. suffixes=('_df1', '_df2'): 由于两个DataFrame都将有一个名为index的列,suffixes参数用于为这些同名列添加后缀,以区分它们来自哪个原始DataFrame(例如,index_df1和index_df2)。
  3. .query('_merge != "both"'): 这是一个简洁的筛选方式,等同于merged_df_with_index[merged_df_with_index['_merge'] != 'both']。

输出示例:

df1:
     key  value1
101  one       1
102  two       2
103  three     3

df2:
     key  value2
201  two      20
202  four     40
203  three    30

带有原始索引的差异行:
   index_df1   key  value1  index_df2  value2      _merge
0      101.0   one     1.0        NaN     NaN   left_only
3        NaN  four     NaN      202.0    40.0  right_only

现在,结果DataFrame中包含了index_df1和index_df2列,它们分别存储了差异行在df1和df2中的原始索引。对于left_only的行,index_df2为NaN;对于right_only的行,index_df1为NaN。

扩展应用:查找匹配行的最大索引

在某些分析场景中,你可能还需要找出那些在两个DataFrame中都匹配的行(即_merge == "both"),并获取它们在原始DataFrame中的最大索引。这在需要追踪最新版本或最高ID的匹配记录时非常有用。

# 查找匹配行(_merge == "both")的最大索引
max_id_same = (
    pd.merge(df1.reset_index(),
             df2.reset_index(),
             on='key',
             suffixes=('_df1', '_df2'),
             how='outer',
             indicator=True)
    .query('_merge == "both"') # 筛选匹配行
)[['index_df1', 'index_df2']].max() # 选择索引列并取最大值

print("\n匹配行(_merge == \"both\")的最大索引:")
print(max_id_same)

输出示例:

匹配行(_merge == "both")的最大索引:
index_df1    103.0
index_df2    203.0
dtype: float64

这表明在df1中,匹配行的最大索引是103,在df2中是203。

总结

本教程详细阐述了在Pandas中比较两个DataFrame并识别差异行的两种主要场景:

  1. 仅追踪行来源: 使用pd.merge(how='outer', indicator=True),然后筛选_merge列中不为'both'的行。_merge列将直接指示行是left_only还是right_only。关键是不要删除_merge列。
  2. 同时追踪行来源和原始索引: 在pd.merge之前,对每个DataFrame使用reset_index()将索引转换为普通列。合并时使用suffixes参数处理同名索引列,然后进行筛选。

掌握这些技术,可以帮助你在数据分析工作中更精确地理解和管理DataFrame之间的变化,从而提高数据处理的效率和准确性。

以上就是Pandas DataFrame差异行识别与来源追踪教程的详细内容,更多请关注知识资源分享宝库其它相关文章!

标签:  追踪 识别 差异 

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。