【问题标题】:Comparing NumPy arrays so that NaNs compare equal比较 NumPy 数组以便 NaN 比较相等
【发布时间】:2012-06-04 21:22:34
【问题描述】:

有没有一种惯用的方法来比较两个将 NaN 视为彼此相等的 NumPy 数组(但不等于除 NaN 之外的任何东西other)。

例如,我希望以下两个数组比较相等:

np.array([1.0, np.NAN, 2.0])
np.array([1.0, np.NAN, 2.0])

和下面两个数组比较不相等:

np.array([1.0, np.NAN, 2.0])
np.array([1.0, 0.0, 2.0])

我正在寻找一种可以产生标量布尔结果的方法。

以下方法可以做到:

np.all((a == b) | (np.isnan(a) & np.isnan(b)))

但它很笨重并且会创建所有这些中间数组。

有没有一种方法可以让眼睛更轻松并更好地利用内存?

附:如果有帮助,已知数组具有相同的形状和 dtype。

【问题讨论】:

  • @DanielRoseman:我明白这一点。我有两种生成 NumPy 数组的方法,我需要知道它们是否生成了相同的数组。
  • 你已经排除了this question的一个答案;你是否也排除了其他两个?
  • 如果您正在使用当前的 git 提示来进行 numpy,则有一个 numpy.isclose function 接受 equal_nan 关键字参数(为了兼容性,默认为 False)。不过,它对内存不是很友好。
  • 如果不是比较相等但具有不同二进制表示(例如,0.0 和 -0.0)的数字,那么 memoryview(a0) == memoryview(a1) 会做到这一点..
  • @DSM:谢谢。它实际上可能适合我的用例。你介意把它写下来作为答案吗?

标签: python numpy comparison nan


【解决方案1】:

如果您真的关心内存使用(例如,有非常大的数组),那么您应该使用 numexpr 并且以下表达式将适合您:

np.all(numexpr.evaluate('(a==b)|((a!=a)&(b!=b))'))

我已经在长度为 3e8 的非常大的数组上进行了测试,代码在我的机器上的性能与

np.all(a==b)

并使用相同数量的内存

【讨论】:

    【解决方案2】:

    Numpy 1.10 将 equal_nan 关键字添加到 np.allclose (https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html)。

    所以你现在可以做:

    In [24]: np.allclose(np.array([1.0, np.NAN, 2.0]), 
                         np.array([1.0, np.NAN, 2.0]), equal_nan=True)
    Out[24]: True
    

    【讨论】:

    • 顺便说一句,这不适用于字符串。将数组与字符串进行比较会抛出:TypeError("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''")
    【解决方案3】:

    免责声明:我不建议经常使用它,我自己也不会使用它,但我可以想象在极少数情况下它可能有用。

    如果数组具有相同的形状和dtype,您可以考虑使用低级memoryview

    >>> import numpy as np
    >>> 
    >>> a0 = np.array([1.0, np.NAN, 2.0])
    >>> ac = a0 * (1+0j)
    >>> b0 = np.array([1.0, np.NAN, 2.0])
    >>> b1 = np.array([1.0, np.NAN, 2.0, np.NAN])
    >>> c0 = np.array([1.0, 0.0, 2.0])
    >>> 
    >>> memoryview(a0)
    <memory at 0x85ba1bc>
    >>> memoryview(a0) == memoryview(a0)
    True
    >>> memoryview(a0) == memoryview(ac) # equal but different dtype
    False
    >>> memoryview(a0) == memoryview(b0) # hooray!
    True
    >>> memoryview(a0) == memoryview(b1)
    False
    >>> memoryview(a0) == memoryview(c0)
    False
    

    但要小心这样的微妙问题:

    >>> zp = np.array([0.0])
    >>> zm = -1*zp
    >>> zp
    array([ 0.])
    >>> zm
    array([-0.])
    >>> zp == zm
    array([ True], dtype=bool)
    >>> memoryview(zp) == memoryview(zm)
    False
    

    这是因为二进制表示不同,即使它们比较相等(当然,它们必须这样做:这就是它知道打印负号的方式)

    >>> memoryview(zp)[0]
    '\x00\x00\x00\x00\x00\x00\x00\x00'
    >>> memoryview(zm)[0]
    '\x00\x00\x00\x00\x00\x00\x00\x80'
    

    从好的方面来说,它会以您希望的方式短路:

    In [47]: a0 = np.arange(10**7)*1.0
    In [48]: a0[-1] = np.NAN    
    In [49]: b0 = np.arange(10**7)*1.0    
    In [50]: b0[-1] = np.NAN     
    In [51]: timeit memoryview(a0) == memoryview(b0)
    10 loops, best of 3: 31.7 ms per loop
    In [52]: c0 = np.arange(10**7)*1.0    
    In [53]: c0[0] = np.NAN   
    In [54]: d0 = np.arange(10**7)*1.0    
    In [55]: d0[0] = 0.0    
    In [56]: timeit memoryview(c0) == memoryview(d0)
    100000 loops, best of 3: 2.51 us per loop
    

    为了比较:

    In [57]: timeit np.all((a0 == b0) | (np.isnan(a0) & np.isnan(b0)))
    1 loops, best of 3: 296 ms per loop
    In [58]: timeit np.all((c0 == d0) | (np.isnan(c0) & np.isnan(d0)))
    1 loops, best of 3: 284 ms per loop
    

    【讨论】:

    • (+1) 太好了,感谢您抽出宝贵时间来写这篇文章。
    • @aix:过去我实际上需要类似的东西(equal-considering-nans-equal),尽管性能和内存不是问题,所以我手动完成了。可能值得提出功能请求。
    【解决方案4】:

    不确定这是否更好,但一个想法......

    import numpy
    class FloatOrNaN(numpy.float_):
        def __eq__(self, other):
            return (numpy.isnan(self) and numpy.isnan(other)) or super(FloatOrNaN,self).__eq__(other)
    
    a = [1., np.nan, 2.]
    one = numpy.array([FloatOrNaN(val) for val in a], dtype=object)
    two = numpy.array([FloatOrNaN(val) for val in a], dtype=object)
    print one == two   # yields  array([ True,  True,  True], dtype=bool)
    

    这将丑陋的东西推到 dtype 中,以牺牲内部工作 python 而不是 c 为代价(Cython/etc 会解决这个问题)。但是,它确实大大降低了内存成本。

    还是有点丑:(

    【讨论】:

      猜你喜欢
      • 2021-06-04
      • 2021-06-20
      • 2019-02-25
      • 2014-10-10
      • 2019-02-05
      • 2015-01-28
      • 2018-12-03
      相关资源
      最近更新 更多