2016-12-17 2 views
3

Ich habe ein n x n Array und möchte seine Umrisswerte erhalten. Zum BeispielWie bekomme ich alle Array-Kanten?

[4,5,6,7]

[2, 2,6, 3]

[4, 4,9, 4 ]

[8,1,6,1]

von diesem, würde ich diese

So im Wesentlichen

[4,5,6,7,3,4,1,6,1,8,4,2] 

(wo fett sehen) erhalten, was die meisten ist effizient Art und Weise ein 1D-Array aller Werte des Erhaltens um die Ränder going 2D-Array? Ich frage, weil ich annehme, es gibt eine numPy-Funktion, die dabei hilft (was ich noch nicht gefunden habe!), Anstatt es manuell mit Loops zu tun?

+1

ist die Reihenfolge wichtig? –

+0

Nein, zum Glück nicht in meinem Fall :) –

Antwort

2
In [1]: arr=np.arange(16).reshape(4,4) 
In [2]: arr 
Out[2]: 
array([[ 0, 1, 2, 3], 
     [ 4, 5, 6, 7], 
     [ 8, 9, 10, 11], 
     [12, 13, 14, 15]]) 

Eine relativ einfach Art und Weise, dies zu tun - im Uhrzeigersinn ist:

In [5]: alist=[arr[0,:-1], arr[:-1,-1], arr[-1,::-1], arr[-2:0:-1,0]] 
In [6]: alist 
Out[6]: [array([0, 1, 2]), array([ 3, 7, 11]), array([15, 14, 13, 12]), array([8, 4])] 
In [7]: np.concatenate(alist) 
Out[7]: array([ 0, 1, 2, 3, 7, 11, 15, 14, 13, 12, 8, 4]) 

In gewisser Weise ist es eine Schleife, in dass ich 4 Scheiben bauen. Aber wenn 4 im Vergleich zu n klein ist, ist das ein kleiner Preis. Es muss auf einer bestimmten Ebene verketten.

Wenn Reihenfolge nicht wichtig ist, könnten wir die Scheiben etwas vereinfachen (z.B. die umgekehrte Reihenfolge vergessen, usw.).

alist=[arr[0,:], arr[1:,-1], arr[-1,:-1], arr[1:-1,0]] 

Wenn ich nicht um scherte oder Doppel die Ecken zu zählen konnte ich verwenden:

np.array([arr[[0,n],:], arr[:,[0,n]].T]).ravel() 

die doppelten Ecken beseitigen

In [18]: np.concatenate((arr[[0,n],:].ravel(), arr[1:-1,[0,n]].ravel())) 
Out[18]: array([ 0, 1, 2, 3, 12, 13, 14, 15, 4, 7, 8, 11]) 
1

Angenommen, Ihre Liste ist in folgendem Format:

l = [ 
    [4, 5, 6, 7], 
    [2, 2, 6, 3], 
    [4, 4, 9, 4], 
    [8, 1, 6, 1] 
    ] 

können Sie erreichen, was Sie mit diesem schnellen Einzeiler wollen, Listenkomprehensionen mit:

out = list(l[0]) + # [4, 5, 6, 7] 
     list([i[-1] for i in l[1:-1]]) + # [3, 4] 
     list(reversed(l[-1])) + # [1, 6, 1, 8] 
     list(reversed([i[0] for i in l[1:-1]])) # [4, 2] 

print(out) # gives [4, 5, 6, 7, 3, 4, 1, 6, 1, 8, 4, 2] 

Dies funktioniert, ob Sie eine haben einfache Python-Liste oder ein numpy Array.

In Bezug auf Effizienz, mit %timeit auf einer 20000x20000-Matrix, nahm diese Methode .

l = np.random.random(20000, 20000) 
%timeit list(l[0]) + list(...) + list(...) + list(...) 
100 loops, best of 3: 16.4 ms per loop 

Ich bin sicher, dass es effizientere Methoden sind diese Aufgabe zu erfüllen, aber ich denke, dass für eine Einzeiler Lösung ziemlich gut ist.

1

Sie auch itertools.groupby und list comprehension verwenden können wie das Beispiel unten:

a = [ 
     [4,5,6,7], 
     [2,2,6,3], 
     [4,4,9,4], 
     [8,1,6,1], 
    ] 

from itertools import groupby 

def edges(a = list): 
    final, i = [], [] 
    for k, _ in groupby(a[1:-1], lambda x : [x[0], x[-1]]): 
     i += k 

    return a[0] + [k for n in range(1,len(i), 2) for k in i[n:n+1]] + a[-1][::-1] + [k for n in range(0, len(i), 2) for k in i[n:n+1] ][::-1] 
Ausgabe

:

print(edges(a)) 
>>> [4, 5, 6, 7, 3, 4, 1, 6, 1, 8, 4, 2] 

Test timeit:

a = [ 
     [4,5,6,7], 
     [2,2,6,3], 
     [4,4,9,4], 
     [8,1,6,1], 
    ] 

from itertools import groupby 

def edges(): 
    final, i = [], [] 
    for k, _ in groupby(a[1:-1], lambda x : [x[0], x[-1]]): 
     i += k 

    return a[0] + [k for n in range(1,len(i), 2) for k in i[n:n+1]] + a[-1][::-1] + [k for n in range(0, len(i), 2) for k in i[n:n+1] ][::-1] 


if __name__ == '__main__': 
    import timeit 
    print(timeit.timeit("edges()", setup="from __main__ import edges", number = 100)) 

Bestzeit wurde 0.0006266489999688929

1

Es ist wahrscheinlich langsamer als die Alternativen in den anderen Antworten erwähnt, weil es eine Maske zu schaffen (was mein Use-Case dann) kann es in Ihrem Fall verwendet werden:

def mask_borders(arr, num=1): 
    mask = np.zeros(arr.shape, bool) 
    for dim in range(arr.ndim): 
     mask[tuple(slice(0, num) if idx == dim else slice(None) for idx in range(arr.ndim))] = True 
     mask[tuple(slice(-num, None) if idx == dim else slice(None) for idx in range(arr.ndim))] = True 
    return mask 

Wie schon gesagt, das schafft und gibt einen mask, wo die Grenzen maskiert sind (True):

>>> mask_borders(np.ones((5,5))) 
array([[ True, True, True, True, True], 
     [ True, False, False, False, True], 
     [ True, False, False, False, True], 
     [ True, False, False, False, True], 
     [ True, True, True, True, True]], dtype=bool) 

>>> # Besides supporting arbitary dimensional input it can mask multiple border rows/cols 
>>> mask_borders(np.ones((5,5)), 2) 
array([[ True, True, True, True, True], 
     [ True, True, True, True, True], 
     [ True, True, False, True, True], 
     [ True, True, True, True, True], 
     [ True, True, True, True, True]], dtype=bool) 

Um die „Grenze“ Werte zu erhalten diese mit boolean indexing auf Ihrem Array angewendet werden muss:

>>> arr = np.array([[4,5,6,7], [2,2,6,3], [4,4,9,4], [8,1,6,1]]) 

>>> arr[mask_borders(arr)] 
array([4, 5, 6, 7, 2, 3, 4, 4, 8, 1, 6, 1]) 
+0

Schön! Ich hatte eine ähnliche Idee vektorisiert. – Divakar

+0

@Divakar Ich hatte Probleme, sie zu vektorisieren, wenn die Form nicht in allen Dimensionen identisch ist. Ich werde definitiv deine Antwort überprüfen! – MSeifert

+0

Ja, wenn also die Form von 'a'' (m, n) 'ist, würde sich meine Lösung ändern in:' r1 = np.minimum (np.bereich (n) [:: - 1], np.bereich (n)) 'und ähnlich für' r2' mit 'm' und schließlich die boole'sche Maske mit' np.minimum (r2 [:, None], r1) 'wenn ich diese Dims richtig verstanden habe. – Divakar

2

Hier ist ein vektorisierter Ansatz, um eine Maske solcher Randpixel/Elemente zu erstellen und dann einfach in das Array zu indizieren, um diese zu erhalten -

Auch dies ist nicht genau für die Leistung gedacht, sondern eher für Fälle, in denen Sie die Kantenbreite variieren oder einfach eine solche Maske solcher Kantenelemente erstellen können. Die Maske wäre: np.minimum(r[:,None],r)<W wie im letzten Schritt erstellt.

Probelauf -

In [89]: a 
Out[89]: 
array([[49, 49, 12, 90, 42], 
     [91, 58, 92, 16, 78], 
     [97, 19, 58, 84, 84], 
     [86, 31, 80, 78, 69], 
     [29, 95, 38, 51, 92]]) 

In [90]: border_elems(a,1) 
Out[90]: array([49, 49, 12, 90, 42, 91, 78, 97, 84, 86, 69, 29, 95, 38, 51, 92]) 

In [91]: border_elems(a,2) # Note this will select all but the center one : 58 
Out[91]: 
array([49, 49, 12, 90, 42, 91, 58, 92, 16, 78, 97, 19, 84, 84, 86, 31, 80, 
     78, 69, 29, 95, 38, 51, 92]) 
Verwandte Themen