python - 在另一个成对的 bin 数组中获取数据数组最小值的最快方法
问题描述
我有三个一维数组:
idxs
: 索引数据weights
: 每个指标的权重idxs
bins
:用于计算其中最小重量的箱。
这是我目前使用的方法idxs
来检查weights
在哪个 bin 中调用的数据,然后计算 binned weights 的最小/最大值:
- 获取
slices
显示每个idxs
元素属于哪个 bin。 - 排序
slices
和weights
同时。 - 计算
weights
每个 bin(切片)中的最小值。
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])
如果我将 1e7 增加到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 秒。
mean
我还检查了计算所消耗的时间np.histogram
:
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,那就太好了!
用户案例
解决方案
实现此目的的一种快速方法是使用dask.dataframe
and pd.cut
,我首先显示pandas
版本:
import numpy as np
from scipy.stats import binned_statistic as bs
import pandas as pd
nrows=10**7
df = pd.DataFrame(np.random.rand(nrows, 2), columns=['x', 'val'])
bins = np.linspace(df['x'].min(), df['x'].max(), 10)
df['binned_x'] = pd.cut(df['x'], bins=bins, right=False)
result_pandas = df.groupby('binned_x')['val'].min().values
result_scipy = bs(df['x'], df['val'], 'min', bins=bins)[0]
print(np.isclose(result_pandas, result_scipy))
# [ True True True True True True True True True]
现在要从pandas
to 开始dask
,您需要确保 bin 在分区之间保持一致,因此请看这里。一旦每个分区都被一致地分箱,您想要应用所需的操作(最小/最大/总和/计数):
import dask.dataframe as dd
ddf = dd.from_pandas(df, npartitions=10)
def f(df, bins):
df = df.copy()
df['binned_x'] = pd.cut(df['x'], bins=bins, right=False)
result = df.groupby('binned_x', as_index=False)['val'].min()
return result
result_dask = ddf.map_partitions(f, bins).groupby('binned_x')['val'].min().compute()
print(np.isclose(result_pandas, result_dask))
# [ True True True True True True True True True]
在我的笔记本电脑上,第一个代码大约需要7 3 秒,第二个代码大约快10倍(忘记了我在重复计算 pandas 和 scipy 执行相同的操作)。分区有一定的空间,但这取决于上下文,所以你可以尝试优化你的数据/硬件。
更新:请注意,这种方法适用于最小值/最大值,但意味着您需要计算总和和计数,然后将它们相除。可能有一种很好的方法可以在一次完成此计算时跟踪权重,但这可能不值得增加代码复杂性。
推荐阅读
- sass - Bulma 中的 Mixins 与官方的 Sass 文档完全不同。你能帮我理解吗?
- neo4j - 在 Cypher 中创建节点
- r - r中的非嵌套和连接值
- c++ - 如何附加到 std::runtime_error 的什么字符串
- python - 如何计算运动物体的速度
- mysql - ERROR 1491 (HY000) : PARTITION 函数返回错误的类型
- javascript - 如何获取包含特定字符串的数组元素的索引
- cookies - Set-Cookie 从服务器到不同域中的 XHR 客户端,将域设置为客户端的域,它应该工作吗?
- python - 如何在 Python 中使用带前缀的 str.get_dummies?
- android-studio - 颤振创建命令在andorid studio中不成功