【问题标题】:How to count distance to the previous zero in pandas series?如何计算熊猫系列中前一个零的距离?
【发布时间】:2015-08-24 05:00:11
【问题描述】:

我有以下熊猫系列(表示为列表):

[7,2,0,3,4,2,5,0,3,4]

我想定义一个新系列,它将距离返回到最后一个零。这意味着我希望得到以下输出:

[1,2,0,1,2,3,4,0,1,2]

如何在 pandas 中以最有效的方式做到这一点?

【问题讨论】:

    标签: python numpy pandas series


    【解决方案1】:

    复杂度为O(n)。会减慢它的是在 python 中执行for 循环。如果系列中有 k 零,并且 log k 与系列的长度相比可以忽略不计,则 O(n log k) 解决方案将是:

    >>> izero = np.r_[-1, (ts == 0).nonzero()[0]]  # indices of zeros
    >>> idx = np.arange(len(ts))
    >>> idx - izero[np.searchsorted(izero - 1, idx) - 1]
    array([1, 2, 0, 1, 2, 3, 4, 0, 1, 2])
    

    【讨论】:

      【解决方案2】:

      Pandas 中的解决方案有点棘手,但可能如下所示(s 是您的系列):

      >>> x = (s != 0).cumsum()
      >>> y = x != x.shift()
      >>> y.groupby((y != y.shift()).cumsum()).cumsum()
      0    1
      1    2
      2    0
      3    1
      4    2
      5    3
      6    4
      7    0
      8    1
      9    2
      dtype: int64
      

      对于最后一步,这将使用 Pandas 食谱here 中的“itertools.groupby”配方。

      【讨论】:

      • 我很佩服它的优雅,但是这会执行大量的传递 + groupby,这在单传递 Cython 扩展中是那么微不足道的事情。
      • 我同意 - 如果性能很重要,这种类型的事情最好在 Cython 中实现。可以在 Pandas 中执行(如食谱所示),如果 Cython 不可用,这很方便。
      【解决方案3】:

      看到使用Cython 为这些东西获得类似 c 的速度是多么简单,有时会令人惊讶。假设您的专栏的.values 给出arr,那么:

      cdef int[:, :, :] arr_view = arr
      ret = np.zeros_like(arr)
      cdef int[:, :, :] ret_view = ret
      
      cdef int i, zero_count = 0
      for i in range(len(ret)):
          zero_count = 0 if arr_view[i] == 0 else zero_count + 1
          ret_view[i] = zero_count
      

      注意typed memory views 的使用,它非常快。您可以使用 @cython.boundscheck(False) 使用 this 装饰函数来进一步加快速度。

      【讨论】:

        【解决方案4】:

        一个可能不那么高效(还没有真正检查过)但在步骤方面更容易理解(至少对我来说)的解决方案是:

        
        df = pd.DataFrame({'X': [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]})
        df
        
        df['flag'] = np.where(df['X'] == 0, 0, 1)
        df['cumsum'] = df['flag'].cumsum()
        df['offset'] = df['cumsum']
        df.loc[df.flag==1, 'offset'] = np.nan
        df['offset'] = df['offset'].fillna(method='ffill').fillna(0).astype(int)
        df['final'] = df['cumsum'] - df['offset']
        
        df
        

        【讨论】:

          【解决方案5】:

          另一种选择

          df = pd.DataFrame({'X': [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]})
          zeros = np.r_[-1, np.where(df.X == 0)[0]]
          
          def d0(a):
              return np.min(a[a>=0])
              
          df.index.to_series().apply(lambda i: d0(i - zeros))
          

          或者使用纯 numpy

          df = pd.DataFrame({'X': [7, 2, 0, 3, 4, 2, 5, 0, 3, 4]})
          a = np.arange(len(df))[:, None] - np.r_[-1 , np.where(df.X == 0)[0]][None]
          
          np.min(a, where=a>=0, axis=1, initial=len(df))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-08-30
            • 2020-06-06
            • 2020-04-29
            • 1970-01-01
            • 1970-01-01
            • 2018-09-04
            • 1970-01-01
            • 2019-02-12
            相关资源
            最近更新 更多