Permuta una matriz en el lugar en numpy


27

Quiero modificar una matriz de transición cuadrada densa en el lugar cambiando el orden de varias de sus filas y columnas, usando la biblioteca numpy de python. Matemáticamente, esto corresponde a la multiplicación previa de la matriz por la matriz de permutación P y la multiplicación posterior por P ^ -1 = P ^ T, pero esta no es una solución computacionalmente razonable.

En este momento estoy intercambiando manualmente filas y columnas, pero hubiera esperado que numpy tuviera una buena función f (M, v) donde M tiene n filas y columnas, y v tiene n entradas, de modo que f (M, v) se actualiza M de acuerdo con el índice de permutación v. Tal vez simplemente no estoy buscando en Internet.

Algo así podría ser posible con la "indexación avanzada" de numpy, pero entiendo que tal solución no estaría en su lugar. También para algunas situaciones simples puede ser suficiente rastrear por separado una permutación de índice, pero esto no es conveniente en mi caso.

Agregado: a
veces, cuando las personas hablan de permutaciones, solo se refieren al muestreo de permutaciones aleatorias, por ejemplo, como parte de un procedimiento para obtener valores p en las estadísticas. O significan contar o enumerar todas las permutaciones posibles. No estoy hablando de estas cosas.

Agregado:
la matriz es lo suficientemente pequeña como para caber en la RAM del escritorio, pero lo suficientemente grande como para que no quiera copiarla sin pensar. En realidad, me gustaría usar matrices lo más grandes posible, pero no quiero lidiar con el inconveniente de no poder mantenerlas en la RAM, y hago operaciones de O (N ^ 3) LAPACK en la matriz que también limitar el tamaño práctico de la matriz. Actualmente copio matrices tan grandes innecesariamente, pero espero que esto pueda evitarse fácilmente para la permutación.


3
Sería bueno si pudiera actualizar la pregunta para dar el tamaño de sus matrices. "Gigantesco" no significa lo mismo para todas las personas.
Bill Barth

2
Tiene razón en que la indexación avanzada (o llamada fantasía) crea una copia. Pero si acepta vivir con ese hecho, entonces su código es solo M[v]para permutar las filas.
Daniel Velkov

@daniel: ¿Y sería M [v,:] [:, v] hacer toda la permutación? ¿Sería esta la mejor manera de obtener la permutación usando una indexación elegante? ¿Y usaría 3 veces la memoria de la matriz, incluido el tamaño de la matriz original, la matriz permutada de fila + columna y la matriz permutada de fila temporal?
ninguno

Eso es correcto, tendría su matriz original y 2 copias. Por cierto, ¿por qué necesitas permutar ambas filas y columnas al mismo tiempo?
Daniel Velkov

44
¿Qué vas a hacer con la matriz permutada? Puede ser mejor simplemente permutar el vector al aplicar el operador.
Jed Brown

Respuestas:


9

Según los documentos, no hay un método de permutación en el lugar en numpy, algo así como ndarray.sort .

Entonces, sus opciones son (suponiendo que Msea ​​una matriz y el vector de permutación)N×Np

  1. implementando su propio algoritmo en C como módulo de extensión (¡pero los algoritmos locales son difíciles, al menos para mí!)
  2. N sobrecarga de memoria

    for i in range(N):
        M[:,i] = M[p,i]
    for i in range(N):
        M[i,:] = M[i,p]
    
  3. N2 sobrecarga de memoria

    M[:,:] = M[p,:]
    M[:,:] = M[:,p]
    

Espero que estos hacks subóptimos sean útiles.


@none is hack 2. ¿a qué llamas 'intercambiar manualmente filas y columnas'?
Stefano M

1
Combinaría las opciones 1 y 2: escribir código C que usa un búfer de orden N para escribir cada columna permutada, luego lo escribe de nuevo de donde vino; luego haga lo mismo para las filas. Como escribe @Stefano, esto solo requiere memoria adicional , que ya está gastando para almacenar la permutación en primer lugar. pO(N)p
Erik P.

@ErikP. para una implementación en C, la memoria adicional es razonable y, con seguridad, el enfoque de escritura dispersa a temperatura y copia de respaldo es sólido. Sin embargo, la pregunta interesante es si existen algoritmos más eficientes, dada la memoria adicional de . Creo que la respuesta es difícil, ya que deberíamos tener en cuenta la arquitectura del procesador, los patrones de acceso a la memoria, los accesos a la memoria caché, ... Dicho esto, seguiría sus consejos y elegiría un algoritmo simple y fácil de implementar. O ( N )O(N)O(N)
Stefano M

2
Esta es una muy buena opción para una función de cython. No debe tener más de 10 líneas. . . ¿Quieres que le dé un crack?
meawoppl

Jajaja Empecé a Cython esto, luego encontré la respuesta correcta en una función que uso todo el tiempo. Doh Ver mi respuesta publicada.
meawoppl

6

Advertencia: El siguiente ejemplo funciona correctamente, pero el uso del conjunto completo de parámetros sugeridos al final de la publicación expone un error , o al menos una "característica no documentada" en la función numpy.take (). Vea los comentarios a continuación para más detalles. Informe de error archivado .

Puede hacerlo en el lugar con la función take () de numpy , pero requiere un poco de salto de aro.

Aquí hay un ejemplo de hacer una permutación aleatoria de las filas de una matriz de identidad:

import numpy as np
i = np.identity(10)
rr = range(10)
np.random.shuffle(rr)
np.take(i, rr, axis=0)
array([[ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.]])

Para hacerlo en el lugar, todo lo que necesita hacer es especificar el parámetro "out" para que sea el mismo que la matriz de entrada Y debe configurar mode = "clip" o mode = "wrap". Si no configura el modo, realizará una copia para restaurar el estado de la matriz en una excepción de Python (consulte aquí) .

En una nota final, take parece ser un método de matriz, así que en lugar de

np.take(i, rr, axis=0)

podrías llamar

i.take(rr, axis=0)

si eso es más a tu gusto. Entonces, en total, la llamada debe tener un aspecto similar al siguiente:

#Inplace Rearrange
arr = makeMyBixMatrix()
pVec0, pVec1 = calcMyPermutationVectors()
arr.take(pVec0, axis=0, out=arr, mode="clip")
arr.take(pVec1, axis=1, out=arr, mode="clip")

Para permutar tanto las filas como las columnas, creo que tienes que ejecutarlo dos veces, o sacar algunas travesuras feas con numpy.unravel_index que me duele la cabeza al pensar.


Como se dijo, los algoritmos establecidos son difíciles. Su solución no funciona con numpy 1.6.2. y 1.7.1 (filas / columnas duplicadas). No tuve tiempo de verificar si 1.8.x soluciona este problema
Stefano M

Hmmm ¿Puedes publicar el código de prueba en alguna parte? En mi cabeza, siento que debe haber una operación de clasificación en los índices que ocurre primero antes del desplume. Investigaré más este PM.
meawoppl

1
Cuando ejecuto el código consigo 1.6.2, test take, not overwriting: True, test not-in-place take: True, test in-place take: False, rr [3, 7, 8, 1, 4, 5, 9, 0, 2, 6], arr [30 70 80 70 40 50 90 30 80 90], ref [30 70 80 10 40 50 90 0 20 60]. Entonces, np.takeal menos para numpy 1.6.2 no es consciente de hacer una permutación en el lugar y arruina las cosas.
Stefano M

Yeouch Bien demostrado Esto probablemente califica como un error en mi humilde opinión. Por lo menos, los documentos deben decir que la entrada y la salida no pueden ser la misma matriz, probablemente verifique para ver y excepto si lo es.
meawoppl

De acuerdo con el error: tal vez debería agregar una nota a su publicación para advertir a los lectores que su solución puede producir resultados incorrectos.
Stefano M

2

Si tiene una matriz dispersa almacenada en COOformato, lo siguiente podría ser útil

    A.row = perm[A.row];
    A.col = perm[A.col];

suponiendo que Acontiene la COOmatriz, y permes un que numpy.arraycontiene la permutación. Esto solo tendrá sobrecarga de memoria, donde es el número de elementos distintos de cero de la matriz.mmm


¿Pero cuál es la sobrecarga de memoria para almacenar una matriz densa completa como una C00matriz dispersa en primer lugar?
Federico Poloni

Como el número de elementos es igual tanto en una representación densa dispersa como en una (densa), la diferencia de memoria es meramente una constante (2 intsy 1 floaten representación dispersa por elemento como un solo floaten la representación densa). Pero la sobrecarga de memoria de este método será en un caso denso, por lo que probablemente se podría seguir mejor con s normal . n2numpy.ndarray
Vincent Traag

1

No tengo suficiente reputación para comentar, pero creo que la siguiente pregunta SO podría ser útil: /programming/4370745/view-onto-a-numpy-array

Los puntos básicos son que se puede utilizar rebanar básica y que va a crear una visión de a la matriz sin necesidad de copiar, pero si lo hace avanzada rebanar / indexación entonces se van a crear una copia.


El OP está pidiendo una permutación, y esto no es posible con el corte básico.
Stefano M

Estás en lo cierto, por supuesto. Pensé que sería útil para el OP entender lo que estaba sucediendo con el corte (en caso de que no lo supieran) ya que les preocupaba cuándo ocurrirían las copias. Si utilizó algo de su respuesta, creo que sería bueno saberlo, ya que los usa dentro de sus bucles.
hablado el

-1

Qué pasa

my_array [:, [0, 1]] = my_array [:, [1, 0]]


1
Esto construye un temporal, que es exactamente lo que quiere evitar.
Michael Grant
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.