计算 Series start/end 处的零个数
Calculate the number of zeros at start/end of Series
我有这样一个系列:
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
但这对于非常大的系列来说非常慢,尤其是尾随零的计算,我需要一个替代方案。
使用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)
测试和编辑:适用于全零和非零情况。
出于好奇,我检查了另一种简单的 pandas 方法,并针对具有 1.000.000 行的系列测试了所有三个版本。
事实证明,Chris 的版本比原始版本快 18 倍,比我的 pandas 版本快 2 倍。但请注意,我的 pandas 版本假设索引是从 0 开始的连续整数索引(因此 .iloc[i]
将 return 与 .loc[i]
相同),而克里斯的版本独立于索引。
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')
我有这样一个系列:
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
但这对于非常大的系列来说非常慢,尤其是尾随零的计算,我需要一个替代方案。
使用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)
测试和编辑:适用于全零和非零情况。
出于好奇,我检查了另一种简单的 pandas 方法,并针对具有 1.000.000 行的系列测试了所有三个版本。
事实证明,Chris 的版本比原始版本快 18 倍,比我的 pandas 版本快 2 倍。但请注意,我的 pandas 版本假设索引是从 0 开始的连续整数索引(因此 .iloc[i]
将 return 与 .loc[i]
相同),而克里斯的版本独立于索引。
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')