NumPy: Vectorize encontrar el valor más cercano en una matriz para cada elemento en otra matriz

Entrada

known_array : matriz numpy; consistiendo solamente en valores escalares;shape: (m, 1)

test_array : matriz numpy; consistiendo solamente en valores escalares;shape: (n, 1)

Salida

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

Ejemplo
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 PythonActualizaciones

Hice 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ón

known_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.

Respuestas a la pregunta(3)

Su respuesta a la pregunta