【发布时间】:2021-08-25 04:29:04
【问题描述】:
我有三个一维数组:
-
idxs:索引数据 -
weights:idxs中各个指标的权重 -
bins: 用于计算其中最小重量的 bin。
这是我目前使用idxs检查名为weights的数据在哪个bin中的方法,然后计算binned weights的最小值/最大值:
- 获取
slices,它显示每个idxs元素属于哪个bin。 - 同时对
slices和weights进行排序。 - 计算每个 bin(切片)中
weights的最小值。
numpy 方法
import random
import numpy as np
# create example data
out_size = int(10)
bins = np.arange(3, out_size-3)
idxs = np.arange(0, out_size)
#random.shuffle(idxs)
# set duplicated slice manually for test
idxs[4] = idxs[3]
idxs[6] = idxs[7]
weights = idxs
# get which bin idxs belong to
slices = np.digitize(idxs, bins)
# get index and weights in bins
valid = (bins.max() >= idxs) & (idxs >= bins.min())
valid_slices = slices[valid]
valid_weights = weights[valid]
# sort slice and weights
sort_index = valid_slices.argsort()
valid_slices_sort = valid_slices[sort_index]
valid_weights_sort = valid_weights[sort_index]
# get index of each first unque slices
unique_slices, unique_index = np.unique(valid_slices_sort, return_index=True)
# calculate the minimum
res_sub = np.minimum.reduceat(valid_weights_sort, unique_index)
# save results
res = np.full((out_size), np.nan)
res[unique_slices-1] = res_sub
print(res)
结果:
array([ 3., nan, 5., nan, nan, nan, nan, nan, nan, nan])
如果我将out_size 增加到 1e7 并打乱数据,速度(从 np.digitize 到最后)很慢:
13.5 s ± 136 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
而且,这是每个部分的消耗时间:
np.digitize: 10.8 s ± 12.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
valid: 171 ms ± 3.78 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
argsort and slice: 2.02 s ± 33.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
unique: 9.9 ms ± 113 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
np.minimum.reduceat: 5.11 ms ± 52.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
np.digitize() 花费最多的时间:10.8 秒。而且,接下来是argsort: 2.02 秒。
我还检查了使用np.histogram计算mean所消耗的时间:
counts, _ = np.histogram(idxs, bins=out_size, range=(0, out_size))
sums, _ = np.histogram(idxs, bins=out_size, range=(0, out_size), weights = weights, density=False)
mean = sums / np.where(counts == 0, np.nan, counts)
33.2 s ± 3.47 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
这类似于我计算最小值的方法。
scipy方法
from scipy.stats import binned_statistic
statistics, _, _ = binned_statistic(idxs, weights, statistic='min', bins=bins)
print(statistics)
结果略有不同,但对于较长(1e7)的混洗数据,速度要慢得多(x6):
array([ 3., nan, 5.])
1min 20s ± 6.93 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
总结
我想找出一个更快的方法。如果该方法也适用于dask,那就太好了!
用户案例
【问题讨论】:
-
对于您的实际用例,您是否需要维护 binned_statistic 的权重功能? (例如,用于计算“平均值”)
-
@SultanOrazbayev 权重只是数据。我不需要乘以它来计算。对不起,误导的名字。我只想获取每个 bin 中数据的最小值或最大值或平均值。
-
好的,没有
binned_statistic很容易做到最小值/最大值,但是对于计算平均值,权重很重要......我会想一个好的答案。 -
平均而言,我知道快速方法:只需使用直方图计算总和和计数。请随时发布最小/最大解决方案;)
-
对您的数据进行排序/排序有任何假设吗?数字化或计算直方图通常涉及排序,因此您将很难击败 O(nlog(n)),除非您可以对数据的排序做出假设。
标签: python pandas numpy scipy dask