numpy 数组的唯一标识符?

Unique identifier for numpy array?

我的情况是我有一些二维数组 A,我的代码中 classes 中的一个方法使用了它,然后我需要检查一个数组是否通过了不同的方法具有相同的值。

明显的解决方法是将 A 另存为 class 属性,但由于 A 可能变得非常大,我想避免将其添加为属性以避免内存问题。

我想做的是为这个数组保存某种唯一标识符并检查它。我的第一个想法是使用 id(A),但这是对象的唯一标识符,而不是数组,所以如果我有一些 B = A.copy(),它会有不同的 id.

另一个想法是保存 A 的一些稀疏版本,例如,对一些随机索引进行采样并检查等价性,但这似乎比我需要的这样的东西更混乱和深入。

有人有什么建议吗?

使用 hash function 例如SHA-256 通过 hashlib 模块。下面是生成 hash-based ID 的示例。 array_id() 函数 returns 对于固定长度为 64 个符号的数组字符串是唯一的。相同内容的数组会产生相同的id,而即使改变一小部分,它也将是完全不同的id。

请注意,不同类型的数组可能会产生不同的结果,例如如果您有两个具有相同整数值的整数数组,但一个具有类型 np.int32 另一个是 np.int64 那么您将获得不同的 ID,在这种情况下,您只需要将数组更改为一种常见类型,例如做 res_id = array_id(a.astype(np.int64))。但不同的类型并不总是意味着哈希 ID 会不同,例如如果所有整数都是 non-negative 且小于 2^31,则 np.int32np.uint32 类型都将给出相同的哈希值。

因此,如果您希望 hash-IDs 对于相同值的数字相同,则始终将数组类型更改为某些常见类型,例如 array_id(a.astype(common_type)),其中 common_type 可能是例如np.int64 用于所有整数类型,np.float64 用于所有浮点类型。相反,如果您希望不同的类型总是产生不同的结果,则将类型名称包含到散列中,例如 hashlib.sha256(str(a.dtype).encode('ascii') + a.tobytes()).hexdigest().upper().

在接下来的代码中,如果您传递标志 include_dtype = True,那么数据类型将包含在 ID 计算中。如果 include_shape = True 也会包含形状。 algo 参数(sha256xxhash)选择要使用的哈希算法。

代码需要通过命令安装一次模块python -m pip install numpy xxhash

Try it online!

# Needs: python -m pip install numpy xxhash
def array_id(a, *, include_dtype = False, include_shape = False, algo = 'xxhash'):
    data = bytes()
    if include_dtype:
        data += str(a.dtype).encode('ascii')
    data += b','
    if include_shape:
        data += str(a.shape).encode('ascii')
    data += b','
    data += a.tobytes()
    if algo == 'sha256':
        import hashlib
        return hashlib.sha256(data).hexdigest().upper()
    elif algo == 'xxhash':
        import xxhash
        return xxhash.xxh3_64(data).hexdigest().upper()
    else:
        assert False, algo

# Test
import numpy as np, timeit
a = np.array([[1,2,3],[4,5,6]])
print(array_id(a))
print(array_id(a, include_shape = True))
print(array_id(a, include_shape = True, include_dtype = True))

# Speed Measure
a = np.ones((10000, 10000,), dtype = np.uint32)
for algo in ['sha256', 'xxhash']:
    print(algo, round(timeit.timeit(lambda: array_id(a, algo = algo), number = 1), 3), 'sec')

输出:

17A96F5E5826D66A
E201378DF28CB280
0FDFAE47334C986A
sha256 3.774 sec
xxhash 1.356 sec