【问题标题】:Compare two Excel files and show divergences using Python比较两个 Excel 文件并使用 Python 显示差异
【发布时间】:2021-12-11 04:34:30
【问题描述】:

我正在比较两个包含两所学校学生信息的 excel 文件。但是,这些文件之间可能包含不同数量的行。

我使用的第一组是在两个数据框中导入excel文件:

df1 = pd.read_excel('School A - Information.xlsx')
df2 = pd.read_excel('School B - Information.xlsx')

print(df1)

       Name  Age Birth_Country  Previous Schools
0   tom   10           USA                 3
1  nick   15           MEX                 1
2  juli   14           CAN                 0
3   tom   19           NOR                 1

print(df2)

   Name  Age Birth_Country  Previous Schools
0   tom   10           USA                 3
1   tom   19           NOR                 1
2  nick   15           MEX                 4

在此之后,我想检查这两个数据帧之间的差异(索引顺序并不重要)。但是,由于数据帧的大小,我收到一个错误。

compare = df1.values == df2.values

<ipython-input-9-7cc64ba0e622>:1: DeprecationWarning: elementwise comparison failed; this will raise an error in the future.
  compare = df1.values == df2.values

print(compare)
False

除此之外,我想创建第三个具有相应差异的 DataFrame,以显示差异。

import numpy as np
rows,cols=np.where(compare==False)

for item in zip(rows,cols):
    df1.iloc[item[0], item[1]] = '{} --> {}'.format(df1.iloc[item[0], item[1]],df2.iloc[item[0], item[1]])

但是,使用此代码不起作用,因为两个数据帧之间的索引顺序可能不同。

我的预期输出应该是以下数据框:

【问题讨论】:

  • 一种方法是选择几行作为将用于比较的键,因此,例如,第一个数据帧上与 nick 相关的行将是与第二个上的nick 相比。但是,看看tom:在第二个数据帧上有两个名为tom 的学生,那么哪一个对应于第一个数据帧上的tom?您可以使用同时使用nameage 的复合键,这将打破上面示例中的平局,但是如果在同一个班级中有两个名为bob 且年龄相同的学生会发生什么情况?

标签: python pandas dataframe matching


【解决方案1】:

您可以使用pd.merge 来完成此操作。如果您不熟悉数据框合并,这里有一篇描述关系数据库合并想法的帖子:link。所以在这种情况下,我们要做的是首先将df2 合并到df1 上,以找出Previous Schools 列的不同之处:

df_merged = pd.merge(df1, df2, how="left", on=["Name", "Age", "Birth_Country"], suffixes=["_A", "_B"])
print(df_merged)

会给你一个新的数据框

   Name  Age Birth_Country  Previous Schools_A  Previous Schools_B
0   tom   10           USA                   3                 3.0
1  nick   15           MEX                   1                 4.0
2  juli   14           CAN                   0                 NaN
3   tom   19           NOR                   1                 1.0

这个新数据框包含您正在寻找的所有信息。要仅查找 Previous Schools 条目不同的行:

df_different = df_merged[df_merged["Previous Schools_A"]!=df_merged["Previous Schools_B"]]
print(df_different)
   Name  Age Birth_Country  Previous Schools_A  Previous Schools_B
1  nick   15           MEX                   1                 4.0
2  juli   14           CAN                   0                 NaN

并查找Previous Schools 未更改的行:

df_unchanged = df_merged[df_merged["Previous Schools_A"]==df_merged["Previous Schools_B"]]
print(df_unchanged)
  Name  Age Birth_Country  Previous Schools_A  Previous Schools_B
0  tom   10           USA                   3                 3.0
3  tom   19           NOR                   1                 1.0

如果我是你,我会停在这里,因为创建你想要的最终数据框将具有通用的 object 列类型,因为字符串和整数的混合,这将限制它的用途......但也许出于某种原因,您需要在特定格式中使用它。在这种情况下,一切都是以正确的方式将这些数据帧子集组合在一起以获得所需的格式。这是一种方法。

首先,使用未更改的行初始化最终数据框:

df_final = df_unchanged[["Name", "Age", "Birth_Country", "Previous Schools_A"]].copy()
df_final = df_final.rename(columns={"Previous Schools_A": "Previous Schools"})
print(df_final)
  Name  Age Birth_Country  Previous Schools
0  tom   10           USA                 3
3  tom   19           NOR                 1

现在处理在数据帧之间发生变化的条目。这里有两种情况:条目已更改(其中Previous Schools_B 不是NaN)和条目是新的(其中Previous Schools_BNaN)。我们将依次处理:

changed_entries = df_different[~pd.isnull(df_different["Previous Schools_B"])].copy()
changed_entries["Previous Schools"] = changed_entries["Previous Schools_A"].astype('str') + " --> " + changed_entries["Previous Schools_B"].astype('int').astype('str')
changed_entries = changed_entries.drop(columns=["Previous Schools_A", "Previous Schools_B"])
print(changed_entries)
   Name  Age Birth_Country Previous Schools
1  nick   15           MEX          1 --> 4

现在处理全新的条目:

new_entries = df_different[pd.isnull(df_different["Previous Schools_B"])].copy()
new_entries = "NaN --> " + new_entries[["Name", "Age", "Birth_Country", "Previous Schools_A"]].astype('str')
new_entries = new_entries.rename(columns={"Previous Schools_A": "Previous Schools"})
print(new_entries)
           Name         Age Birth_Country Previous Schools
2  NaN --> juli  NaN --> 14   NaN --> CAN        NaN --> 0

最后,连接所有数据帧:

df_final = pd.concat([df_final, changed_entries, new_entries])
print(df_final)
           Name         Age Birth_Country Previous Schools
0           tom          10           USA                3
3           tom          19           NOR                1
1          nick          15           MEX          1 --> 4
2  NaN --> juli  NaN --> 14   NaN --> CAN        NaN --> 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 1970-01-01
    • 2017-05-10
    相关资源
    最近更新 更多