【问题标题】:Find consecutive repeated nan in a numpy array在 numpy 数组中查找连续重复的 nan
【发布时间】:2017-06-02 23:09:05
【问题描述】:

在 numpy 数组中找到最大连续重复 nan 数的最佳方法是什么?

例子:

from numpy import nan

输入1:[nan, nan, nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, nan, 0.16]

输出 1:3

输入 2:[nan, nan, 2, 1, 1, nan, nan, nan, nan, 0.101, nan, 0.16]

输出 2:4

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    这可以在 NumPy 中非常有效地完成,而无需使用任何循环。

    如果我们调用序列x,那么我们可以找到最大的后续nan 数:

    np.max(np.diff(np.concatenate(([-1], np.where(-np.isnan(x))[0], [len(x)]))) - 1)
    

    【讨论】:

      【解决方案2】:

      性能提升是可能的,尤其是当存在较长的 nan 序列时。 在这些情况下,无需测试所有值。

      使用@MSeifert 方法和符号,如果max_ 长度块中出现任何孔,则可以通过max_ 而不是一个步长扫描数组:

      @nb.njit
      def max_consecutive_nan2(arr):
          max_ = 0
          idx = 0
          while idx < arr.size:
              while idx < arr.size and math.isnan(arr[idx]): # amelioration
                  max_ += 1
                  idx  += 1
              while idx < arr.size - max_:
                  idx2 = idx + max_
                  while idx2>idx and math.isnan(arr[idx2]):
                      idx2 -=1
                  if idx2==idx: # record reached.
                    idx = idx + max_ +1
                    break # goto amelioration
                  idx=idx2 # skip unuseful tests
              else : return max_         
          return max_ #case record at end. 
      

      结果:

      arr = np.random.rand(10000)
      arr[np.random.choice(range(len(arr)),size=4000,replace=0)] = np.nan
      
      In [25]: max_consecutive_nan(arr)
      Out[25]: 14
      
      In [26]: max_consecutive_nan2(arr)
      Out[26]: 14
      

      还有表演:

      In [27]: %timeit max_consecutive_nan2(arr)
      100000 loops, best of 3: 3.29 µs per loop
      
      In [28]: %timeit max_consecutive_nan(arr) # MSeifert
      10000 loops, best of 3: 68.5 µs per loop
      

      【讨论】:

        【解决方案3】:

        另一种易于阅读和理解的方式是使用字符串,然后是str.split

        array2 = [nan, nan, 2, 1, 1, nan, nan, nan, nan, 0.101, nan, 0.16]
        thestring=isnan(array2).tobytes().decode()
        #'\x01\x01\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00'
        m=max(len(c) for c in thestring.split('\x00'))
        # 4
        

        【讨论】:

          【解决方案4】:

          我不知道您是否有 numba,但对于此类特殊问题,它非常方便(而且快速):

          import numba as nb
          import math
          
          @nb.njit   # also works without but then it's several orders of magnitudes slower
          def max_consecutive_nan(arr):
              max_ = 0
              current = 0
              idx = 0
              while idx < arr.size:
                  while idx < arr.size and math.isnan(arr[idx]):
                      current += 1
                      idx += 1
                  if current > max_:
                      max_ = current
                  current = 0
                  idx += 1
              return max_
          

          你的例子:

          >>> from numpy import nan
          >>> max_consecutive_nan(np.array([nan, nan, 2, 1, 1, nan, nan, nan, nan, 0.101, nan, 0.16]))
          4
          
          >>> max_consecutive_nan(np.array([nan, nan, nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, nan, 0.16]))
          3
          
          >>> max_consecutive_nan(np.array([0.16, 0.16, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]))
          22
          

          使用@Divarkar 提出的基准并按性能排序(基准的完整代码可在此gist 中找到):

          arr = np.random.rand(10000)
          arr[np.random.choice(range(len(arr)),size=1000,replace=0)] = np.nan
          %timeit mine(arr)         # 10000 loops, best of 3: 67.7 µs per loop
          %timeit Divakar_v2(arr)   # 1000 loops, best of 3: 196 µs per loop
          %timeit Divakar(arr)      # 1000 loops, best of 3: 252 µs per loop
          %timeit Tagc(arr)         # 100 loops, best of 3: 6.92 ms per loop
          %timeit Kasramvd(arr)     # 10 loops, best of 3: 38.2 ms per loop
          %timeit pltrdy(arr)       # 10 loops, best of 3: 70.9 ms per loop
          

          【讨论】:

          • 哦,我无权访问numba!你能用我的计时测试吗?
          • @Divakar 好的,我已将其包含在内。太糟糕了,只有这么少的人有 numba :(
          • 太棒了!谢谢。是的..我想更多的人应该探索/使用它。
          • 有趣的是,最快的解决方案比最慢的解决方案快 1000 倍以上。
          • 如果我能再打扰你一次 - 我添加了一个更好的版本,我认为它比之前的版本改进了 15%+。所以,如果不是太多的工作,你能把它加到你的时间里吗? :)
          【解决方案5】:

          这是一种方法 -

          def max_repeatedNaNs(a):
              # Mask of NaNs
              mask = np.concatenate(([False],np.isnan(a),[False]))
              if ~mask.any():
                  return 0
              else:
                  # Count of NaNs in each NaN group. Then, get max count as o/p.
                  c = np.flatnonzero(mask[1:] < mask[:-1]) - \
                      np.flatnonzero(mask[1:] > mask[:-1])
                  return c.max()
          

          这是一个改进的版本 -

          def max_repeatedNaNs_v2(a):
              mask = np.concatenate(([False],np.isnan(a),[False]))
              if ~mask.any():
                  return 0
              else:
                  idx = np.nonzero(mask[1:] != mask[:-1])[0]
                  return (idx[1::2] - idx[::2]).max()
          

          响应@pltrdy's comment的基准测试-

          In [77]: a = np.random.rand(10000)
          
          In [78]: a[np.random.choice(range(len(a)),size=1000,replace=0)] = np.nan
          
          In [79]: %timeit contiguous_NaN(a) #@pltrdy's solution
          100 loops, best of 3: 15.8 ms per loop
          
          In [80]: %timeit max_repeatedNaNs(a)
          10000 loops, best of 3: 103 µs per loop
          
          In [81]: %timeit max_repeatedNaNs_v2(a)
          10000 loops, best of 3: 86.4 µs per loop
          

          【讨论】:

          • 如何把琐碎的事情弄得一团糟。
          • @pltrdy 感谢您对此投反对票的评论。此解决方案旨在提高性能。会添加一些运行时测试来证明这一点。
          • @pltrdy 这个人在提到best 时可能会问性能问题。很快就会添加时间。
          • 我完全同意 Divarkar,解决方案可能比它需要的更复杂,我不喜欢任何包含 np.concatenate 的代码,但这对于更大的数组来说非常好 .
          • @Tagc MSeifert 承担了繁重的工作,并在his post 中发布了时间安排。看看这些!
          【解决方案6】:

          这是我的解决方案。
          计算复杂度为O(n)n = len(arr),空间为O(1)

          def contiguous_NaN(arr):
               count, max_count = 0, 0
               for e in arr:
                   if np.isnan(e):
                       count += 1
                       max_count = max(max_count, count)
                   else:
                       count = 0
          
               return max_count
          

          编辑:请记住,您的代码的重点是:

          1. 上班
          2. 使用合理的资源(时间和空间)。
          3. 易于阅读和理解。

          【讨论】:

          • 遍历 numpy 数组显然违反了第 2 点:因为它是处理数组的最低效方法之一。
          • 感谢@pltrdy 的回答,但是当数组的最后一个为'nan'时,此函数返回0。示例:“[0.16, 0.16, nan, nan, nan, nan, nan, nan ,南,南,南,南,南,南,南,南,南,南,南,南,南,南,南,南]" 返回 0
          【解决方案7】:

          我根据itertools发布了另一个答案,但我相信这个更好:

          from itertools import groupby
          
          from numpy import nan
          
          
          def longest_nan_run(sequence):
              return max((sum(1 for _ in group) for key, group in groupby(sequence) if key is nan), default=0)
          
          
          if __name__ == '__main__':
              array1 = [nan, nan, nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, nan, 0.16]
              array2 = [nan, nan, 2, 1, 1, nan, nan, nan, nan, 0.101, nan, 0.16]
          
              print(longest_nan_run(array1))  # 3
              print(longest_nan_run(array2))  # 4
              print(longest_nan_run([]))      # 0
              print(longest_nan_run([1, 2]))  # 0
          

          编辑:现在可以处理不存在 nan 值的情况(感谢 MSeifert 指出)。

          【讨论】:

          • 在性能层面上,我猜@Divakar 的解决方案会更好
          • @pltrdy 可能 - 让我们试试吧 :)
          • @Tagc 当我做基准测试时,我注意到你应该指定一个default 来捕获没有nan 的情况:max((sum(1 for _ in group) for key, group in groupby(sequence) if key is nan), default=0)
          • @MSeifert 谢谢!更新。也感谢您发布时间。
          猜你喜欢
          • 1970-01-01
          • 2014-09-13
          • 2020-04-26
          • 1970-01-01
          • 2017-07-03
          • 2011-11-13
          • 1970-01-01
          • 2013-04-25
          • 1970-01-01
          相关资源
          最近更新 更多