Permita uma matriz no local em numpy

27

Eu quero modificar uma matriz de transição quadrada densa no local, alterando a ordem de várias de suas linhas e colunas, usando a biblioteca numpy do python. Matematicamente, isso corresponde à pré-multiplicação da matriz pela matriz de permutação P e à pós-multiplicação por P ^ -1 = P ^ T, mas essa não é uma solução computacionalmente razoável.

No momento, estou trocando manualmente linhas e colunas, mas eu esperava que o numpy tivesse uma boa função f (M, v), onde M tem n linhas e colunas ev tem n entradas, para que f (M, v) seja atualizado M de acordo com a permutação de índice v. Talvez eu esteja apenas falhando ao pesquisar na internet.

Algo assim pode ser possível com a "indexação avançada" da numpy, mas meu entendimento é que essa solução não estaria em vigor. Também para algumas situações simples, pode ser suficiente rastrear apenas uma permutação de índice separadamente, mas isso não é conveniente no meu caso.

Adicionado:
Às vezes, quando as pessoas falam sobre permutações, elas significam apenas a amostragem de permutações aleatórias, por exemplo, como parte de um procedimento para obter valores de p em estatística. Ou eles significam contar ou enumerar todas as permutações possíveis. Eu não estou falando sobre essas coisas.

Adicionado:
a matriz é pequena o suficiente para caber na RAM da área de trabalho, mas grande o suficiente para que eu não queira copiá-la sem pensar. Na verdade, eu gostaria de usar matrizes tão grandes quanto possível, mas não quero lidar com o inconveniente de não poder mantê-las na RAM e faço O (N ^ 3) operações LAPACK na matriz, o que também limitar o tamanho da matriz prática. Atualmente, copio matrizes desse tamanho desnecessariamente, mas espero que isso possa ser facilmente evitado por permutação.

Nenhum
fonte
3
Seria bom se você pudesse atualizar a pergunta para fornecer o tamanho de suas matrizes. "Gigantesco" não significa a mesma coisa para todas as pessoas.
Bill Barth
2
Você está certo de que a indexação avançada (ou chamada fantasia) cria uma cópia. Mas se você aceitar viver com esse fato, seu código será apenas M[v]para permutar as linhas.
Daniel Velkov
@daniel: E seria M [v,:] [:, v] fazer toda a permutação? Essa seria a melhor maneira de obter a permutação usando indexação sofisticada? E usaria 3x a memória da matriz, incluindo o tamanho da matriz original, a matriz permeada linha + coluna e a matriz permeada linha temporária?
nenhuma
Está correto, você teria sua matriz original e duas cópias. Btw, por que você precisa permutar linhas e colunas ao mesmo tempo?
Daniel Velkov
4
O que você vai fazer com a matriz permutada? Pode ser melhor simplesmente permutar o vetor ao aplicar o operador.
Jed Brown

Respostas:

9

De acordo com os documentos, não existe um método de permutação no local em numpy, algo como ndarray.sort .

Portanto, suas opções são (assumindo que Mseja uma matriz e o vetor de permutação)N×Np

  1. implementar seu próprio algoritmo em C como um módulo de extensão (mas algoritmos no local são difíceis, pelo menos para mim!)
  2. NSobrecarga de memória

    for i in range(N):
        M[:,i] = M[p,i]
    for i in range(N):
        M[i,:] = M[i,p]
    
  3. N2Sobrecarga de memória

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

Espero que esses hacks abaixo do ideal sejam úteis.

Stefano M
fonte
@ ninguém é hack 2. o que você chama de 'troca manual de linhas e colunas'?
Stefano M
1
Eu combinaria as opções 1 e 2: escreva o código C que usa um buffer de ordem N para escrever cada coluna permutada e, em seguida, escreva-o de volta para a origem; faça o mesmo para as linhas. Como o @Stefano escreve, isso requer apenas memória extra, que você já está gastando para armazenar a permutação em primeiro lugar. pO(N)p
Erik P.
@ErikP. para uma implementação C, a memória extra é razoável e, com certeza, sua abordagem de gravação de dispersão para temp e cópia de retorno é sólida. A questão interessante, porém, é se existem algoritmos mais eficientes, dada a memória extra de . A resposta é difícil, eu acho, pois devemos levar em conta a arquitetura do processador, os padrões de acesso à memória, os acertos no cache, ... Isso dizia que eu seguiria seu conselho e adotaria um algoritmo simples e fácil de implementar. O ( N )O(N)O(N)
7308 Stefano M
2
Este é realmente um bom candidato para uma função cython. Não deve ter mais do que 10 linhas. . . quer que eu dê uma chance?
meawoppl 9/09/12
Ri muito. Comecei a Cython isso, então encontrei a resposta certa em uma função que eu uso o tempo todo. Doh. Veja minha resposta postada.
precisa saber é
6

Aviso: O exemplo abaixo funciona corretamente, mas o uso do conjunto completo de parâmetros sugeridos no final da publicação expõe um bug ou pelo menos um "recurso não documentado" na função numpy.take (). Veja os comentários abaixo para obter detalhes. Relatório de bug arquivado .

Você pode fazer isso no local com a função take () de numpy , mas isso requer um pouco de pulos de argola.

Aqui está um exemplo de como fazer uma permutação aleatória das linhas de uma matriz de identidade:

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 fazer isso no local, tudo o que você precisa fazer é especificar o parâmetro "out" para ser o mesmo da matriz de entrada E você deve definir o mode = "clip" ou mode = "wrap". Se você não definir o modo, ele fará uma cópia para restaurar o estado do array em uma exceção do Python (veja aqui) .

Em uma nota final, take parece ser um método de matriz, portanto, em vez de

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

você poderia ligar

i.take(rr, axis=0)

se isso é mais do seu gosto. Portanto, no total, você chama deve ser algo como o seguinte:

#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 linhas e colunas, acho que você precisa executá-lo duas vezes ou puxar algumas travessuras feias com numpy.unravel_index que machuca minha cabeça.

meawoppl
fonte
Como dito, algoritmos no local são difíceis. Sua solução não funciona com o numpy 1.6.2. e 1.7.1 (linhas / colunas duplicadas). Não tinha tempo para verificar se 1.8.x corrige esse problema
Stefano M
Hummm. Você pode postar código de teste em algum lugar? Na minha cabeça, sinto que precisa haver uma operação de classificação nos índices que acontece primeiro antes da depilação. Vou investigar mais este PM.
precisa saber é o seguinte
1
Quando eu executar este código eu recebo 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]. Portanto, np.takepelo menos para o numpy 1.6.2 não está ciente de fazer uma permutação no local e estraga tudo.
23414 Stefano M
Yeouch. Bem demonstrado. Isso provavelmente se qualifica como um bug IMHO. No mínimo, os documentos devem dizer que entrada e saída não podem ser da mesma matriz, provavelmente verifique para ver e, exceto se for.
18713
Concordou com o bug: talvez você deva adicionar uma nota à sua postagem para avisar os leitores que sua solução pode produzir resultados errados.
276 Stefano M
2

Se você possui uma matriz esparsa armazenada em COOformato, o seguinte pode ser útil

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

ACOOpermnumpy.arraymm

Vincent Traag
fonte
mas qual é a sobrecarga de memória para armazenar uma matriz densa completa como uma C00matriz esparsa em primeiro lugar?
Federico Poloni
intfloatfloatn2numpy.ndarray
1

Não tenho reputação suficiente para comentar, mas acho que a seguinte pergunta SO pode ser útil: /programming/4370745/view-onto-a-numpy-array

Os pontos básicos são que você pode usar o corte básico e que irá criar uma visão para a matriz sem copiar, mas se você fizer avançado corte / indexação em seguida, ele irá criar uma cópia.

arruinado
fonte
O OP está pedindo uma permutação, e isso não é possível com o fatiamento básico.
27613 Stefano M
Você está certo, é claro. Eu pensei que seria útil para o OP entender o que estava acontecendo com o fatiamento (caso eles não soubessem), pois estavam preocupados com quando as cópias estariam acontecendo. Se ele usou algo da sua resposta, acho que seria bom saber, já que você os usa dentro de seus loops.
hadsed
-1

Sobre o quê

minha matriz [:, [0, 1]] = minha matriz [:, [1, 0]]

johnsankey
fonte
1
Isso constrói um temporário, que é exatamente o que ele deseja evitar.
9788 Michael Jackson Grant