【问题标题】:Calculate the number of zeros at start/end of Series计算系列开始/结束时的零数
【发布时间】:2019-08-13 05:40:41
【问题描述】:

我有一个这样的系列:

s = pd.Series([0, 0, 0, 1, 2, 3])
s
Out[00]: 
0    0
1    0
2    0
3    1
4    2
5    0
dtype: int64

我想计算这个系列中开头和结尾零的数量。所以在这种情况下,我应该得到 3 作为开始,因为在第一个非零数字之前有 3 个零,而 1 用于尾随零,因为在最后一个非零之后的系列尾部有一个零。

到目前为止我做了什么

到目前为止,我的解决方案是使用累积和

sum(s.cumsum() == 0) # begenning
np.sum(np.cumsum(s.values[::-1]) == 0) # trailing

但这对于非常大的系列来说非常慢,尤其是尾随零计算,我需要一个替代方案。

【问题讨论】:

    标签: python pandas series


    【解决方案1】:

    使用numpy.nonzero:

    import numpy as np
    
    n_rows = len(s)
    indices = np.nonzero(s)[0]
    
    if indices.size>0:
        head = indices[0]
        trail = n_rows - indices[-1] -1
    else:
        head, trail = n_rows, n_rows
    print(head, trail)
    

    输出:

    3 1
    

    基准测试(大约快 15 倍):

    s = np.zeros(100000)
    s[30000:50000] +=1
    s = pd.Series(s)
    
    %%timeit
    
    n_rows = len(s)
    indices = np.nonzero(s)[0]
    
    if indices.size>0:
        head = indices[0]
        trail = n_rows - indices[-1] -1
    else:
        head, trail = n_rows, n_rows
    # 661 µs ± 8.63 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    %%timeit
    
    sum(s.cumsum() == 0) # begenning
    np.sum(np.cumsum(s.values[::-1]) == 0) # trailing
    # 9.39 ms ± 163 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    经过测试和编辑:适用于全零和非零情况。

    【讨论】:

    • @jezrael 它确实不适用于全零,因此已更新。慢一点,但仍然快 15 倍 ;)
    【解决方案2】:

    出于好奇,我检查了另一种普通的 pandas 方法,并针对一个有 1.000.000 行的系列测试了所有三个版本。

    事实证明,Chris 的版​​本比原始版本快 18 倍,比我的 pandas 版本快 2 倍。但请注意,我的 pandas 版本假设索引是从 0 开始的连续整数索引(因此 .iloc[i] 将返回与 .loc[i] 相同的值),而 chris 的版​​本独立于索引。

    def test_pandas_version(s):
        truth=(s!=0)
        idxs= truth.index.where(truth, np.NaN)
        #first_one=idxs.min()
        first_one=truth.idxmax()
        last_one= idxs.max()
        whole_len=   truth.shape[0]
        prefix_len=  first_one
        suffix_le=   whole_len - last_one - 1
        if prefix_len == np.NaN:
            prefix_len= whole_len
            suffix_len= 0
        return (prefix_len, suffix_le)
    
    def test_original_version(s):
        suffix_len = np.sum(np.cumsum(s.values[::-1]) == 0) # begenning
        prefix_len= sum(s.cumsum() == 0) 
        return (prefix_len, suffix_le)
    
    def test_np_version(s):
        n_rows = len(s)
        indices = np.nonzero(s)[0]
    
        if indices.size>0:
            head = indices[0]
            trail = n_rows - indices[-1] -1
        else:
            head, trail = n_rows, n_rows
        return (head, trail)
    
    for func in [test_np_version, test_pandas_version, test_original_version]:
        before= datetime.now()
        for i in range(100):
            result= func(s1)
        after= datetime.now()
        time_diff= (after-before).total_seconds()
        print(f'result for {func.__name__} was {result} in {time_diff} seconds')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      相关资源
      最近更新 更多