Permettere una matrice sul posto in numpy


27

Voglio modificare una densa matrice di transizione quadrata sul posto cambiando l'ordine di molte delle sue righe e colonne, usando la libreria numpy di Python. Matematicamente questo corrisponde a pre-moltiplicare la matrice per la matrice di permutazione P e post-moltiplicarla per P ^ -1 = P ^ T, ma questa non è una soluzione computazionalmente ragionevole.

In questo momento sto scambiando manualmente righe e colonne, ma mi sarei aspettato che numpy avesse una bella funzione f (M, v) dove M ha n righe e colonne e v ha n voci, quindi f (M, v) si aggiorna M secondo la permutazione dell'indice v. Forse non riesco a cercare su Internet.

Qualcosa del genere potrebbe essere possibile con l '"indicizzazione avanzata" di numpy, ma la mia comprensione è che tale soluzione non sarebbe in atto. Anche per alcune semplici situazioni può essere sufficiente tenere traccia separatamente di una permutazione dell'indice, ma questo non è conveniente nel mio caso.

Aggiunto: a
volte quando le persone parlano di permutazioni, significano solo il campionamento di permutazioni casuali, ad esempio come parte di una procedura per ottenere valori p nelle statistiche. Oppure significano contare o enumerare tutte le possibili permutazioni. Non sto parlando di queste cose.

Aggiunto:
la matrice è abbastanza piccola da adattarsi alla RAM del desktop ma abbastanza grande da non volerlo copiare senza pensarci. In realtà vorrei usare matrici il più grandi possibile, ma non voglio affrontare l'inconveniente di non essere in grado di tenerle nella RAM, e faccio O (N ^ 3) operazioni LAPACK sulla matrice che anche limitare le dimensioni pratiche della matrice. Attualmente copio matrici così grandi inutilmente, ma spero che questo possa essere facilmente evitato per permutazione.


3
Sarebbe bello se potessi aggiornare la domanda per dare la dimensione delle tue matrici. "Gigantesco" non significa la stessa cosa per tutte le persone.
Bill Barth,

2
Hai ragione che l'indicizzazione avanzata (o cosiddetta fantasia) crea una copia. Ma se accetti di convivere con quel fatto, allora il tuo codice è solo M[v]per permutare le righe.
Daniel Velkov,

@daniel: E sarebbe M [v,:] [:, v] fare l'intera permutazione? Questo sarebbe il modo migliore per ottenere la permutazione usando l'indicizzazione fantasia? E userebbe 3 volte la memoria della matrice, comprese le dimensioni della matrice originale, la matrice permutata riga + colonna e la matrice permutata riga temporanea?
nessuna dal

Esatto, avresti la tua matrice originale e 2 copie. A proposito, perché è necessario consentire sia le righe che le colonne contemporaneamente?
Daniel Velkov,

4
Che cosa hai intenzione di fare con la matrice permutata? Potrebbe essere meglio permutare semplicemente il vettore quando si applica l'operatore.
Jed Brown,

Risposte:


9

Secondo i documenti, non esiste un metodo di permutazione sul posto in numpy, qualcosa come ndarray.sort .

Quindi le tue opzioni sono (supponendo che Msia una matrice e il vettore di permutazione)N×Np

  1. implementare il proprio algoritmo in C come modulo di estensione (ma gli algoritmi sul posto sono difficili, almeno per me!)
  2. memoria overheadN

    for i in range(N):
        M[:,i] = M[p,i]
    for i in range(N):
        M[i,:] = M[i,p]
  3. sovraccarico di memoriaN2

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

Spero che questi hack non ottimali siano utili.


@none is hack 2. come si chiama "scambio manuale di righe e colonne"?
Stefano M,

1
O(N)p

O(N)O(N)

2
Questo è un ottimo canidate per una funzione cython. Non ci dovrebbero essere più di 10 righe. . . vuoi che ci provi?
meawoppl,

Lol. Ho iniziato a Cython questo, poi ho trovato la risposta giusta in una funzione che uso sempre. Doh. Vedi la mia risposta postata.
meawoppl,

6

Avvertenza: l'esempio seguente funziona correttamente, ma l' utilizzo dell'intero set di parametri suggerito alla fine del post espone un bug o almeno una "caratteristica non documentata" nella funzione numpy.take (). Vedi i commenti qui sotto per i dettagli. Segnalazione di bug archiviata .

Puoi farlo sul posto con la funzione take () di numpy , ma richiede un po 'di salto del cerchio.

Ecco un esempio di fare una permutazione casuale delle righe di una matrice di identità:

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.]])

Per farlo sul posto, tutto ciò che devi fare è specificare il parametro "out" in modo che sia lo stesso dell'array di input E devi impostare mode = "clip" o mode = "wrap". Se non imposti la modalità, verrà creata una copia per ripristinare lo stato dell'array su un'eccezione Python (vedi qui) .

In ultima analisi, take sembra essere un metodo array, quindi invece di

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

potresti chiamare

i.take(rr, axis=0)

se questo è più di tuo gusto. Quindi, in totale, la tua chiamata dovrebbe essere simile alla seguente:

#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")

Per permutare sia le righe che le colonne, penso che sia necessario eseguirlo due volte o tirare alcuni brutti shenanigans con numpy.unravel_index che mi fa male alla testa a cui pensare.


Come detto, gli algoritmi sul posto sono difficili. La tua soluzione non funziona con numpy 1.6.2. e 1.7.1 (righe / colonne duplicate). Non ho avuto il tempo di verificare se 1.8.x risolve questo problema
Stefano M,

Hmmm. Puoi pubblicare il codice di prova da qualche parte? Nella mia testa, ho la sensazione che ci debba essere una sorta di operazione sugli indici che avviene prima del pizzicamento. Investigherò di più questo PM.
meawoppl

1
Quando eseguo questo codice ottengo 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]. Quindi np.take, almeno per il numpy 1.6.2 non è consapevole di fare una permutazione sul posto e rovina le cose.
Stefano M,

Yeouch. Ben dimostrato. Questo probabilmente si qualifica come un bug IMHO. Per lo meno i documenti dovrebbero dire che input e output non possono essere lo stesso array, probabilmente controlla per vedere e tranne se lo è.
meawoppl,

Concordato sul bug: forse dovresti aggiungere una nota al tuo post per avvertire i lettori che la tua soluzione può produrre risultati sbagliati.
Stefano M,

2

Se hai una matrice sparsa archiviata in COOformato, potrebbe essere utile quanto segue

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

ACOOpermnumpy.arraymm


ma qual è il sovraccarico di memoria per l'archiviazione di una matrice piena densa come C00matrice sparsa al primo posto?
Federico Poloni,

intfloatfloatn2numpy.ndarray

1

Non ho abbastanza reputazione per commentare, ma penso che la seguente domanda SO possa essere utile: /programming/4370745/view-onto-a-numpy-array

I punti di base sono che è possibile utilizzare lo slicing di base e che creerà una vista sull'array senza copiare, ma se si esegue lo slicing / indicizzazione avanzati , verrà creata una copia.


L'OP chiede una permutazione, e ciò non è possibile con lo slicing di base.
Stefano M,

Hai ragione ovviamente. Ho pensato che sarebbe stato utile per l'OP capire cosa stava succedendo con lo slicing (nel caso in cui non lo sapessero) poiché erano preoccupati per quando sarebbero avvenute le copie. Se avesse usato qualcosa della tua risposta, penso che sarebbe bene saperlo dato che li usi all'interno dei tuoi loop.
avuto il

-1

Che dire

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


1
Questo costruisce un temporaneo, che è esattamente ciò che vuole evitare.
Michael Grant,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.