Tengo una matriz de números y me gustaría crear otra matriz que represente el rango de cada elemento en la primera matriz. Estoy usando Python y NumPy.
Por ejemplo:
array = [4,2,7,1]
ranks = [2,1,3,0]
Este es el mejor método que se me ocurrió:
array = numpy.array([4,2,7,1])
temp = array.argsort()
ranks = numpy.arange(len(array))[temp.argsort()]
¿Existen métodos mejores / más rápidos que eviten ordenar la matriz dos veces?
ranks = temp.argsort()
.Respuestas:
Utilice el corte en el lado izquierdo en el último paso:
Esto evita ordenar dos veces al invertir la permutación en el último paso.
fuente
Use argsort dos veces, primero para obtener el orden de la matriz, luego para obtener la clasificación:
Cuando se trata de matrices 2D (o de mayor dimensión), asegúrese de pasar un argumento de eje a argsort para ordenar sobre el eje correcto.
fuente
[4,2,7,1,1]
), la salida clasificará esos números en función de su posición de matriz ([3,2,4,0,1]
)argsort
.array = np.random.rand(10)
debería serarray = np.random.rand(n)
.Esta pregunta tiene algunos años y la respuesta aceptada es excelente, pero creo que vale la pena mencionar lo siguiente. Si no le importa la dependencia de
scipy
, puede usarscipy.stats.rankdata
:Una característica interesante de
rankdata
es que elmethod
argumento ofrece varias opciones para manejar los empates. Por ejemplo, hay tres ocurrencias de 20 y dos ocurrencias de 40 enb
:El valor predeterminado asigna el rango promedio a los valores vinculados:
method='ordinal'
asigna rangos consecutivos:method='min'
asigna el rango mínimo de los valores vinculados a todos los valores vinculados:Consulte la cadena de documentos para obtener más opciones.
fuente
rankdata
parezca usar el mismo mecanismo que la respuesta aceptada para generar la clasificación inicial internamente.Intenté extender ambas soluciones para matrices A de más de una dimensión, suponiendo que procesa su matriz fila por fila (eje = 1).
Extendí el primer código con un bucle en filas; probablemente se pueda mejorar
Y el segundo, siguiendo la sugerencia de k.rooijers, se convierte en:
Generé aleatoriamente 400 matrices con forma (1000,100); el primer código tomó aproximadamente 7.5, el segundo 3.8.
fuente
Para obtener una versión vectorizada de un rango promedio, consulte a continuación. Me encanta np.unique, realmente amplía el alcance de lo que el código puede y no puede vectorizarse de manera eficiente. Además de evitar los bucles for de Python, este enfoque también evita el bucle doble implícito sobre 'a'.
fuente
Aparte de la elegancia y la brevedad de las soluciones, también está la cuestión del rendimiento. Aquí hay un pequeño punto de referencia:
fuente
rankdata(l, method='ordinal') - 1
.Use argsort () dos veces lo hará:
fuente
Probé los métodos anteriores, pero fallé porque tenía muchos zeores. Sí, incluso con flotadores, los elementos duplicados pueden ser importantes.
Así que escribí una solución 1D modificada agregando un paso de verificación de empates:
Creo que es lo más eficiente que puede ser.
fuente
Me gustó el método de k.rooijers, pero como escribió rcoup, los números repetidos se clasifican según la posición de la matriz. Esto no fue bueno para mí, así que modifiqué la versión para posprocesar los rangos y fusionar los números repetidos en un rango promedio combinado:
Espero que esto también ayude a otros, intenté encontrar otra solución para esto, pero no pude encontrar ninguna ...
fuente
argsort y slice son operaciones de simetría.
intente cortar dos veces en lugar de argsort dos veces. ya que slice es más rápido que argsort
fuente
Versión más general de una de las respuestas:
Consulte ¿Cómo utilizar numpy.argsort () como índices en más de 2 dimensiones? para generalizar a más dims.
fuente