据我所知,没有't an intuitive approach here that doesn't涉及显式迭代,这对于 numpy 和 pandas 来说并不理想 . 但是,这个问题的时间复杂度是O(n),这使得它成为 numba 库的一个很好的目标 . 这使我们能够提出一个非常有效的解决方案 .
关于我的解决方案的一个注意事项,我使用 (a + threshold // 2) // threshold * threshold ,与使用 np.round(a, decimals=-2) 相比看起来冗长 . 这是由于使用 numba 的 nopython=True ,flag的性质,它与 np.round 函数不兼容 .
from numba import jit
@jit(nopython=True)
def cumsum_with_threshold(arr, threshold):
"""
Rounds values in an array, propogating the last value seen until
a cumulative sum reaches a threshold
:param arr: the array to round and sum
:param threshold: the point at which to stop propogation
:return: rounded output array
"""
s = a.shape[0]
o = np.empty(s)
d = a[0]
r = (a + threshold // 2) // threshold * threshold
c = 0
o[0] = r[0]
for i in range(1, s):
if np.abs(a[i] - d) > threshold:
o[i] = r[i]
d = a[i]
else:
o[i] = o[i - 1]
return o
我们来测试一下:
a = df['input'].values
pd.Series(cumsum_with_threshold(a, 100))
0 11700.0
1 11700.0
2 11700.0
3 11700.0
4 11700.0
5 11700.0
6 11600.0
7 11600.0
8 11600.0
9 11600.0
10 11700.0
11 11700.0
12 11700.0
13 11600.0
14 11600.0
dtype: float64
如果要将舍入值与输入进行比较而不是实际值,只需在循环中对上面的函数进行以下更改,从而提供问题的输出 .
for i in range(1, s):
if np.abs(a[i] - d) > t:
o[i] = r[i]
# OLD d = a[i]
d = r[i]
else:
o[i] = o[i - 1]
为了测试效率,让我们在更大的数据集上运行它:
l = np.random.choice(df['input'].values, 10_000_000)
%timeit cumsum_with_threshold(l, 100)
1.54 µs ± 7.93 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)