2015-06-18 19 views
20

Zajrzałem do dokumentacji, a także innych pytań tutaj, ale wydaje mi się, że nie mam jeszcze zawieszonego podzbioru w tablicach numpy.Podział dwuwymiarowej tablicy numerycznej

mam numpy tablicę, i dla dobra argumentacji, niech będzie zdefiniowany następująco:

import numpy as np 
a = np.arange(100) 
a.shape = (10,10) 
# array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 
#  [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], 
#  [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 
#  [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], 
#  [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], 
#  [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]]) 

teraz chcę wybrać wiersze i kolumny a określone przez wektory n1 i n2. Jako przykład:

n1 = range(5) 
n2 = range(5) 

ale gdy używać:

b = a[n1,n2] 
# array([ 0, 11, 22, 33, 44]) 

tylko wtedy pierwsze piąte elementy przekątnej są wybrane, nie cały blok 5x5. Rozwiązanie, które znalazłem, to zrobić tak:

b = a[n1,:] 
b = b[:,n2] 
# array([[ 0, 1, 2, 3, 4], 
#  [10, 11, 12, 13, 14], 
#  [20, 21, 22, 23, 24], 
#  [30, 31, 32, 33, 34], 
#  [40, 41, 42, 43, 44]]) 

Ale jestem pewien, że powinien istnieć sposób na wykonanie tego prostego zadania za pomocą tylko jednego polecenia.

Odpowiedz

17

Dostałeś garść ładnych przykładów, jak robić to, co chcesz. Warto jednak zrozumieć, co się dzieje i dlaczego wszystko działa tak, jak oni. Istnieje kilka prostych zasad, które pomogą ci w przyszłości.

Istnieje duża różnica pomiędzy "fantazyjnym" indeksowaniem (tj. Za pomocą listy/sekwencji) i "normalnym" indeksowaniem (za pomocą plastra). Podstawowy powód ma związek z tym, czy tablica może być "regularnie kroczona", a zatem czy należy wykonać kopię. Arbitralne sekwencje muszą więc być traktowane inaczej, jeśli chcemy mieć możliwość tworzenia "widoków" bez wykonywania kopii.

W twoim przypadku:

import numpy as np 

a = np.arange(100).reshape(10,10) 
n1, n2 = np.arange(5), np.arange(5) 

# Not what you want 
b = a[n1, n2] # array([ 0, 11, 22, 33, 44]) 

# What you want, but only for simple sequences 
# Note that no copy of *a* is made!! This is a view. 
b = a[:5, :5] 

# What you want, but probably confusing at first. (Also, makes a copy.) 
# np.meshgrid and np.ix_ are basically equivalent to this. 
b = a[n1[:,None], n2[None,:]] 

Fancy indeksowania z 1D sekwencji jest w zasadzie równoważne skompresowanie ich razem i indeksowanie z wyniku.

print "Fancy Indexing:" 
print a[n1, n2] 

print "Manual indexing:" 
for i, j in zip(n1, n2): 
    print a[i, j] 

Jednakże, jeśli jesteś indeksowania sekwencje z meczu wymiarowości tablicy jesteś indeksowania (w 2D, w tym przypadku), Indeksowanie jest traktowany inaczej. Zamiast "zipowania dwóch razem", numpy używa indeksów jak maski.

Innymi słowy, a[[[1, 2, 3]], [[1],[2],[3]]] jest traktowane zupełnie inaczej niż a[[1, 2, 3], [1, 2, 3]], ponieważ sekwencje/tablice, które przechodzisz, są dwuwymiarowe.

In [4]: a[[[1, 2, 3]], [[1],[2],[3]]] 
Out[4]: 
array([[11, 21, 31], 
     [12, 22, 32], 
     [13, 23, 33]]) 

In [5]: a[[1, 2, 3], [1, 2, 3]] 
Out[5]: array([11, 22, 33]) 

być nieco dokładniej

a[[[1, 2, 3]], [[1],[2],[3]]] 

traktuje się tak samo jak:

i = [[1, 1, 1], 
    [2, 2, 2], 
    [3, 3, 3]]) 
j = [[1, 2, 3], 
    [1, 2, 3], 
    [1, 2, 3]] 
a[i, j] 

Innymi słowy, czy sygnał wejściowy jest wektorem wiersz/kolumna skrót informacji o tym, jak indeksy powinny się powtarzać w indeksowaniu.


np.meshgrid i np.ix_ tylko convienent sposoby, aby włączyć sekwencje 1D w ich wersjach 2D do indeksowania:

In [6]: np.ix_([1, 2, 3], [1, 2, 3]) 
Out[6]: 
(array([[1], 
     [2], 
     [3]]), array([[1, 2, 3]])) 

Podobnie (the sparse argumentem stałaby się identyczne ix_ powyżej):

In [7]: np.meshgrid([1, 2, 3], [1, 2, 3], indexing='ij') 
Out[7]: 
[array([[1, 1, 1], 
     [2, 2, 2], 
     [3, 3, 3]]), 
array([[1, 2, 3], 
     [1, 2, 3], 
     [1, 2, 3]])] 
+0

Dzięki za wyjaśnienia. Będąc bardziej zaznajomionym z MATLAB, znajduję konwencję dzielenia w numpy nieco dziwną, ale przynajmniej teraz wiem, jak to zrobić dobrze. – CrossEntropy

6

Można użyć np.meshgrid dać n1, n2 tablice właściwego kształtu, aby wykonać żądaną indeksowania:

In [104]: a[np.meshgrid(n1,n2, sparse=True, indexing='ij')] 
Out[104]: 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

Lub bez meshgrid:

In [117]: a[np.array(n1)[:,np.newaxis], np.array(n2)[np.newaxis,:]] 
Out[117]: 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

istnieje podobna przykład z wyjaśnieniem, jak działa ten integer array indexing w dokumentach.

Zobacz także przepis na książkę kucharską Picking out rows and columns.

8

Kolejny szybki sposób, aby zbudować pożądany wskaźnik jest użycie np.ix_ funkcję:

>>> a[np.ix_(n1, n2)] 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

Zapewnia to wygodny sposób do budowy otwartej siatki z sekwencji wskaźników.

0

Wydaje się, że przypadek użycia dla danego pytania dotyczył manipulowania obrazem n. Do tego stopnia, że ​​używasz tego przykładu do edycji numpy tablic pochodzących z obrazów, możesz użyć Python Imaging Library (PIL).

# Import Pillow: 
from PIL import Image 

# Load the original image: 
img = Image.open("flowers.jpg") 

# Crop the image 
img2 = img.crop((0, 0, 5, 5)) 

Obiekt img2 jest tablicą numpy wynikowego przyciętego obrazu.

można przeczytać więcej na temat manipulacji obrazem tutaj z Pillow package (przyjaznej dla użytkownika widelcem na opakowaniu PIL):