比较字典与不可用或不可比的值? (例如列表或数据帧)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了比较字典与不可用或不可比的值? (例如列表或数据帧)相关的知识,希望对你有一定的参考价值。
TL; DR:你如何比较两个python词典,如果它们中的一些具有不可变/可变的值(例如列表或pandas Dataframes)?
我必须比较字典对的相等性。从这个意义上讲,这个问题与这两个问题类似,但他们的解决方案似乎只适用于不可变对象......
我的问题是,我正在处理成对的高度嵌套的词典,其中根据我正在比较的哪一对词典,可以在不同的地方找到不可用的对象。我的想法是,我需要迭代字典中包含的最便宜的值,而不能只依赖于只展开最高键值对的dict.iteritems()
。我不确定如何迭代字典中包含的所有可能的键值对,并使用sets / ==为可散列对象进行比较,在pandas数据帧的情况下,运行df1.equals(df2).
(注意pandas数据帧,只是运行) df1==df2
进行了分段比较,NA的处理效果很差.df1.equals(df2)
可以解决问题。)
例如:
a = {'x': 1, 'y': {'z': "George", 'w': df1}}
b = {'x': 1, 'y': {'z': "George", 'w': df1}}
c = {'x': 1, 'y': {'z': "George", 'w': df2}}
至少,这将是非常棒的,解决方案将产生TRUE / FALSE,以确定它们的值是否相同并且适用于pandas数据帧。
def dict_compare(d1, d2):
if ...
return True
elif ...
return False
dict_compare(a,b)
>>> True
dict_compare(a,c)
>>> False
中等更好:解决方案会指出字典中的键/值会有什么不同。
在理想情况下:解决方案可以将值分为4个分组:
- 添加,
- 去除,
- 改性
- 相同
好吧,有一种方法可以使任何类型都具有可比性:只需将它包装在一个比较你需要它的类中:
class DataFrameWrapper():
def __init__(self, df):
self.df = df
def __eq__(self, other):
return self.df.equals(other.df)
因此,当您包装“无法比较”的值时,您现在可以简单地使用==
:
>>> import pandas as pd
>>> df1 = pd.DataFrame({'a': [1,2,3]})
>>> df2 = pd.DataFrame({'a': [3,2,1]})
>>> a = {'x': 1, 'y': {'z': "George", 'w': DataFrameWrapper(df1)}}
>>> b = {'x': 1, 'y': {'z': "George", 'w': DataFrameWrapper(df1)}}
>>> c = {'x': 1, 'y': {'z': "George", 'w': DataFrameWrapper(df2)}}
>>> a == b
True
>>> a == c
False
当然,包装你的价值观有它的缺点,但如果你只需要比较它们,这将是一个非常简单的方法。所有可能需要的是在进行比较之前的递归包装和之后的递归展开:
def recursivewrap(dict_):
for key, value in dict_.items():
wrapper = wrappers.get(type(value), lambda x: x) # for other types don't wrap
dict_[key] = wrapper(value)
return dict_ # return dict_ so this function can be used for recursion
def recursiveunwrap(dict_):
for key, value in dict_.items():
unwrapper = unwrappers.get(type(value), lambda x: x)
dict_[key] = unwrapper(value)
return dict_
wrappers = {pd.DataFrame: DataFrameWrapper,
dict: recursivewrap}
unwrappers = {DataFrameWrapper: lambda x: x.df,
dict: recursiveunwrap}
样例:
>>> recursivewrap(a)
{'x': 1,
'y': {'w': <__main__.DataFrameWrapper at 0x2affddcc048>, 'z': 'George'}}
>>> recursiveunwrap(recursivewrap(a))
{'x': 1, 'y': {'w': a
0 1
1 2
2 3, 'z': 'George'}}
如果你觉得真的很冒险,你可以使用包装类,根据比较结果修改一些保存信息不相等的变量。
这部分答案是基于原始问题,不包括嵌套:
您可以从可散列值中分离不可消除的值,并对可散列值进行集合比较,并对不可合并的值进行“与顺序无关”的列表比较:
def split_hashable_unhashable(vals):
"""Seperate hashable values from unhashable ones and returns a set (hashables)
and list (unhashable ones)"""
set_ = set()
list_ = []
for val in vals:
try:
set_.add(val)
except TypeError: # unhashable
list_.append(val)
return set_, list_
def compare_lists_arbitary_order(l1, l2, cmp=pd.DataFrame.equals):
"""Compare two lists using a custom comparison function, the order of the
elements is ignored."""
# need to have equal lengths otherwise they can't be equal
if len(l1) != len(l2):
return False
remaining_indices = set(range(len(l2)))
for item in l1:
for cmpidx in remaining_indices:
if cmp(item, l2[cmpidx]):
remaining_indices.remove(cmpidx)
break
else:
# Run through the loop without finding a match
return False
return True
def dict_compare(d1, d2):
if set(d1) != set(d2): # compare the dictionary keys
return False
set1, list1 = split_hashable_unhashable(d1.values())
set2, list2 = split_hashable_unhashable(d2.values())
if set1 != set2: # set comparison is easy
return False
return compare_lists_arbitary_order(list1, list2)
它比预期的要长一点。对于您的测试用例,它肯定有效:
>>> import pandas as pd
>>> df1 = pd.DataFrame({'a': [1,2,3]})
>>> df2 = pd.DataFrame({'a': [3,2,1]})
>>> a = {'x': 1, 'y': df1}
>>> b = {'y': 1, 'x': df1}
>>> c = {'y': 1, 'x': df2}
>>> dict_compare(a, b)
True
>>> dict_compare(a, c)
False
>>> dict_compare(b, c)
False
set
操作也可用于发现差异(参见set.difference
)。 list
s有点复杂,但并非真的不可能。可以将未找到匹配项的项添加到单独的列表中,而不是立即返回False
。
Deepdiff库提供了扩展两个python词典的广泛能力
https://github.com/seperman/deepdiff
DeepDiff:字典,迭代,字符串和其他对象的深层差异。它将以递归方式查找所有更改。
pip安装deepdiff
以上是关于比较字典与不可用或不可比的值? (例如列表或数据帧)的主要内容,如果未能解决你的问题,请参考以下文章