2015-12-21 19 views
5

Indeksy plasterka slice(start, stop[, step]) mogą być często reprezentowane przez range(start, stop, step) (lub range(*slice(start, stop, step).indices(length)) podczas uwzględniania wymiarów podrzędnych).Kromka przedstawiająca połączone plasterki

Załóżmy, że mam nawet dwa wielowymiarowe plasterki, a drugi plaster może zostać użyty jako plaster w wyniku zastosowania pierwszego plasterka.

Przykład:

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,) 

chciałbym znaleźć ogólne wyrażenie obliczania pojedynczy plasterek, który wykonuje tę samą pracę. Znam wymiary podstawowej struktury danych.

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

Więc w uzyskaniu od [::2, 7, :] i [1, ::-1] do [2, 7, ::-1] w tym przykładzie będę potrzebował funkcji takich jak:

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

gdzie outer_slice i inner_slice będzie zarówno być krotką plasterki. W przykładzie shape=(100, 100, 100) i outer_slice=(slice(None, None, 2), 7, slice(None, None, None)) i inner_slice=(1, slice(None, None, -1)).

Nie jestem pewien, jak to zrobić skutecznie.

Moje obiekty robią coś, gdy jest wywoływana __getitem__(slice) (brak pośrednich widoków) i chcę to zrobić tylko raz, ale nadal mam możliwość posiadania plasterków plasterków.

Jako rozszerzenie (opcjonalnie) chciałbym wiedzieć, co się stanie, jeśli mam elipsy w plasterkach. Jak mogę utworzyć kombinację?

+2

tylko dla odniesienia: 'zasięg (start, stop, step)' nie zawsze generuje poprawne indeksy, ponieważ może również generować indeksy poza zakresem, aby utworzyć poprawny zakres ['slice.indices()'] (https://docs.python.org/3/reference/datamodel.html?highlight=slice.indices#slice.indices) musi być użyty: 'range (* slice (start, stop, step) .indices (length)) '. Nie jestem pewien, czy możliwe jest łączenie plasterków bez uwzględnienia kształtu podstawowej struktury. – mata

+0

@mata Dzięki. Masz rację. Również w moim przypadku znałbym kształt podstawowej struktury. Edytowałem trochę. – Trilarion

+0

Sądzę, że można to zrobić ręcznie nakładając nowy plaster, patrząc na ograniczenia dwóch pierwszych. Zacząłem to pisać, ale jest zbyt brudny jak na mój gust, powinien być lepszy sposób (więc zrezygnowałem). –

Odpowiedz

1

Zacznijmy od prostego przypadku: tablice 1-d. Musimy śledzić start, stop i step wartości końcowego kawałek, który możemy aktualizować tak:

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) 

pamiętać, że ta spodziewa a i b aby być plastry. Zwykle możesz konwertować inne formy na obiekty typu slice. Obejmuje to nawet obiekty Ellipsis, zakładając, że wiesz, ile masz wymiarów.

Aby rozszerzyć to na wielowymiarowy przypadek, będziemy musieli wykonać pewne czynności księgowe, aby śledzić, który z oryginalnych wymiarów zostanie przycięty. Na przykład, jeśli masz data[::2, 7, :][:, 2:-2], będziesz musiał odwzorować drugi wymiar drugiego plasterka na trzeci wymiar pierwszego plasterka.

+0

Sprawdź tę sprawę: 'a = slice (-1, 1, -2)', 'b = slice (0, -1, 1)', 'length = 16'. –

+0

Dzięki. To już dobry początek. len (xrange()) powinno być nieefektywne Czy zamiast tego można użyć floor ((stop - start)/step)? – Trilarion

+0

'len (xrange (...))' jest w rzeczywistości dość szybki, ponieważ obiekt 'xrange' ma w zasadzie to samo, co sugerowałeś, ale w kodzie C. Ponadto, Warren ma rację, napisany przeze mnie kod nie działa we wszystkich przypadkach. Wkrótce otrzymasz lepszą wersję. – perimosocordiae

1

Podejrzewam, że po prostu musisz przejść przez nudę analizowania każdego wymiaru, aby zbudować nowy kawałek lub tablicę indeksów. Wątpię, czy jest skrót.

Aby zilustrować wziąć przykład:

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)) 

cel jest (z prawej?)

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

Aspekt - podjąć szereg całym zakresie wskaźników, a część z nich kolejno:

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

można wywnioskować, że w [:: 2] i [1]? Muszę rozumieć, że zaczyna się od 0, kształt jest wystarczająco duży, aby uzyskać 2. wartość, itd.

Teraz dla drugiego wymiaru. To jest skalar, więc nie ma odpowiadającego plasterka.

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

Na trzecim, zróbmy to samo jak w przypadku 1, ale biorąc pod uwagę przesunięcia pomiędzy zewnętrznymi i wewnętrznymi list:

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]) 

Lub mogę wywnioskować, że outer_slice[2] nic nie robi, więc mogą bezpośrednio używać inner_slice[1].

Oczywiście równie łatwe i efektywne byłoby zastosowanie dwóch krotek krotności do rzeczywistej tablicy.

X[outer_slice][inner_slice] 

Dopóki outer_slice daje pogląd, łącząc je w kompozytowym wycinka nie jest wiele do poprawy.

Przy kształcie i krotkach krotności jest wystarczająco dużo informacji, aby zbudować nową krotkę. Wydaje się jednak, że wymagana logika będzie dość zaangażowana i wymagać dogłębnej wiedzy o krojeniu i wielu testach.

Powiązane problemy