【发布时间】: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
我有以下熊猫系列(表示为列表):
[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
复杂度为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])
【讨论】:
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 为这些东西获得类似 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 装饰函数来进一步加快速度。
【讨论】:
一个可能不那么高效(还没有真正检查过)但在步骤方面更容易理解(至少对我来说)的解决方案是:
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
【讨论】:
另一种选择
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))
【讨论】: