【发布时间】:2014-01-13 19:48:15
【问题描述】:
输入
known_array : numpy 数组;仅由标量值组成; shape: (m, 1)
test_array : numpy 数组;仅由标量值组成; shape: (n, 1)
输出
indices : numpy 数组; shape: (n, 1);对于test_array 中的每个值,查找known_array 中最接近值的索引
residual : numpy 数组; shape: (n, 1);对于test_array 中的每个值,找出与known_array 中最接近的值的差
示例
In [17]: known_array = np.array([random.randint(-30,30) for i in range(5)])
In [18]: known_array
Out[18]: array([-24, -18, -13, -30, 29])
In [19]: test_array = np.array([random.randint(-10,10) for i in range(10)])
In [20]: test_array
Out[20]: array([-6, 4, -6, 4, 8, -4, 8, -6, 2, 8])
示例实现(未完全矢量化)
def find_nearest(known_array, value):
idx = (np.abs(known_array - value)).argmin()
diff = known_array[idx] - value
return [idx, -diff]
In [22]: indices = np.zeros(len(test_array))
In [23]: residual = np.zeros(len(test_array))
In [24]: for i in range(len(test_array)):
....: [indices[i], residual[i]] = find_nearest(known_array, test_array[i])
....:
In [25]: indices
Out[25]: array([ 2., 2., 2., 2., 2., 2., 2., 2., 2., 2.])
In [26]: residual
Out[26]: array([ 7., 17., 7., 17., 21., 9., 21., 7., 15., 21.])
加快这项任务的最佳方法是什么? Cython 是一种选择,但是,我总是希望能够删除 for 循环并让代码保持纯 NumPy。
注意:咨询了以下 Stack Overflow 问题
- Python/Numpy - Quickly Find the Index in an Array Closest to Some Value
- Find the index of numerically closest value
- Find nearest value in numpy array
- Finding the nearest value and return the index of array in Python
- finding nearest items across two lists/arrays in Python
更新
我做了一些小的基准来比较非矢量化和矢量化解决方案(接受的答案)。
In [48]: [indices1, residual1] = find_nearest_vectorized(known_array, test_array)
In [53]: [indices2, residual2] = find_nearest_non_vectorized(known_array, test_array)
In [54]: indices1==indices2
Out[54]: array([ True, True, True, True, True, True, True, True, True, True], dtype=bool)
In [55]: residual1==residual2
Out[55]: array([ True, True, True, True, True, True, True, True, True, True], dtype=bool)
In [56]: %timeit [indices2, residual2] = find_nearest_non_vectorized(known_array, test_array)
10000 loops, best of 3: 173 µs per loop
In [57]: %timeit [indices1, residual1] = find_nearest_vectorized(known_array, test_array)
100000 loops, best of 3: 16.8 µs per loop
约 10 倍 加速!
澄清
known_array 未排序。
我运行了下面@cyborg 回答中给出的基准。
案例 1:如果 known_array 已排序
known_array = np.arange(0,1000)
test_array = np.random.randint(0, 100, 10000)
print('Speedups:')
base_time = time_f('base')
for func_name in ['diffs', 'searchsorted1', 'searchsorted2']:
print func_name + ' is x%.1f faster than base.' % (base_time / time_f(func_name))
assert np.allclose(base(known_array, test_array), eval(func_name+'(known_array, test_array)'))
Speedups:
diffs is x0.4 faster than base.
searchsorted1 is x81.3 faster than base.
searchsorted2 is x107.6 faster than base.
首先,对于大型数组,diffs 方法实际上速度较慢,它还占用了大量 RAM,当我在实际数据上运行时我的系统挂起。
情况2:当known_array未排序时;代表实际场景
known_array = np.random.randint(0,100,100)
test_array = np.random.randint(0, 100, 100)
Speedups:
diffs is x8.9 faster than base.
AssertionError Traceback (most recent call last)
<ipython-input-26-3170078c217a> in <module>()
5 for func_name in ['diffs', 'searchsorted1', 'searchsorted2']:
6 print func_name + ' is x%.1f faster than base.' % (base_time / time_f(func_name))
----> 7 assert np.allclose(base(known_array, test_array), eval(func_name+'(known_array, test_array)'))
AssertionError:
searchsorted1 is x14.8 faster than base.
我还必须评论说,这种方法也应该是内存效率的。否则我的 8 GB RAM 是不够的。在基本情况下,这很容易就足够了。
【问题讨论】:
-
你的数据是否排序没关系; HYRY 发布的方法处理了这种情况,并且具有线性而不是 diff 方法的二次内存性能;他的答案应该被标记为正确的
标签: python algorithm numpy vectorization cython