比较两个熊猫数据框的差异
Posted
技术标签:
【中文标题】比较两个熊猫数据框的差异【英文标题】:Comparing two pandas dataframes for differences 【发布时间】:2013-11-23 21:58:07 【问题描述】:我有一个更新 5-10 列数据的脚本,但有时起始 csv 将与结束 csv 相同,因此我不想编写相同的 csv 文件,而是希望它什么都不做......
如何比较两个数据框以检查它们是否相同?
csvdata = pandas.read_csv('csvfile.csv')
csvdata_old = csvdata
# ... do stuff with csvdata dataframe
if csvdata_old != csvdata:
csvdata.to_csv('csvfile.csv', index=False)
有什么想法吗?
【问题讨论】:
不是 Pandas 专家,但不应该进行正常的相等比较工作吗? 我已经查看了相等性,但我不确定如何使用该功能,我也无法在搜索中找到任何内容:( 【参考方案1】:您还需要小心创建 DataFrame 的副本,否则 csvdata_old 将使用 csvdata 更新(因为它指向同一个对象):
csvdata_old = csvdata.copy()
查看是否相等,可以use assert_frame_equal as in this answer:
from pandas.util.testing import assert_frame_equal
assert_frame_equal(csvdata, csvdata_old)
您可以将其包装在一个函数中,例如:
try:
assert_frame_equal(csvdata, csvdata_old)
return True
except: # appeantly AssertionError doesn't catch all
return False
讨论了更好的方法...
【讨论】:
由于某种原因我得到了异常:Exception: Can only compare identically-labeled DataFrame objects
那给了你答案,对吧?这是说标签(行和列名称/值)不相同,因此 DataFrames 不能相同。在 Andy 的包装器中添加另一个 except
行:except Exception: return False
@Hyflex 是的,正如 Tom 指出的那样,只需删除 AssertionError (在它仅在 AssertionError 上停止并引发其他任何事情之前)...
好的,我修复了标签(没有意识到我必须重新排序 csvdat_old 的标签顺序,我尝试将其合并到我的实际数据中,但即使数据相同时它也会更新输出,有没有办法找到/查看究竟是什么“不同”?
如何获取这些帧中的不同值?【参考方案2】:
这会比较两个数据框的值,注意表之间的行/列数需要相同
comparison_array = table.values == expected_table.values
print (comparison_array)
>>>[[True, True, True]
[True, False, True]]
if False in comparison_array:
print ("Not the same")
#Return the position of the False values
np.where(comparison_array==False)
>>>(array([1]), array([1]))
然后您可以使用此索引信息返回表之间不匹配的值。由于它是零索引的,它指的是第二个位置的第二个数组,这是正确的。
【讨论】:
这里的主要好处是您可以使用 np.where 返回的索引位置来确定表格不匹配的确切位置并提醒用户该位置。【参考方案3】:不确定在发布问题时这是否存在,但 pandas 现在有一个内置函数来测试两个数据帧之间的相等性:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.equals.html。
【讨论】:
不,从未存在过,它的工作原理也一样好。 (更好,因为它是单行而不是 5 行 它在花车上是如何工作的?我希望有一个精度参数。 肯定有比这更好的东西。只是一个错误不是一个令人满意的答案 不确定这是否是大多数人想要的。DataFrame.equals
进行了非常深入的比较。例如.. 我有 2 个值相等的数据帧.. 但事实证明 DataFrames
也有一些参数称为 Axis 1
、IntBlock
和 ObjectBlock
。这些在pd._data
下定义。如果这两个对象之间的这些不相同..它将导致 False
@Nickpick DataFrame.eq
可能很适合定位不相等的元素?【参考方案4】:
检查使用:df_1.equals(df_2) # 返回 True 或 False,详情如下
In [45]: import numpy as np
In [46]: import pandas as pd
In [47]: np.random.seed(5)
In [48]: df_1= pd.DataFrame(np.random.randn(3,3))
In [49]: df_1
Out[49]:
0 1 2
0 0.441227 -0.330870 2.430771
1 -0.252092 0.109610 1.582481
2 -0.909232 -0.591637 0.187603
In [50]: np.random.seed(5)
In [51]: df_2= pd.DataFrame(np.random.randn(3,3))
In [52]: df_2
Out[52]:
0 1 2
0 0.441227 -0.330870 2.430771
1 -0.252092 0.109610 1.582481
2 -0.909232 -0.591637 0.187603
In [53]: df_1.equals(df_2)
Out[53]: True
In [54]: df_3= pd.DataFrame(np.random.randn(3,3))
In [55]: df_3
Out[55]:
0 1 2
0 -0.329870 -1.192765 -0.204877
1 -0.358829 0.603472 -1.664789
2 -0.700179 1.151391 1.857331
In [56]: df_1.equals(df_3)
Out[56]: False
【讨论】:
有一个问题:index.names
没有进行比较。详情见我的回答:***.com/a/43420842/304209【参考方案5】:
更准确的比较应该单独检查索引名称,因为DataFrame.equals
不会对此进行测试。所有其他属性(索引值(单/多索引)、值、列、dtypes)都由它正确检查。
df1 = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']], columns=['num', 'name'])
df1 = df1.set_index('name')
df2 = pd.DataFrame([[1, 'a'], [2, 'b'], [3, 'c']], columns=['num', 'another_name'])
df2 = df2.set_index('another_name')
df1.equals(df2)
True
df1.index.names == df2.index.names
False
注意:使用index.names
而不是index.name
也可以用于多索引数据帧。
【讨论】:
【参考方案6】:不确定这是否有用,但我将这个快速的 python 方法组合在一起,只返回两个具有相同列和形状的数据帧之间的差异。
def get_different_rows(source_df, new_df):
"""Returns just the rows from the new dataframe that differ from the source dataframe"""
merged_df = source_df.merge(new_df, indicator=True, how='outer')
changed_rows_df = merged_df[merged_df['_merge'] == 'right_only']
return changed_rows_df.drop('_merge', axis=1)
【讨论】:
这是我真正在寻找的答案。非常感谢 这太棒了!但我可以问它怎么能不忽略数据类型?例如,如果source_df
上有一个值4
和new_df
上的4.0
,它仍然应该返回一个dataframe
,因为这意味着存在差异,谢谢!
谢谢你。而且实用功能真的好用!【参考方案7】:
就我而言,我遇到了一个奇怪的错误,即使索引、列名
并且值相同,DataFrames
不匹配。我追踪到
数据类型,似乎pandas
有时可以使用不同的数据类型,
导致这样的问题
例如:
param2 = pd.DataFrame('a': [1])
param1 = pd.DataFrame('a': [1], 'b': [2], 'c': [2], 'step': ['alpha'])
如果你检查param1.dtypes
和param2.dtypes
,你会发现'a'是
为param1
输入object
,为param2
输入int64
。现在,如果你这样做
使用param1
和param2
的组合进行一些操作,其他
数据框的参数将偏离默认参数。
因此,在生成最终数据帧之后,即使实际值
打印出来是一样的,final_df1.equals(final_df2)
,结果可能是
不相等,因为像Axis 1
、ObjectBlock
这样的小参数,
IntBlock
可能不一样。
解决此问题并比较值的一种简单方法是使用
final_df1==final_df2
.
但是,这将逐个元素进行比较,所以如果你
正在使用它来断言例如pytest
中的语句。
TL;DR
效果很好的是
all(final_df1 == final_df2)
.
这会逐个元素进行比较,同时忽略不包含的参数 比较重要。
TL;DR2
如果您的值和索引相同,但final_df1.equals(final_df2)
显示False
,您可以使用final_df1._data
和final_df2._data
检查数据框的其余元素。
【讨论】:
有谁知道为什么从 CSV 读取的数据框与从 Excel 工作簿读取的数据框显示不同,即使它们相同?【参考方案8】:拉出对称差异:
df_diff = pd.concat([df1,df2]).drop_duplicates(keep=False)
例如:
df1 = pd.DataFrame(
'num': [1, 4, 3],
'name': ['a', 'b', 'c'],
)
df2 = pd.DataFrame(
'num': [1, 2, 3],
'name': ['a', 'b', 'd'],
)
将产生:
注意:在 pandas 的下一个版本之前,为避免将来如何设置 sort 参数的警告,只需添加 sort=False
参数即可。如下:
df_diff = pd.concat([df1,df2], sort=False).drop_duplicates(keep=False)
【讨论】:
【参考方案9】:希望下面的sn-p代码对你有帮助!
import pandas as pd
import datacompy
df_old_original = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [7, 7, 7, 7], [3, 3, 3, 3], [4, 4, 4, 4], [7, 7, 7, 7], [5, 5, 5, 5], [6, 6, 6, 6]], columns=['A', 'B', 'C', 'D'], index=[0, 1, 2, 3, 4, 5, 6, 7], dtype=object)
df_new_original = pd.DataFrame([[None, None, None, None], [1, 1, 1, 1], [2, 2, 2, 2], [8, 8, 8, 8], [3, 3, 3, 3], [4, 4, 4, 4], [7, 7, 7, 7], [5, 5, 5, 5], [None, None, None, None]], columns=['A', 'B', 'C', 'D'], index=[0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=object)
compare = datacompy.Compare(df_old_original, df_new_original, join_columns=['A', 'B', 'C', 'D'], abs_tol=0, rel_tol=0, df1_name='Old', df2_name='New')
changes_in_old_df = compare.df1_unq_rows
changes_in_new_df = compare.df2_unq_rows
print(changes_in_old_df)
print(changes_in_new_df)
print(Compare.report())
【讨论】:
以上是关于比较两个熊猫数据框的差异的主要内容,如果未能解决你的问题,请参考以下文章