2016-03-10 31 views
5

Supponiamo a qualche 1d numppy.array con n elementi:array 2D Di Tutti spostamenti ciclici di una matrice 1D

a = np.array([a_0, a_1, ..., a_n_minus_1]) 

Vorrei generare il 2d (n X n) numpy.array contenente, alla riga i, il i esimo spostamento ciclico di a:

np.array([[a_0, a_1, ..., a_n_minus_1], [a_n_minus_1, a_0, a_1, ...], ...]]) 

preferibilmente senza anelli. Come può essere fatto in modo efficiente?

(La funzione np.roll sembra legato, ma a quanto pare vuole solo uno scalare shift.)

+0

Il trucco per fare questo in modo efficace è quello di utilizzare diagonali. Ad esempio, la diagonale principale sarà tutto il primo elemento, ecc. –

+0

@JoeKington Way cool, grazie! IIUC, intendi utilizzare il fatto che un cambiamento ciclico può essere pensato come una trasformazione lineare, sì? –

+0

@JoeKington In realtà, suona molto bene, ma non riesco a capirlo - la generazione delle matrici che eseguono le diverse trasformazioni lineari sarebbe molto costosa, no? –

risposta

9

in realtà si sta costruendo una matrice circolanti. Basta usare lo scipy circulant function. Attenzione, perché si deve passare nella prima colonna verticale, non prima fila:

from scipy.linalg import circulant 
circulant([1,4,3,2] 
> array([[1, 2, 3, 4], 
     [4, 1, 2, 3], 
     [3, 4, 1, 2], 
     [2, 3, 4, 1]] 

Per riferimento, matrici circolanti sono molto molto bello properties.

2

Se si vuole farlo a mano basta usare np.tile:

import numpy as np 
a = np.array([1,2,3]) 

replicarlo con piastrelle, ma una volta di più di quanto effettivamente necessario per ottenere il "shift" voluto

b = np.tile(a, a.size+1) 
# [1 2 3 1 2 3 1 2 3 1 2 3] 

Poi rimodellare esso quindi è una matrice 2D di forma (a, a+1)

b.reshape(a.size, a.size+1) 
#[[1 2 3 1] 
# [2 3 1 2] 
# [3 1 2 3]] 

Ok, t cappello era solo un passo di debug per vedere cosa stava succedendo. Ma se lo vedi, sai che devi solo cancellare l'ultima colonna:

b.reshape(a.size, a.size+1)[:,:-1] 

E poi hai il risultato desiderato.


Questo può anche essere generalizzati per consentire (quasi) turni arbitrari:

shift = 3 
a = np.array([...]) 
b = np.tile(a, a.size+shift) 
res = b.reshape(a.size, a.size+shift)[:,:-shift]