Hace unos años, alguien posted en Recetas de estado activo con fines de comparación, tres funciones de python/NumPy; cada uno de estos aceptó los mismos argumentos y arrojó el mismo resultado, una matriz de distancia .¿Por qué Looping Beat Indexing aquí?
Dos de estos fueron tomados de fuentes publicadas; ambos son, o me parecen ser, un código idiomático numpy. Los cálculos repetitivos necesarios para crear una matriz de distancia se basan en la sintaxis del índice elegante de numpy. He aquí uno de ellos:
from numpy.matlib import repmat, repeat
def calcDistanceMatrixFastEuclidean(points):
numPoints = len(points)
distMat = sqrt(sum((repmat(points, numPoints, 1) -
repeat(points, numPoints, axis=0))**2, axis=1))
return distMat.reshape((numPoints,numPoints))
La tercera creada la matriz de distancia utilizando un solo bucle (que, obviamente, una gran cantidad de looping, dado que una matriz de distancia de tan sólo 1.000 puntos 2D, tiene un millón de entradas). A primera vista, esta función me pareció como el código que solía escribir cuando estaba aprendiendo NumPy y escribía el código de NumPy escribiendo primero el código de Python y luego traduciéndolo, línea por línea.
Varios meses después de la publicación de estado activo, los resultados de las pruebas de rendimiento que comparaban los tres se publicaron y discutieron en un thread en la lista de correo de NumPy.
La función con el bucle de hecho significativamente superaron los otros dos:
from numpy import mat, zeros, newaxis
def calcDistanceMatrixFastEuclidean2(nDimPoints):
nDimPoints = array(nDimPoints)
n,m = nDimPoints.shape
delta = zeros((n,n),'d')
for d in xrange(m):
data = nDimPoints[:,d]
delta += (data - data[:,newaxis])**2
return sqrt(delta)
Uno de los participantes en el hilo (Keir Mierle) ofreció una razón por qué esto podría ser verdad:
La razón por la que sospecho que esto será más rápido es que tiene mejor localidad, completando por completo un cálculo en un conjunto de trabajo relativamente pequeño antes de pasar al siguiente uno. Los trazadores de líneas uno tienen que tirar de la matriz MxN potencialmente grande en el procesador en varias ocasiones.
Por la propia cuenta de este cartel, su observación es solo una sospecha, y no parece que se haya discutido más.
¿Alguna otra idea sobre cómo dar cuenta de estos resultados?
En particular, ¿existe una regla útil sobre cuándo realizar un bucle y cuándo indexar que se puede extraer de este ejemplo como guía para escribir código numpy?
Para aquellos que no están familiarizados con NumPy, o que no han mirado el código, esta comparación no se basa en un caso marginal, sin duda no sería tan interesante para mí. En cambio, esta comparación implica una función que realiza una tarea común en el cálculo de la matriz (es decir, la creación de un conjunto de resultados dados dos antecedentes); además, cada función está compuesta a su vez por las más comunes numpy incorporadas.
muy útil, gracias. +1 de mi parte – doug