Wycinanie tablicy NumPy 2d, czyli jak wyodrębnić submatrix mxm z tablicy nxn (n>m)?

Chcę pokroić tablicę NumPy nxn. Chcę wyodrębnić dowolny wybór wierszy m i kolumn tej tablicy (tzn. bez żadnego wzorca w liczbie wierszy/kolumn), tworząc nową tablicę mxm. W tym przykładzie powiedzmy, że tablica jest 4x4 i chcę wyodrębnić z niej tablicę 2x2.

Oto nasza tablica:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

Linia i kolumny do usunięcia są takie same. Najprostszym przypadkiem jest, gdy chcę wyodrębnić submatrix 2x2, który jest na początku lub na końcu, tj.:

In [33]: x[0:2,0:2]
Out[33]: 
array([[0, 1],
       [4, 5]])

In [34]: x[2:,2:]
Out[34]: 
array([[10, 11],
       [14, 15]])

Ale co jeśli muszę usunąć kolejną mieszankę wierszy/kolumn? Co zrobić, jeśli muszę usunąć pierwszą i trzecią linię / wiersze, wydobywając w ten sposób submatrix [[5,7],[13,15]]? Może istnieć Dowolna kompozycja wierszy/linii. Czytałem gdzieś, że po prostu muszę indeksować moją tablicę za pomocą tablic/list indeksów zarówno dla wierszy, jak i kolumn, ale to chyba nie działa:

In [35]: x[[1,3],[1,3]]
Out[35]: array([ 5, 15])

Znalazłem jeden sposób, czyli:

    In [61]: x[[1,3]][:,[1,3]]
Out[61]: 
array([[ 5,  7],
       [13, 15]])

Pierwszy problem z tym jest to, że jest mało czytelny, chociaż mogę żyj z tym. Jeśli ktoś ma lepsze rozwiązanie, z pewnością chętnie posłucham.

Inną rzeczą jest to, że czytałem na forum , że indeksowanie tablic tablicami zmusza NumPy do zrobienia kopii żądanej tablicy, więc podczas leczenia dużymi tablicami może to być problem. Dlaczego tak jest / jak działa ten mechanizm?

Author: Alex Riley, 2010-11-23

7 answers

Jak wspomniał Sven, x[[[0],[2]],[1,3]] zwróci wiersze 0 i 2, które pasują do kolumn 1 i 3, podczas gdy x[[0,2],[1,3]] zwróci wartości x[0,1] i x [2,3] w tablicy.

Jest pomocna funkcja do wykonania pierwszego przykładu, który podałem, numpy.ix_. Możesz zrobić to samo, co mój pierwszy przykład z x[numpy.ix_([0,2],[1,3])]. Dzięki temu nie będziesz musiał wpisywać wszystkich dodatkowych nawiasów.

 46
Author: Justin Peel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-23 16:07:25

Aby odpowiedzieć na to pytanie, musimy przyjrzeć się jak indeksowanie tablicy wielowymiarowej działa w Numpy. Powiedzmy, że masz tablicę x z twojego pytania. Bufor przypisany do x będzie zawierał 16 rosnących liczb całkowitych od 0 do 15. Jeśli uzyskasz dostęp do jednego elementu, powiedzmy x[i,j], NumPy musi określić położenie pamięci tego elementu względem początku bufora. Jest to wykonywane przez obliczenie w efekcie i*x.shape[1]+j (i pomnożenie przez wielkość int, aby uzyskać rzeczywistą pamięć offset).

Jeśli wyodrębnisz subarray przez podstawowe krojenie, takie jak y = x[0:2,0:2], wynikowy obiekt będzie współdzielił bufor bazowy z x. Ale co się stanie, jeśli uzyskasz dostęp y[i,j]? NumPy nie może użyć i*y.shape[1]+j do obliczenia przesunięcia do tablicy, ponieważ dane należące do {[12] } nie są kolejne w pamięci.

NumPy rozwiązuje ten problem wprowadzając strides. Przy obliczaniu przesunięcia pamięci dla dostępu x[i,j], to, co jest faktycznie obliczane, to i*x.strides[0]+j*x.strides[1] (i to zawiera już czynnik wielkości int):

x.strides
(16, 4)

Gdy y jest ekstrahowany jak powyżej, NumPy nie tworzy nowego bufora, ale tworzy nowy obiekt tablicy odwołujący się do tego samego bufora (w przeciwnym razie y będzie równe x.) Nowy obiekt array będzie miał inny kształt niż x i może inne przesunięcie początkowe do bufora, ale będzie współdzielił kroki z x (w tym przypadku przynajmniej):

y.shape
(2,2)
y.strides
(16, 4)

W ten sposób, obliczając przesunięcie pamięci dla y[i,j] da poprawny wynik.

Ale co NumPy powinien zrobić dla czegoś takiego z=x[[1,3]]? Mechanizm strides nie pozwala na poprawne indeksowanie, jeśli oryginalny bufor jest używany dla z. NumPy teoretycznie mógłby dodać jakiś bardziej wyrafinowany mechanizm niż strides, ale to uczyniłoby dostęp do elementów stosunkowo kosztownym, w jakiś sposób zaprzeczając całej idei tablicy. Ponadto widok nie byłby naprawdę lekkim obiektem już nie.

Jest to szczegółowo omówione w dokumentacji NumPy na temat indeksowania .

I prawie zapomniałem o twoim pytaniu: oto jak sprawić, by indeksowanie z wieloma listami działało zgodnie z oczekiwaniami:]}
x[[[1],[3]],[1,3]]

Dzieje się tak dlatego, że tablice indeksowe są nadawane do wspólnego kształtu. Oczywiście, dla tego konkretnego przykładu, można również zrobić z podstawowego krojenia: {]}

x[1::2, 1::2]
 102
Author: Sven Marnach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-28 09:14:45

Nie wydaje mi się, aby x[[1,3]][:,[1,3]] było mało czytelne. Jeśli chcesz być bardziej jasny w swoich intencjach, możesz to zrobić:

a[[1,3],:][:,[1,3]]

Nie jestem ekspertem w krojeniu, ale zazwyczaj, jeśli spróbujesz pokroić w tablicę i wartości są ciągłe, otrzymasz widok, w którym wartość kroku zostanie zmieniona.

Np. w wejściach 33 i 34, mimo że otrzymujesz tablicę 2x2, krok wynosi 4. Tak więc, gdy indeksujesz następny wiersz, wskaźnik przesuwa się do właściwej pozycji w pamięci.

Najwyraźniej to mechanizm nie przenosi się dobrze do przypadku tablicy wskaźników. Dlatego numpy będzie musiał wykonać kopię. W końcu wiele innych funkcji matematycznych macierzy opiera się na wielkości, kroku i ciągłej alokacji pamięci.

 11
Author: Dat Chu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-10 23:37:21

Jeśli chcesz pominąć co drugi wiersz i co drugą kolumnę, możesz to zrobić za pomocą podstawowego krojenia:

In [49]: x=np.arange(16).reshape((4,4))
In [50]: x[1:4:2,1:4:2]
Out[50]: 
array([[ 5,  7],
       [13, 15]])

To zwraca widok, a nie kopię tablicy.

In [51]: y=x[1:4:2,1:4:2]

In [52]: y[0,0]=100

In [53]: x   # <---- Notice x[1,1] has changed
Out[53]: 
array([[  0,   1,   2,   3],
       [  4, 100,   6,   7],
       [  8,   9,  10,  11],
       [ 12,  13,  14,  15]])

Podczas gdy z=x[(1,3),:][:,(1,3)] używa zaawansowanego indeksowania i w ten sposób zwraca kopię:

In [58]: x=np.arange(16).reshape((4,4))
In [59]: z=x[(1,3),:][:,(1,3)]

In [60]: z
Out[60]: 
array([[ 5,  7],
       [13, 15]])

In [61]: z[0,0]=0

Zauważ, że {[5] } jest bez zmian:

In [62]: x
Out[62]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

Jeśli chcesz wybrać dowolne wiersze i kolumny, nie możesz użyć podstawowego krojenia. Będziesz musiał użyć zaawansowanego indeksowania, używając czegoś w rodzaju x[rows,:][:,columns], Gdzie rows i columns są sekwencje. To oczywiście da ci kopię, a nie Widok, oryginalnej tablicy. Jest to tak, jak należy się spodziewać, ponieważ tablica numpy używa sąsiedniej pamięci (ze stałymi krokami) i nie byłoby sposobu na wygenerowanie widoku z dowolnymi wierszami i kolumnami (ponieważ wymagałoby to niestałych kroków).

 9
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-24 11:50:51

Z numpy, możesz przekazać kawałek dla każdego składnika indeksu - więc twój x[0:2,0:2] Przykład powyżej działa.

Jeśli chcesz po prostu równomiernie pominąć kolumny lub wiersze, możesz przekazać plasterki z trzema składnikami (tj. start, stop, step).

Jeszcze raz, dla Twojego przykładu powyżej:

>>> x[1:4:2, 1:4:2]
array([[ 5,  7],
       [13, 15]])

Czyli w zasadzie: wycinamy w pierwszym wymiarze, rozpoczynając od indeksu 1, zatrzymujemy, gdy indeks jest równy lub większy niż 4 i dodajemy 2 do indeksu w każdym przebiegu. To samo dotyczy drugiego wymiaru. Again: to działa tylko dla stałych kroków.

Składnia, którą musisz zrobić zupełnie inaczej wewnętrznie-to, co x[[1,3]][:,[1,3]] faktycznie robi, to tworzenie nowej tablicy zawierającej tylko wiersze 1 i 3 z oryginalnej tablicy (wykonanej z częścią x[[1,3]]), a następnie przekrój ją ponownie-tworząc trzecią tablicę - zawierającą tylko kolumny 1 i 3 poprzedniej tablicy.

 5
Author: jsbueno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-17 18:06:03

Mam podobne pytanie tutaj: pisanie w sub-ndarray ndarray w najbardziej pythoński sposób. Python 2 .

Po rozwiązaniu poprzedniego postu dla twojej sprawy rozwiązanie wygląda następująco:

columns_to_keep = [1,3] 
rows_to_keep = [1,3]

Użycie ix_:

x[np.ix_(rows_to_keep, columns_to_keep)] 

Czyli:

array([[ 5,  7],
       [13, 15]])
 2
Author: Rafael Valero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-29 10:37:40

Nie jestem pewien, jak wydajne to jest, ale możesz użyć range (), aby przeciąć obie oś

 x=np.arange(16).reshape((4,4))
 x[range(1,3), :][:,range(1,3)] 
 0
Author: Valery Marcel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-26 05:32:49