2013-09-05 11 views
9

Mam tablicę i chciałbym utworzyć mniejszą tablicę, skanując nienakładające się okna 2x2 i uzyskując maksimum. Oto przykład:Windowed maximum in numpy

import numpy as np 

np.random.seed(123) 
np.set_printoptions(linewidth=1000,precision=3) 
arr = np.random.uniform(-1,1,(4,4)) 
res = np.zeros((2,2)) 
for i in xrange(res.shape[0]): 
    for j in xrange(res.shape[1]): 
     ii = i*2 
     jj = j*2 
     res[i][j] = max(arr[ii][jj],arr[ii+1][jj],arr[ii][jj+1],arr[ii+1][jj+1]) 

print arr 
print res 

więc macierz tak:

[[ 0.393 -0.428 -0.546 0.103] 
[ 0.439 -0.154 0.962 0.37 ] 
[-0.038 -0.216 -0.314 0.458] 
[-0.123 -0.881 -0.204 0.476]] 

powinno stać się w ten sposób:

[[ 0.439 0.962] 
[-0.038 0.476]]  

Jak mogę to zrobić bardziej efektywnie?

+0

Czy możesz pokazać nam, co próbujesz i dlaczego nie zadziałało? –

+0

powyższy kod wykonuje wymagane zadanie, ale musi być szybkie i dlatego chciałbym usunąć pętlę for. –

+1

Rozważ użycie [NumBa] (http://numba.pydata.org/). Możesz zostawić podwójną pętlę tak, jak jest, dodaj około 10 znaków w dekoratorze i uzyskaj wydajność podobną do C. Łatwe w użyciu, gotowe do użycia, jeśli korzystasz z dystrybucji Continuum Analytics "[" Anaconda "] (https://store.continuum.io/cshop/anaconda/) dystrybucji Pythona. – ely

Odpowiedz

9

Można to zrobić:

print arr.reshape(2,2,2,2).swapaxes(1,2).reshape(2,2,4).max(axis=-1) 

[[ 0.439 0.962] 
[-0.038 0.476]] 

wyjaśnić zaczynające się od:

arr=np.array([[0.393,-0.428,-0.546,0.103], 
[0.439,-0.154,0.962,0.37,], 
[-0.038,-0.216,-0.314,0.458], 
[-0.123,-0.881,-0.204,0.476]]) 

Najpierw chcą grupy osi w odpowiednie sekcje.

tmp = arr.reshape(2,2,2,2).swapaxes(1,2) 
print tmp  

[[[[ 0.393 -0.428] 
    [ 0.439 -0.154]] 

    [[-0.546 0.103] 
    [ 0.962 0.37 ]]] 


[[[-0.038 -0.216] 
    [-0.123 -0.881]] 

    [[-0.314 0.458] 
    [-0.204 0.476]]]] 

Reshape jeszcze raz, aby uzyskać grupy danych chcemy:

tmp = tmp.reshape(2,2,4) 
print tmp 

[[[ 0.393 -0.428 0.439 -0.154] 
    [-0.546 0.103 0.962 0.37 ]] 

[[-0.038 -0.216 -0.123 -0.881] 
    [-0.314 0.458 -0.204 0.476]]] 

wreszcie wziąć max wzdłuż ostatniej osi.

to można uogólnić na macierzach kwadratowych, do:

k = arr.shape[0]/2 
arr.reshape(k,2,k,2).swapaxes(1,2).reshape(k,k,4).max(axis=-1) 

Po komentarzach Jamie i Dougal możemy uogólniać to dalej:

n = 2     #Height of window 
m = 2     #Width of window 
k = arr.shape[0]/n #Must divide evenly 
l = arr.shape[1]/m #Must divide evenly 
arr.reshape(k,n,l,m).max(axis=(-1,-3))    #Numpy >= 1.7.1 
arr.reshape(k,n,l,m).max(axis=-3).max(axis=-1)  #Numpy < 1.7.1 
+0

wow, który to robi. dzięki –

+1

Pamiętaj, że nie ma powodu, dla którego tablice muszą być kwadratowe, o ile są podzielne; możesz zmienić go na 'k = arr.shape [0]/n; l = arr.shape [1]/n; arr.reshape (k, n, l, n) .sapap (1, 2) .reshape (k, l, n * n) .max (axis = -1) ', myślę. – Dougal

+4

Ta ostatnia zmiana po zamianie osi wyzwala kopię pełnej tablicy, co może być kosztowne w przypadku dużych tablic.Najlepszym rozwiązaniem jest pominięcie go i (używając numpy> 1.7) dać krotkę osi do '.max', czyli' arr.reshape (2,2,2,2) .max (oś = (- 1, - 3)) 'Nawet jeśli utkniesz ze starszą wersją numpy, skopiujesz połowę danych, jeśli wykonasz dwa połączenia z' .max', np. 'Arr.reshape (2,2,2,2) .max (oś = -3) .max (oś = -1) '. – Jaime

2

Jak już wspomniano w obszarze komentarz rozważ użycie NumBa. Możesz zostawić podwójną pętlę tak, jak jest, dodaj około 10 znaków w dekoratorze i uzyskaj wydajność podobną do C. Łatwe w użyciu, gotowe do użycia, jeśli pracujesz z dystrybucją Python Continuum Analytics "Anaconda".

Jest to prawie idealny przypadek użycia dla NumBa, ponieważ ten algorytm jest znacznie bardziej naturalnie wyrażony za pomocą podwójnej pętli. Metoda zmiany kształtu wykorzystuje szybkie operacje tablicowe, ale jest wyjątkowo nieczytelna, chyba że znasz już cel tego programu. Bardzo pożądane jest pozostawienie takich funkcji w rozszerzonej formie i osiągnięcie prędkości poprzez umożliwienie konwersji czegoś na język niskiego poziomu po fakcie.

+0

Jestem świadomy numba i odpowiedniej prędkości, ale szukałem czystego, numpkiego rozwiązania. dzięki –

+4

Byłem ciekawy, jak dobrze zrobi numba w tym problemie, więc dałem temu szansę: http://nbviewer.ipython.org/c22a894f260d17876f01. W moim teście nieznacznie zmodyfikowanych wersji tych funkcji, na matrycy 200x200, oryginalny kod trwał 100ms, wersja JIT'dała numba zajęła ~ 85ms, a wersja @ Ofhionu zajęła 0.5ms. Skalowanie na matrycy 2k x 2k zajęło 8s, a Ophion trwało 64 ms. ~ 150-krotne przyspieszenia są prawdopodobnie warte niewielkiej utraty czytelności; czy wiesz, że robię coś złego, powodując, że numba robi tak źle? – Dougal

+0

Nie jestem pewien, ale dla tego rodzaju jądra brzmi to bardzo nietypowo. Rzucę okiem na notatnik. – ely