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