python pandas - 通过自定义 ID 以多种方式比较两个数据帧

python pandas - compare two dataframes in multiple ways by custom ID

我需要检查两个具有不同日期的数据集随时间发生的变化:

之前的日期:

Date      ID        Value      Category  Subcategory
30-Nov    0001      100.00     A         A100
30-Nov    0002      200.00     B         B120
30-Nov    0003      300.00     C         C300
30-Nov    0004      450.00     D         D900
30-Nov    0005      500.00     D         D900

当前日期:

Date      ID        Value      Category  Subcategory
31-Dec    0001      100.00     A         A100
31-Dec    0002      200.00     B         B101
31-Dec    0003      300.00     C         C300
31-Dec    0004      400.00     E         E900
31-Dec    0006      600.00     D         D900

现在我需要创建 4 个数据框:

  1. 价值变化:
Date      ID        Value      Category  Subcategory Prior Value
31-Dec    0004      400.00     E         E900        450.00
  1. 类别变化:
Date      ID        Value      Category  Subcategory Prior Category
31-Dec    0004      400.00     E         E900        D
  1. 子类别的变化,但前提是类别没有变化:
Date      ID        Value      Category  Subcategory Prior Subcategory
31-Dec    0002      200.00     B         B101        B120
  1. 项目人口变化:
Date      ID        Value      Category  Subcategory
31-Dec    0006      600.00     D         D900
30-Nov    0005      500.00     D         D900

我想我应该首先 运行 人口检查并排除那些中断,所以我将只有两个具有相同 ID 集的数据集。我将按照此处的示例进行操作: Comparing two dataframes and getting the differences

为了比较 1to1 值,我找到了一段 numpy 代码,但它是通过默认索引而不是 ID 进行比较的,如何使用我的 ID 列作为记录标识符来进行比较?这将是一个大型数据集,我无法将其基于默认索引。

value_df = current_df
value_df['prior value'] = np.where(prior_df['Value'] == current_df['Value'], 'Match', prior_df['Value'])
value_df = value_df[value_df['prior value'] != 'Match']

对于多个条件,我必须逐步过滤掉它(首先过滤掉类别变化,然后过滤子类别变化)还是我可以使用 AND 连接条件?

下面是创建数据帧的代码:

prior_data = {'Date': ['30-Nov','30-Nov','30-Nov','30-Nov', '30-Nov'],
          'ID': ['0001','0002','0003','0004', '0005'],
          'Value' : [100.00, 200.00, 300.00, 450.00, 500.00],
          'Category' : ['A','B','C','D','D'],
          'Subcategory' : ['A100','B120','C300','D900','D900']}


current_data = {'Date': ['31-Dec','31-Dec','31-Dec','31-Dec','31-Dec'],
          'ID': ['0001','0002','0003','0004', '0006'],
          'Value' : [100.00, 200.00, 300.00, 400.00, 600.00],
          'Category' : ['A','B','C','E','D'],
          'Subcategory' : ['A100','B101','C300','E900','D900']}

prior_df = pd.DataFrame(prior_data)
current_df = pd.DataFrame(current_data)

我不确定这是否是最快的解决方案,但这个问题似乎需要 pd.merge。正如您所说,让我们首先处理一个数据框中的内容,而不是另一个数据框中的内容:

def get_only_left(df1, df2):
    left_merge = pd.merge(df1, df2, on='ID', suffixes=('', '_other'), how='left')
    added_columns = [c + '_other' for c in df1.columns if c != 'ID']
    mask = left_merge.loc[:, added_columns].isna().all(axis=1)
    return left_merge[mask].drop(added_columns, axis=1)

pd.concat([get_only_left(prior_df, current_df), get_only_left(current_df, prior_df)])

这给

     Date    ID  Value Category Subcategory
4  30-Nov  0005  500.0        D        D900
4  31-Dec  0006  600.0        D        D900

然后,让我们处理适当改变的值。

columns = list(current_df.columns)
df = pd.merge(current_df, prior_df, on='ID', suffixes=('', '_prior'), how='inner')
mask = df['Value'] != df['Value_prior']
df[mask].loc[:, columns + ['Value_prior']]

这给

     Date    ID  Value Category Subcategory  Value_prior
3  31-Dec  0004  400.0        E        E900        450.0

然后类似地:

mask = df['Category'] != df['Category_prior']
df[mask].loc[:, columns + ['Category_prior']]

给予

     Date    ID  Value Category Subcategory Category_prior
3  31-Dec  0004  400.0        E        E900              D

最后

import numpy as np
mask = np.logical_and(df['Category'] == df['Category_prior'], df['Subcategory'] != df['Subcategory_prior'])
df[mask].loc[:, columns + ['Subcategory_prior']]

给予

     Date    ID  Value Category Subcategory Subcategory_prior
1  31-Dec  0002  200.0        B        B101              B120