比较 2 个包含不同类型和 NaN 值的结构化数组

Posted

技术标签:

【中文标题】比较 2 个包含不同类型和 NaN 值的结构化数组【英文标题】:Comparing 2 Structured Arrays that contain values of different types and NaNs 【发布时间】:2021-06-16 08:37:44 【问题描述】:

所以我有 2 个结构化的 Numpy 数组:

a = numpy.array([('2020-01-04', 'Test', 1, 1.0), 
                 ('2020-01-05', 'Test2', 2, NaN)], 
                  dtype=[('Date', 'M8[D]'), ('Name', 'S8'), ('idx', 'i8'), ('value', 'f8')])
b = numpy.array([('2020-01-04', 'Test', 2, 1.0), 
                 ('2020-01-05', 'Test2', 2, NaN)], 
                dtype=[('Date', 'M8[D]'), ('Name', 'S8'), ('idx', 'i8'), ('value', 'f8')])

我需要比较这 2 个数组并获得一个包含 True/False 值的数组,这些值将指示数组中的哪些索引不同。

做类似的事情:

not_same = np.full(shape=a.shape, dtype=bool, fill_value=False)
for field in a.dtype.names:
     not_same = np.logical_or(not_same,
                              a[field] != b[field])

工作到一定程度,但NaN != NaN 的比较实际上是True,所以我需要使用np.allclose 之类的东西,但只有当你比较的值是浮点数时你才能这样做(字符串爆炸)。

所以我需要以下两件事之一:

    确定a[field] 中的值是否为浮点数

    一种比较 2 个数组的方法,可以比较 2 个 NaN 值,结果为真

以下关于错误的请求:

dt = np.dtype([('string', 'S10'), ('val', 'f8')])
arr = np.array([('test', 1.0)], dtype=dt)
np.isreal(arr['string'])

使用 Python 3.8.5 在 Ubuntu 20.04 上运行

【问题讨论】:

这是您可以使用的解决方案.... ***.com/questions/10710328/… 所以你想要NaN == NaN?好吧,根据 NaN 的定义,这不是真的。 我给你的链接(上面)是我能得到的最接近 Nan == Nan = True 解决方案的链接。通过那个。我认为我们可以结束这个问题,因为这已经被问及回答了 @JoeFerndz 这可以生成单个值而不是一组值。此外,我希望我们能走得更远一点,然后进行例外比较。在问我之前我已经看到了这个问题 @a_guest 是的。 np.allclose 可以选择让比较返回 True 但要调用它,您需要知道数组中值的类型。 【参考方案1】:

这是我能够用来解决此问题的解决方法。这并不漂亮,但会检查所有元素,比较并提供答案。如果 np.Nan == np.Nan,您可以扩展它以找到将答案更改为 True 的方法。

import numpy as np
a = np.array([('2020-01-04', 'Test', 1, 1.0), 
                 ('2020-01-05', 'Test2', 2, np.NaN)], 
                  dtype=[('Date', 'M8[D]'), ('Name', 'S8'), ('idx', 'i8'), ('value', 'f8')])
b = np.array([('2020-01-04', 'Test', 2, 1.0), 
                 ('2020-01-05', 'Test2', 2, np.NaN)], 
                dtype=[('Date', 'M8[D]'), ('Name', 'S8'), ('idx', 'i8'), ('value', 'f8')])
idx_ab = []
for i,j in zip(a,b):
    for x,y in zip(i,j):
        if (isinstance(x, float) and np.isnan(x)) or (isinstance(y, float) and np.isnan(y)):   
            idx_ab.append(False)
        elif x == y:
            idx_ab.append(True)
        else:
            idx_ab.append(False)

print (idx_ab)

输出将是:

[True, True, False, True, True, True, True, False]

不幸的是,您不能只检查 np.isnan(x,y)。如果要检查,x 和 y 都必须是浮动的。如果它是一个字符串,它会给你一个错误。所以在检查nan之前需要先检查isinstance。

另一种方法是使用我共享链接的 np.isclose() 或 np.allclose() 选项:

comparing numpy arrays containing NaN

如果要分别检查a和b中的每个元素,可以给出:

idx_ab = []
for i,j in zip(a,b):
    ab = []
    for x,y in zip(i,j):
        if (isinstance(x, float) and np.isnan(x)) or (isinstance(y, float) and np.isnan(y)):   
            ab.append(False)
        elif x == y:
            ab.append(True)
        else:
            ab.append(False)
    idx_ab.append(ab)
print (idx_ab)

这个输出将是:

[[True, True, False, True], [True, True, True, False]]

如果您希望 np.NaN == np.NaN 的结果为 True,请将其添加为第一个条件,然后是其余条件:

if (isinstance(x, float) and isinstance(y, float) and all(np.isnan([x,y]))): ab.append(True)

这将导致上述答案为:

[[True, True, False, True], [True, True, True, True]]

最后一个值设置为 True,因为 a[1][3]np.NaNb[1][3]np.NaN

【讨论】:

arr2 = np.array([1, 2.1, 3.3, np.nan, 2]) arr1 = np.array([1, 2.0, 3.3, np.nan, 3]) arr1 == arr2 array([ True, False, True, False, False]) 这里有些不对劲:nan == nan 应该是错误的:***.com/questions/20320022/… 同意。我试过很多次。它显示 False 没有几个值。当我向数组添加更多值时,它显示为 True。奇怪的行为。在我的另一台 Mac 上检查它是否有不同的行为 查看我的新回复。希望它能解决问题。然而,这不是优雅的方式。【参考方案2】:

试试isclose(或allclose),然后发现错误。我在下面看到的错误发生在 isclose 代码的早期,因此不会有太多的时间损失。

In [129]: for field in a.dtype.names:
     ...:     print(field, a[field], b[field])
     ...:     try:
     ...:         print("1st", np.isclose(a[field],b[field],equal_nan=True))
     ...:     except TypeError as f:
     ...:         print(f)
     ...:         print("2nd",a[field]==b[field])
     ...: 
     ...: 
Date ['2020-01-04' '2020-01-05'] ['2020-01-04' '2020-01-05']
The DTypes <class 'numpy.dtype[float16]'> and <class 'numpy.dtype[datetime64]'> do not have a common DType. For example they cannot be stored in a single array unless the dtype is `object`.
2nd [ True  True]
Name [b'Test' b'Test2'] [b'Test' b'Test2']
ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
2nd [ True  True]
idx [1 2] [2 2]
1st [False  True]
value [ 1. nan] [ 1. nan]
1st [ True  True]

【讨论】:

以上是关于比较 2 个包含不同类型和 NaN 值的结构化数组的主要内容,如果未能解决你的问题,请参考以下文章

Set以及数组去重

如何获取numpy数组中所有NaN值的索引列表?

Groovy 比较两个带有未知节点名称和值的 json

如何比较忽略nans的numpy数组? [复制]

用最简洁的实现数组去重

计算二维数组每行中非 NaN 值的数量