2013-07-19 16 views
14

Ho una matrice numpy e voglio ottenere il "vicinato" del punto in questione. Di solito gli array che sto usando sono bidimensionali, ma il seguente esempio 1D illustra quello che sto cercando. Sewrapping around slices in Python/numpy

A = numpy.array([0,10,20,30,40,50,60,70,80,90]) 

Poi il (misura 5) quartiere di elemento 4 è [20,30,40,50,60], e questo può essere facilmente ottenuto facendo A[i-2:i+3].

Tuttavia, ho anche bisogno che i quartieri "avvolgano" i bordi dell'array, in modo che il vicinato dell'elemento 0 sia [80,90,0,10,20] e il vicinato dell'elemento 9 sia [70,80,90,0,10]. Non riesco a trovare un modo elegante per farlo, quindi finisco per dover usare una logica complicata e fastidiosa ogni volta che viene fuori (che è molto spesso per me). Nel caso 2D il vicinato di un punto sarebbe un array rettangolare.

Quindi la mia domanda è, c'è un modo pulito per esprimere questa operazione di "quartiere avvolgente" in numpy? Preferirei qualcosa che restituisca una porzione piuttosto che una copia, ma la leggibilità e la velocità sono le considerazioni più importanti.

+2

nota che è fondamentalmente impossibile ottenere una visione di tale sottotrama in numpy; il sottoarray non può essere espresso usando una singola falcata per ciascun asse. –

risposta

21

numpy.take nella modalità 'wrap' utilizzerà gli indici modulo la lunghezza della matrice.

indices = range(i-2,i+3) 
neighbourhood = A.take(indices, mode='wrap') 

vedere la documentazione per i dettagli numpy.take

+3

Beat me di 10 secondi. Hmmph! Si noti che è anche possibile accedere a 'take' come un metodo array, ad esempio' A.take (indici, mode = 'wrap') '. – DSM

+0

Grazie a @DSM, modificato perché in questo caso ha più senso utilizzare il metodo dell'istanza. – Henrik

+0

Ah. Ho dimenticato di menzionare che il mio array è bidimensionale: ho appena usato un array 1D per chiarire il problema. – Nathaniel

5

è possibile utilizzare l'argomento axis=0 di numpy.take per n-d array.

A = zip(range(0,101,10),range(0,11)) #create 2-d list 
A = numpy.array(A) #create 2-d array 
indices = range(i-2,i+3) 
neightbourhood = A.take(indices,axis=0,mode='wrap') 

Lo stesso axis=0 lavorerà per N * m dimensioni ...

3

So che questa domanda è vecchio, ma dovrebbe parlare scipy.ndimage.filter.generic_filter.

Ha un'opzione mode='wrap' e in più gestisce l'applicazione della funzione adiacente.

import scipy.ndimage as nd 

A = np.array([0,10,20,30,40,50,60,70,80,90]) 

Diciamo che avete una funzione vicino di casa:

def nbf(arr): 
    return sum(arr) 

Per applicare la funzione vicina ad ogni 5, con valori avvolti ai bordi:

C = nd.generic_filter(A, nbf, 5, mode='wrap') 

print(C) 
[200 150 100 150 200 250 300 350 300 250] 
1

È possibile utilizzare il np. pad routine come questa:

A = np.array([0,10,20,30,40,50,60,70,80,90]) 
A = np.pad(A, 2, 'wrap') 
print(A) 
[80, 90, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 0, 10] 

Diciamo che avete una funzione vicino di casa:

def nbf(arr): 
    return sum(arr) 

Per applicare la funzione vicina ad ogni 5 è necessario essere attenti circa i vostri indici di inizio e fine (nel range (...) di comando) e la relativa fetta voi prendere da A.

B = [nbf(A[i-2:i+3]) for i in range(2,12)] 
print(B) 
[200, 150, 100, 150, 200, 250, 300, 350, 300, 250] 
1

numpy.roll può spostare la matrice in modo tale che l'intera fetta è all'inizio dell'array. Quindi prendi la tua fetta all'inizio e numpy.roll di nuovo per riportare l'array alla sua posizione originale.

# modify array at index i and nearest two 
# locations on each side of i, wrapping 
# around the edges 
A = np.array([0,10,20,30,40,50,60,70,80,90]) 
i = 9 
neighbors = 2 
A=np.roll(A, -i+neighbors) 
A[:5] += 1 
A=np.roll(A, i-neighbors) 

array([ 1, 11, 20, 30, 40, 50, 60, 71, 81, 91]) 

numpy.roll tuttavia non funziona bene su array di grandi dimensioni.

2

Nota: Per i casi in cui i tuoi vicini non richiedono confezionamento, numpy.take è più lento che semplicemente prendendo una fetta A[i-2:i+3]. Si consiglia di avvolgere la funzione di vicini di casa con alcune istruzioni condizionali:

def neighbors(a,i,n): 
    N = a.shape[0] 
    if i - n < 0 and i + n > 0: 
     indices = range(i-n,i+n+1) 
     nbrs = a.take(indices, mode='wrap') 
    elif i-n < N - 1 and i+n > N - 1: 
     indices = range(i-n,i+n+1) 
     nbrs = a.take(indices, mode='wrap') 
    else: 
     nbrs = a[i-n:i+n+1] 
    return nbrs 

Se vi trovate a prendere i vicini, mentre l'iterazione attraverso una serie, come in una media mobile centrata, troverete che questo richiede meno tempo, in particolare per array più lunghi:

enter image description here

Ecco la funzione media mobile ho usato:

def moving_average(a,n=1): 
    N = a.shape[0] 
    ma = np.empty(N) 
    for i in range(N): 
     if n*2+1 > N: 
      ma[i] = a.mean() 
     else: 
      ma[i] = neighbors(a,i,n).mean() 
    return ma 

I Sono sicuro che queste funzioni possono essere ulteriormente migliorate. Sono aperto a suggerimenti.