【问题标题】:Get the same hash value for a Pandas DataFrame each time每次为 Pandas DataFrame 获取相同的哈希值
【发布时间】:2015-10-12 13:55:53
【问题描述】:

我的目标是获取 DataFrame 的唯一哈希值。我从 .csv 文件中获取它。 重点是每次我调用 hash() 时都得到相同的哈希值。

我的想法是创建函数

def _get_array_hash(arr):
    arr_hashable = arr.values
    arr_hashable.flags.writeable = False
    hash_ = hash(arr_hashable.data)
    return hash_

调用底层 numpy 数组,将其设置为不可变状态并获取缓冲区的哈希值。

内联更新。

自 08.11.2016 起,此版本的功能不再起作用。相反,您应该使用

hash(df.values.tobytes())

Most efficient property to hash for numpy array 见 cmets。

内联 UPD 结束。

它适用于常规 pandas 数组:

In [12]: data = pd.DataFrame({'A': [0], 'B': [1]})

In [13]: _get_array_hash(data)
Out[13]: -5522125492475424165

In [14]: _get_array_hash(data)
Out[14]: -5522125492475424165 

然后我尝试将其应用于从 .csv 文件中获得的 DataFrame:

In [15]: fpath = 'foo/bar.csv'

In [16]: data_from_file = pd.read_csv(fpath)

In [17]: _get_array_hash(data_from_file)
Out[17]: 6997017925422497085

In [18]: _get_array_hash(data_from_file)
Out[18]: -7524466731745902730

谁能解释一下,这怎么可能?

我可以用它创建新的DataFrame,比如

new_data = pd.DataFrame(data=data_from_file.values, 
            columns=data_from_file.columns, 
            index=data_from_file.index)

它又可以工作了

In [25]: _get_array_hash(new_data)
Out[25]: -3546154109803008241

In [26]: _get_array_hash(new_data)
Out[26]: -3546154109803008241

但我的目标是在应用程序启动时为数据帧保留相同的哈希值,以便从缓存中检索一些值。

【问题讨论】:

  • 这可能会有所帮助:github.com/TomAugspurger/engarde/issues/3
  • 我尝试了获取索引和列的哈希值以及 str(data_frame) 值的方法。它很慢,并且遇到同样的问题。
  • 我也有兴趣这样做 - 我能问一下你为什么包含“arr_hashable.flags.writeable = False”吗?您是否希望 hash() 函数可能会修改数组?
  • @MaxPower 这是很久以前的事了,所以我不记得了。但我想我受到了stackoverflow.com/questions/16589791/… 的启发。那时我已经工作了。现在它不起作用了,但是您可以使用hash(a.data.tobytes()) 代替,并且您不再需要flags.writeable = False。请参阅参考答案的 cmets。
  • 实际上,你甚至不需要.data,如果从DataFrame调用,只需使用hash(a.tobytes())hash(df.values.tobytes())。我已经更新了原来的问题。

标签: python pandas


【解决方案1】:

从 Pandas 0.20.1 开始,您可以使用最近在 pandas.util 中的 made public 鲜为人知(且记录不充分)的 hash_pandas_object (source code)。它为数据帧的到达行返回一个哈希值(也适用于系列等)

import pandas as pd
import numpy as np

np.random.seed(42)
arr = np.random.choice(['foo', 'bar', 42], size=(3,4))
df = pd.DataFrame(arr)

print(df)
#      0    1   2    3
# 0   42  foo  42   42
# 1  foo  foo  42  bar
# 2   42   42  42   42

from pandas.util import hash_pandas_object
h = hash_pandas_object(df)

print(h)
# 0     5559921529589760079
# 1    16825627446701693880
# 2     7171023939017372657
# dtype: uint64

如果您想要所有行的整体哈希,您可以随时使用hash_pandas_object(df).sum()

【讨论】:

  • 不是 100%,但很可能hashlib.sha256(pd.util.hash_pandas_object(df, index=True).values).hexdigest() 会比.sum() 更少冲突。
  • @mathtick 确实如此,否则重新排序行会得到相同的哈希值。
  • hash_pandas_object 的问题在于它不可序列化,由于循环依赖,请参见此处:github.com/pandas-dev/pandas/issues/35097 和此处:github.com/uqfoundation/dill/issues/374
  • 如果列名不同,它们会返回不同的值吗?
【解决方案2】:

我遇到了类似的问题:检查数据帧是否已更改,我通过散列 msgpack 序列化字符串来解决它。这在不同的重新加载相同数据之间似乎是稳定的。

import pandas as pd
import hashlib
DATA_FILE = 'data.json'

data1 = pd.read_json(DATA_FILE)
data2 = pd.read_json(DATA_FILE)

assert hashlib.md5(data1.to_msgpack()).hexdigest() == hashlib.md5(data2.to_msgpack()).hexdigest()
assert hashlib.md5(data1.values.tobytes()).hexdigest() != hashlib.md5(data2.values.tobytes()).hexdigest()

【讨论】:

  • 我发现.to_msgpack() 在 Python 3.6 中稳定,但在 3.5 中不稳定(不知道为什么,可能与 Python 3.6+ 中的字典排序有关)。保持简单,改为.to_csv().encode('utf-8')
  • 牢记 ostrokach 的(上述)评论:此解决方案具有处理不可散列数据帧元素的开箱即用优势(与 pd.util.hash_pandas_object 相比)。
  • 我认为 pandas 1.0,df.to_msgpack() 已被弃用。 pandas 文档中推荐的 .to_msgpack() 替代品是 pyarrow,但这会打开一个全新的蠕虫罐
  • 另外,data1.values.tobytes() 可能会返回数字数据帧内容的确定性值,但如果您的数据帧中有字符串值,您将获得针对不同 python 会话的不同字节串。虽然可能在同一个 python 会话中匹配
  • 不幸的是,.values 无法捕获 DataFrame 的唯一性,print(hashlib.md5(pd.DataFrame({'X': []}).values.tobytes()).hexdigest())print(hashlib.md5(pd.DataFrame({'Y': []}).values.tobytes()).hexdigest()) 产生相同的哈希值。
【解决方案3】:

Joblib 为包含 numpy 数组的对象(例如 pandas 数据帧)提供了优化的散列函数。

import joblib
joblib.hash(df)

【讨论】:

  • 这对我不起作用! (df1 == df2).all() 为 True,但哈希值不同。
  • @JulianWgs 你有例子吗?为了比较两个系列,如果它们的名称字段不同,它们的哈希值最终会不同,而它们的值相同,但我无法为任何 DataFrame 复制它。
【解决方案4】:

这个功能似乎工作正常:

from hashlib import sha256
def hash_df(df):
    s = str(df.columns) + str(df.index) + str(df.values)
    return sha256(s.encode()).hexdigest()

【讨论】:

猜你喜欢
  • 2023-02-19
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
  • 2020-07-18
  • 2021-09-21
  • 2012-01-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多