Strana indicizzazione usando numpy


27

Ho una variabile, x, che è della forma (2,2,50,100).

Ho anche un array, y, che equivale a np.array ([0,10,20]). Una cosa strana accade quando indicizzo x [0,:,:, y].

x = np.full((2,2,50,100),np.nan)
y = np.array([0,10,20])
print(x.shape)
(2,2,50,100)
print(x[:,:,:,y].shape)
(2,2,50,3)
print(x[0,:,:,:].shape)
(2,50,100)
print(x[0,:,:,y].shape)
(3,2,50)

Perché l'ultimo output (3,2,50) e non (2,50,3)?


Sono nuovo di intorpidire, quindi non ho una risposta alla tua domanda. Per approfondire ulteriormente, suggerisco di trovare un esempio più piccolo che sia solo 2D o 3D ed è come al massimo 10 elementi su qualsiasi asse.
Apprendista di codice il

Risposte:


21

Ecco come numpy usa l'indicizzazione avanzata per trasmettere forme di array. Quando si passa a 0per il primo indice e yper l'ultimo indice, numpy trasmetterà 0la stessa forma di y. Il seguente equivalenza vale: x[0,:,:,y] == x[(0, 0, 0),:,:,y]. ecco un esempio

import numpy as np

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

np.equal(x[0,:,:,y], x[(0, 0, 0),:,:,y]).all()
# returns:
True

Ora, poiché stai effettivamente passando in due set di indici, stai usando l'API di indicizzazione avanzata per formare (in questo caso) coppie di indici.

x[(0, 0, 0),:,:,y])

# equivalent to
[
  x[0,:,:,y[0]], 
  x[0,:,:,y[1]], 
  x[0,:,:,y[2]]
]

# equivalent to
rows = np.array([0, 0, 0])
cols = y
x[rows,:,:,cols]

# equivalent to
[
  x[r,:,:,c] for r, c in zip(rows, columns)
]

Che ha una prima dimensione uguale alla lunghezza di y. Questo è quello che stai vedendo.

Ad esempio, guarda un array con 4 dimensioni che sono descritte nel prossimo pezzo:

x = np.arange(120).reshape(2,3,4,5)
y = np.array([0,2,4])

# x looks like:
array([[[[  0,   1,   2,   3,   4],    -+      =+
         [  5,   6,   7,   8,   9],     Sheet1  |
         [ 10,  11,  12,  13,  14],     |       |
         [ 15,  16,  17,  18,  19]],   -+       |
                                                Workbook1
        [[ 20,  21,  22,  23,  24],    -+       |
         [ 25,  26,  27,  28,  29],     Sheet2  |
         [ 30,  31,  32,  33,  34],     |       |
         [ 35,  36,  37,  38,  39]],   -+       |
                                                |
        [[ 40,  41,  42,  43,  44],    -+       |
         [ 45,  46,  47,  48,  49],     Sheet3  |
         [ 50,  51,  52,  53,  54],     |       |
         [ 55,  56,  57,  58,  59]]],  -+      =+


       [[[ 60,  61,  62,  63,  64],
         [ 65,  66,  67,  68,  69],
         [ 70,  71,  72,  73,  74],
         [ 75,  76,  77,  78,  79]],

        [[ 80,  81,  82,  83,  84],
         [ 85,  86,  87,  88,  89],
         [ 90,  91,  92,  93,  94],
         [ 95,  96,  97,  98,  99]],

        [[100, 101, 102, 103, 104],
         [105, 106, 107, 108, 109],
         [110, 111, 112, 113, 114],
         [115, 116, 117, 118, 119]]]])

x ha una forma sequenziale davvero facile da capire che ora possiamo usare per mostrare cosa sta succedendo ...

La prima dimensione è come avere 2 cartelle di lavoro Excel, la seconda dimensione è come avere 3 fogli in ciascuna cartella di lavoro, la terza dimensione è come avere 4 righe per foglio e l'ultima dimensione è 5 valori per ogni riga (o colonne per foglio).

Guardandolo in questo modo, chiedendo x[0,:,:,0], è il detto: "nella prima cartella di lavoro, per ogni foglio, per ogni riga, dammi il primo valore / colonna".

x[0,:,:,y[0]]
# returns:
array([[ 0,  5, 10, 15],
       [20, 25, 30, 35],
       [40, 45, 50, 55]])

# this is in the same as the first element in:
x[(0,0,0),:,:,y]

Ma ora con l'indicizzazione avanzata, possiamo pensare x[(0,0,0),:,:,y]a "nella prima cartella di lavoro, per ogni foglio, per ogni riga, dammi il yvalore th / colonna. Ok, ora fallo per ogni valore di y"

x[(0,0,0),:,:,y]
# returns:
array([[[ 0,  5, 10, 15],
        [20, 25, 30, 35],
        [40, 45, 50, 55]],

       [[ 2,  7, 12, 17],
        [22, 27, 32, 37],
        [42, 47, 52, 57]],

       [[ 4,  9, 14, 19],
        [24, 29, 34, 39],
        [44, 49, 54, 59]]])

Dove impazzisce è che numpy trasmetterà per abbinare le dimensioni esterne della matrice di indice. Quindi, se si desidera eseguire la stessa operazione di cui sopra, ma per ENTRAMBI le "cartelle di lavoro di Excel", non è necessario eseguire il ciclo e concatenare. Puoi semplicemente passare un array alla prima dimensione, ma esso DEVE avere una forma compatibile.

Il passaggio di un numero intero viene trasmesso a y.shape == (3,). Se si desidera passare un array come primo indice, solo l'ultima dimensione dell'array deve essere compatibile con y.shape. Vale a dire, l'ultima dimensione del primo indice deve essere 3 o 1.

ix = np.array([[0], [1]])
x[ix,:,:,y].shape
# each row of ix is broadcast to length 3:
(2, 3, 3, 4)

ix = np.array([[0,0,0], [1,1,1]])
x[ix,:,:,y].shape
# this is identical to above:
(2, 3, 3, 4)

ix = np.array([[0], [1], [0], [1], [0]])
x[ix,:,:,y].shape
# ix is broadcast so each row of ix has 3 columns, the length of y
(5, 3, 3, 4)

Ho trovato una breve spiegazione nei documenti: https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#combining-advanced-and-basic-indexing


Modificare:

Dalla domanda originale, per ottenere una riga del sottotitolo desiderato, è possibile utilizzare x[0][:,:,y]:

x[0][:,:,y].shape
# returns
(2, 50, 3)

Tuttavia, se stai cercando di assegnare a questi sottotitoli, è necessario fare molta attenzione a guardare una vista di memoria condivisa dell'array originale. In caso contrario, l'assegnazione non sarà dell'array originale, ma di una copia.

La memoria condivisa si verifica solo quando si utilizza un numero intero o una porzione per sottoinsieme dell'array, ovvero x[:,0:3,:,:]o x[0,:,:,1:-1].

np.shares_memory(x, x[0])
# returns:
True

np.shares_memory(x, x[:,:,:,y])
# returns:
False

Sia nella tua domanda originale che nel mio esempio y non è né un int né una sezione, quindi finirò sempre per assegnare una copia dell'originale.

MA! Perché il vostro array per ypuò essere espressa come una fetta, è POSSIBILE effettivamente ottenere una visione assegnabile della matrice tramite:

x[0,:,:,0:21:10].shape
# returns:
(2, 50, 3)

np.shares_memory(x, x[0,:,:,0:21:10])
# returns:
True

# actually assigns to the original array
x[0,:,:,0:21:10] = 100

Qui usiamo la sezione 0:21:10per afferrare ogni indice in cui si troverebbe range(0,21,10). Dobbiamo usare 21e non 20perché il punto di arresto è escluso dalla sezione, proprio come nel filerange funzione.

Quindi, fondamentalmente, se riesci a costruire una sezione che si adatta ai tuoi criteri di sottotitolo, puoi fare un incarico.


4

Si chiama combining advanced and basic indexing. Incombining advanced and basic indexing , numpy esegue prima l'indicizzazione nell'indicizzazione avanzata e sottospazio / concatena il risultato alla dimensione dell'indicizzazione di base.

Esempio da documenti:

Sia x.shape essere (10,20,30,40,50) e supponiamo che ind_1 e ind_2 possano essere trasmessi alla forma (2,3,4). Quindi x [:, ind_1, ind_2] ha forma (10,2,3,4,40,50) perché il sottospazio a forma di (20,30) di X è stato sostituito con il sottospazio di (2,3,4) di gli indici. Tuttavia, x [:, ind_1,:, ind_2] ha forma (2,3,4,10,30,50) perché non esiste un posto inequivocabile da rilasciare nel sottospazio di indicizzazione, quindi è fissato all'inizio . È sempre possibile utilizzare .transpose () per spostare il sottospazio ovunque desiderato. Si noti che questo esempio non può essere replicato utilizzando take.

così, su x[0,:,:,y], 0e yindicizzazione anticipata. Sono trasmessi insieme per dare dimensione (3,).

In [239]: np.broadcast(0,y).shape
Out[239]: (3,)

Questo (3,) adatta all'inizio della seconda e terza dimensione da realizzare(3, 2, 50)

Per vedere che il 1 ° e ultima dimensione realmente stanno trasmettendo insieme, si può provare il cambiamento 0per [0,1]vedere l'errore di trasmissione

print(x[[0,1],:,:,y])

Output:
IndexError                                Traceback (most recent call last)
<ipython-input-232-5d10156346f5> in <module>
----> 1 x[[0,1],:,:,y]

IndexError: shape mismatch: indexing arrays could not be broadcast together with
 shapes (2,) (3,)
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.