En relación con esta respuesta , ¿hay una manera rápida de calcular medianas sobre una matriz que tiene grupos con un número desigual de elementos?
P.ej:
data = [1.00, 1.05, 1.30, 1.20, 1.06, 1.54, 1.33, 1.87, 1.67, ... ]
index = [0, 0, 1, 1, 1, 1, 2, 3, 3, ... ]
Y luego quiero calcular la diferencia entre el número y la mediana por grupo (por ejemplo, la mediana del grupo 0
es 1.025
el primer resultado 1.00 - 1.025 = -0.025
). Entonces, para la matriz anterior, los resultados aparecerían como:
result = [-0.025, 0.025, 0.05, -0.05, -0.19, 0.29, 0.00, 0.10, -0.10, ...]
Dado np.median.reduceat
que no existe (todavía), ¿hay otra forma rápida de lograr esto? ¡Mi matriz contendrá millones de filas, por lo que la velocidad es crucial!
Se puede suponer que los índices son contiguos y ordenados (es fácil transformarlos si no lo son).
Datos de ejemplo para comparaciones de rendimiento:
import numpy as np
np.random.seed(0)
rows = 10000
cols = 500
ngroup = 100
# Create random data and groups (unique per column)
data = np.random.rand(rows,cols)
groups = np.random.randint(ngroup, size=(rows,cols)) + 10*np.tile(np.arange(cols),(rows,1))
# Flatten
data = data.ravel()
groups = groups.ravel()
# Sort by group
idx_sort = groups.argsort()
data = data[idx_sort]
groups = groups[idx_sort]
python
performance
numpy
median
numpy-ufunc
Juan Pablo
fuente
fuente
scipy.ndimage.median
sugerencia en la respuesta vinculada? No me parece que necesite un número igual de elementos por etiqueta. ¿O me perdí algo?Respuestas:
A veces necesita escribir código numpy no idiomático si realmente quiere acelerar su cálculo, lo que no puede hacer con numpy nativo.
numba
compila su código de Python a bajo nivel C. Dado que una gran cantidad de numpy suele ser tan rápido como C, esto generalmente resulta útil si su problema no se presta a la vectorización nativa con numpy. Este es un ejemplo (donde supuse que los índices son contiguos y ordenados, lo que también se refleja en los datos del ejemplo):Y aquí hay algunos tiempos usando la
%timeit
magia de IPython :Usando los datos de ejemplo actualizados en la pregunta, estos números (es decir, el tiempo de ejecución de la función python frente al tiempo de ejecución de la función acelerada por JIT) son
Esto equivale a una aceleración de 65x en el caso más pequeño y una aceleración de 26x en el caso más grande (en comparación con el código de bucle lento, por supuesto) usando el código acelerado. Otra ventaja es que (a diferencia de la vectorización típica con numpy nativo) no necesitábamos memoria adicional para lograr esta velocidad, se trata de código de bajo nivel optimizado y compilado que termina ejecutándose.
La función anterior supone que las matrices numpy int son
int64
por defecto, lo cual no es el caso en Windows. Así que una alternativa es eliminar la firma de la llamada anumba.njit
, lo que provocó adecuada compilación justo a tiempo. Pero esto significa que la función se compilará durante la primera ejecución, lo que puede interferir con los resultados de temporización (podemos ejecutar la función una vez manualmente, utilizando tipos de datos representativos, o simplemente aceptar que la primera ejecución de temporización será mucho más lenta, lo que debería ser ignorado). Esto es exactamente lo que intenté evitar al especificar una firma, que desencadena una compilación anticipada.De todos modos, en el caso adecuado de JIT, el decorador que necesitamos es solo
Tenga en cuenta que los tiempos anteriores que mostré para la función compilada jit solo se aplican una vez que la función se ha compilado. Esto ocurre ya sea en la definición (con la compilación ansiosos, cuando una firma explícita se pasa a
numba.njit
), o durante la primera llamada de función (con la compilación perezoso, cuando no hay ninguna firma se pasa anumba.njit
). Si la función solo se ejecutará una vez, el tiempo de compilación también debe considerarse para la velocidad de este método. Por lo general, solo vale la pena compilar funciones si el tiempo total de compilación + ejecución es menor que el tiempo de ejecución sin compilar (que en realidad es cierto en el caso anterior, donde la función nativa de Python es muy lenta). Esto ocurre principalmente cuando llama a su función compilada muchas veces.Como max9111 señaló en un comentario, una característica importante de
numba
es lacache
palabra clave tojit
. Pasarcache=True
anumba.jit
almacenará la función compilada en el disco, de modo que durante la próxima ejecución del módulo python dado, la función se cargará desde allí en lugar de volver a compilarse, lo que de nuevo puede ahorrarle tiempo de ejecución a largo plazo.fuente
index
datos de roganjosh . Dejaré una nota sobre esto, gracias :)cache=True
para evitar la recompilación en cada reinicio del intérprete.Un enfoque sería usar
Pandas
aquí simplemente para usargroupby
. He inflado un poco los tamaños de entrada para dar una mejor comprensión de los tiempos (ya que hay una sobrecarga en la creación del DF).Da lo siguiente
timeit
:Para el mismo tamaño de muestra, obtengo el enfoque dict de Aryerez :
Sin embargo, si aumentamos las entradas por otro factor de 10, los tiempos se convierten en:
Sin embargo, a expensas de cierta fiabilidad, la respuesta de Divakar usando numpy puro llega a:
A la luz del nuevo conjunto de datos (que realmente debería haberse configurado al principio):
fuente
Tal vez ya hiciste esto, pero si no, mira si es lo suficientemente rápido:
Salida:
fuente
np.vectorize
es un envoltorio muy delgado para un bucle, por lo que no esperaría que este enfoque sea particularmente rápido.data
yindex
quenp.array
s como en la pregunta.Aquí hay un enfoque basado en NumPy para obtener la mediana de binned para bins positivos / valores de índice:
Para resolver nuestro caso específico de los restados:
fuente
df.groupby('index').transform('median')
?