2015-12-21 19 views
5

Gli indici di una sezione slice(start, stop[, step]) possono essere spesso rappresentati da range(start, stop, step) (o range(*slice(start, stop, step).indices(length)) quando si prendono in considerazione le dimensioni sottostanti).Sezione che rappresenta le fette concatenate

Diciamo che ho anche due fette multidimensionali e la seconda fetta potrebbe essere usata come una fetta nel risultato dell'applicazione della prima fetta.

Esempio:

import numpy as np 
data = np.random.rand(*(100, 100, 100)) 
a = data[::2, 7, :] # slice 1, a.shape = (50,100) 
b = a[1, ::-1] # slice 2, b.shape = (100,) 

mi piacerebbe trovare un'espressione generale per il calcolo della sola fetta che fa lo stesso lavoro. Conosco le dimensioni della struttura dati sottostante.

c = data[2, 7, ::-1] # same as b 
np.array_equal(b, c) # True 

Così, a ottenere dal [::2, 7, :] e [1, ::-1]-[2, 7, ::-1] in questo esempio avrei bisogno di una funzione come:

def concatenate_slices(shape, outer_slice, inner_slice): 
    ... 
    return combined_slice 

dove outer_slice e inner_slice sarebbero entrambi una tupla di fette. Nell'esempio shape=(100, 100, 100) e outer_slice=(slice(None, None, 2), 7, slice(None, None, None)) e inner_slice=(1, slice(None, None, -1)).

Non sono sicuro di come farlo in modo efficiente.

I miei oggetti fanno qualcosa quando si chiama __getitem__(slice) (nessuna vista intermedia) e voglio farlo una sola volta ma ho ancora la possibilità di avere fette di fette.

Come estensione (facoltativo) vorrei sapere cosa succede se ho delle ellissi nelle sezioni. Come posso quindi creare la combinazione?

+2

solo per riferimento: 'range (start, stop, step)' non produce sempre gli indici corretti in quanto può anche produrre indici fuori intervallo, per creare un intervallo valido ['slice.indices()'] (https://docs.python.org/3/reference/datamodel.html?highlight=slice.indices#slice.indices) deve essere usato: 'range (* slice (start, stop, step) .indices (length)) '. Non sono sicuro se sia possibile combinare le sezioni senza prendere in considerazione la forma della struttura sottostante. – mata

+0

@mata Grazie. Hai ragione. Anche nel mio caso vorrei conoscere la forma della struttura sottostante. Ho modificato un po '. – Trilarion

+0

Immagino che si possa fare manualmente mettendo insieme una nuova fetta guardando i limiti dei primi due. Ho iniziato a scriverlo ma è troppo disordinato per i miei gusti, dovrebbe esserci un modo migliore (quindi ho rinunciato). –

risposta

1

Iniziamo con il caso semplice: array 1-d. Avremo bisogno di tenere traccia delle start, stop e step valori per la fetta finale, che possiamo aggiornare in questo modo:

def update_1d(a, b, length): 
    a_start, a_stop, a_step = a.indices(length) 
    a_length = len(xrange(a_start, a_stop, a_step)) 
    if a_length == 0: 
    # doesn't matter what b is if data[a] is [] 
    return a 
    b_start, b_stop, b_step = b.indices(a_length) 
    b_length = len(xrange(b_start, b_stop, b_step)) 
    if b_length == 0: 
    # result will be empty, so we can exit early 
    return slice(0, 0, 1) 
    # convert b's start into a's coordinates, without wrapping around 
    start = max(0, a_start + b_start * a_step) 
    # steps are multiplicative, which makes things easy 
    step = a_step * b_step 
    # the stop index is the hard part because it depends on the sign of both steps 
    x = a_start + b_stop * a_step 
    if step < 0: 
    # indexing backwards, so truncate if b's converted step goes below zero 
    stop = x if x >= 0 else None 
    elif a_step > 0: 
    # both steps are positive, so take the smallest stop index 
    stop = min(a_stop, x) 
    else: 
    # both steps are negative, so take the largest stop index 
    stop = max(a_stop, x) 
    return slice(start, stop, step) 

Si noti che questo si aspetta a e b per essere fette. Di solito, puoi convertire altre forme in oggetti fetta. Ciò include anche gli oggetti Ellipsis, supponendo che tu sappia quante dimensioni hai.

Per estendere questo caso multidimensionale, è necessario eseguire una contabilità per tenere traccia di quale dimensione originale viene suddivisa. Ad esempio, se si dispone di data[::2, 7, :][:, 2:-2], sarà necessario mappare la seconda dimensione della seconda porzione alla terza dimensione della prima porzione.

+0

Controlla questo caso: 'a = slice (-1, 1, -2)', 'b = slice (0, -1, 1)', 'length = 16'. –

+0

Grazie. Questo è già un buon inizio. len (xrange()) dovrebbe essere inefficiente Potrebbe invece usare floor ((stop - start)/step)? – Trilarion

+0

'len (xrange (...))' è abbastanza veloce, perché l'oggetto 'xrange' fa essenzialmente la stessa cosa che hai suggerito, ma nel codice C. Inoltre, Warren ha ragione, il codice che ho postato non funziona in tutti i casi. Resta sintonizzato per una versione migliore a breve. – perimosocordiae

1

Ho il sospetto che tu debba solo passare attraverso il tedio di un'analisi di ogni dimensione, per costruire una nuova sezione o una serie di indici. Dubito che ci sia una scorciatoia.

Per illustrare prendere il tuo esempio:

In [77]: shape=(100,100,100) 
In [78]: outer_slice=(slice(None, None, 2), 7, slice(None, None, None)) 
In [79]: inner_slice=(1, slice(None, None, -1)) 

L'obiettivo è (a destra?):

(2, 7, slice(None,None,-1)) 

prima dimensione - creare un array di tutta la gamma di indici, e affettateli in sequenza:

In [80]: idx=np.arange(shape[0]) 
In [81]: idx[outer_slice[0]][inner_slice[0]] 
Out[81]: 2 

Potrei dedurre che da [:: 2] e [1]? Devo ragionare che inizia a 0, la forma è abbastanza grande da produrre un secondo valore, ecc.

Ora per la 2a dimensione. Questo è uno scalare, quindi non c'è una fetta inner corrispondente.

In [82]: outer_slice[1] 
Out[82]: 7 

Per il terzo, facciamo lo stesso come con la prima, ma tenendo conto l'offset tra liste esterne ed interne:

In [83]: idx=np.arange(shape[2]) 
In [84]: idx[outer_slice[2]][inner_slice[1]] 
Out[84]: 
array([99, 98, 97, 96, 95, 94, 93, 92, 91, ....7, 6, 5, 4, 3, 2, 1, 0]) 

O potrei dedurre che outer_slice[2] non fa nulla, così ho possibile utilizzare direttamente inner_slice[1].

Ovviamente sarebbe altrettanto semplice ed efficiente applicare le due tuple di slice all'array effettivo.

X[outer_slice][inner_slice] 

Fintanto outer_slice produce una vista, combinandoli in una fetta composita non è molto di un miglioramento.

Con la forma e le tuple della fetta ci sono abbastanza informazioni per costruire una nuova tupla. Ma sembra che la logica richiesta sarà abbastanza coinvolta e richiederà una conoscenza approfondita dell'affettatura e molti test.

Problemi correlati