Skip to main content
 首页 » 编程设计

python之自上次出现在 Pandas DataFrame 以来有几天了

2025年05月04日8txw1958

假设我有一个 Pandas DataFrame df:

Date      Value 
01/01/17  0 
01/02/17  0 
01/03/17  1 
01/04/17  0 
01/05/17  0 
01/06/17  0 
01/07/17  1 
01/08/17  0 
01/09/17  0 

对于每一行,我想有效地计算自上次出现 Value=1 以来的天数。

所以df:

Date      Value    Last_Occurence 
01/01/17  0        NaN 
01/02/17  0        NaN 
01/03/17  1        0 
01/04/17  0        1 
01/05/17  0        2 
01/06/17  0        3 
01/07/17  1        0 
01/08/17  0        1 
01/09/17  0        2 

我可以做一个循环:

for i in range(0, len(df)): 
    last = np.where(df.loc[0:i,'Value']==1) 
    df.loc[i, 'Last_Occurence'] = i-last 

但对于非常大的数据集来说,它似乎效率很低,而且可能也不对。

请您参考如下方法:

这是一个 NumPy 方法 -

def intervaled_cumsum(a, trigger_val=1, start_val = 0, invalid_specifier=-1): 
    out = np.ones(a.size,dtype=int)     
    idx = np.flatnonzero(a==trigger_val) 
    if len(idx)==0: 
        return np.full(a.size,invalid_specifier) 
    else: 
        out[idx[0]] = -idx[0] + 1 
        out[0] = start_val 
        out[idx[1:]] = idx[:-1] - idx[1:] + 1 
        np.cumsum(out, out=out) 
        out[:idx[0]] = invalid_specifier 
        return out 

在数组数据上运行的示例很少,以展示涵盖触发器和起始值的各种场景的用法:

In [120]: a 
Out[120]: array([0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0]) 
 
In [121]: p1 = intervaled_cumsum(a, trigger_val=1, start_val=0) 
     ...: p2 = intervaled_cumsum(a, trigger_val=1, start_val=1) 
     ...: p3 = intervaled_cumsum(a, trigger_val=0, start_val=0) 
     ...: p4 = intervaled_cumsum(a, trigger_val=0, start_val=1) 
     ...:  
 
In [122]: np.vstack(( a, p1, p2, p3, p4 )) 
Out[122]:  
array([[ 0,  1,  1,  1,  0,  0,  1,  0,  0,  1,  1,  1,  1,  1,  0], 
       [-1,  0,  0,  0,  1,  2,  0,  1,  2,  0,  0,  0,  0,  0,  1], 
       [-1,  1,  1,  1,  2,  3,  1,  2,  3,  1,  1,  1,  1,  1,  2], 
       [ 0,  1,  2,  3,  0,  0,  1,  0,  0,  1,  2,  3,  4,  5,  0], 
       [ 1,  2,  3,  4,  1,  1,  2,  1,  1,  2,  3,  4,  5,  6,  1]]) 

用它来解决我们的问题:

df['Last_Occurence'] = intervaled_cumsum(df.Value.values) 

示例输出-

In [181]: df 
Out[181]:  
       Date  Value  Last_Occurence 
0  01/01/17      0              -1 
1  01/02/17      0              -1 
2  01/03/17      1               0 
3  01/04/17      0               1 
4  01/05/17      0               2 
5  01/06/17      0               3 
6  01/07/17      1               0 
7  01/08/17      0               1 
8  01/09/17      0               2 

运行时测试

方法-

# @Scott Boston's soln 
def pandas_groupby(df): 
    mask = df.Value.cumsum().replace(0,False).astype(bool) 
    return df.assign(Last_Occurance=df.groupby(df.Value.astype(bool).\ 
                                    cumsum()).cumcount().where(mask)) 
 
# Proposed in this post 
def numpy_based(df): 
    df['Last_Occurence'] = intervaled_cumsum(df.Value.values) 

时间 -

In [33]: df = pd.DataFrame((np.random.rand(10000000)>0.7).astype(int), columns=[['Value']]) 
 
In [34]: %timeit pandas_groupby(df) 
1 loops, best of 3: 1.06 s per loop 
 
In [35]: %timeit numpy_based(df) 
10 loops, best of 3: 103 ms per loop 
 
In [36]: df = pd.DataFrame((np.random.rand(100000000)>0.7).astype(int), columns=[['Value']]) 
 
In [37]: %timeit pandas_groupby(df) 
1 loops, best of 3: 11.1 s per loop 
 
In [38]: %timeit numpy_based(df) 
1 loops, best of 3: 1.03 s per loop