NumPy: Vectorize encontrar el valor más cercano en una matriz para cada elemento en otra matriz
known_array
: matriz numpy; consistiendo solamente en valores escalares;shape: (m, 1)
test_array
: matriz numpy; consistiendo solamente en valores escalares;shape: (n, 1)
indices
: matriz numpy;shape: (n, 1)
; Para cada valor entest_array
encuentra el índice del valor más cercano enknown_array
residual
: matriz numpy;shape: (n, 1)
; Para cada valor entest_array
encuentra la diferencia del valor más cercano enknown_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])
Implementación de la muestra (no completamente vectorizada)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.])
¿Cuál es la mejor manera de acelerar esta tarea? Cython es una opción, pero siempre preferiría poder eliminar elfor
Bucle y dejar que el código permanezca son puro NumPy.
nótese bien: Se consultaron las siguientes preguntas de desbordamiento de pila
Python / Numpy - Encuentre rápidamente el índice en una matriz más cercana a algún valorEncuentra el índice del valor numéricamente más cercanoEncuentra el valor más cercano en la matriz numpyEncontrar el valor más cercano y devolver el índice de matriz en PythonEncontrar elementos más cercanos en dos listas / arrays en PythonActualizacionesHice algunos pequeños puntos de referencia para comparar la solución no vectorizada y vectorizada (respuesta aceptada).
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
Acerca de una10 veces ¡acelerar!
Aclaraciónknown_array
no está ordenado
Ejecuté los puntos de referencia como se indica en la respuesta de @cyborg a continuación.
Caso 1: Siknown_array
fueron ordenados
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.
En primer lugar, para grandes matrices.diffs
El método es en realidad más lento, también consume mucha memoria RAM y mi sistema se bloquea cuando lo ejecuto en datos reales.
Caso 2 : Cuandoknown_array
no está ordenado que representa el escenario real
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.
También debo comentar que el enfoque también debe ser eficiente en memoria. De lo contrario mis 8 GB de RAM no son suficientes. En el caso base, es fácilmente suficiente.