2016-04-07 3 views
0

Ich muss ein häufigstes Element in einer Matrix basierend auf den Nachbarwerten und sich selbst berechnen. Ich habe eine generic_filter Funktion gefunden, mit der ich berechnet habe, was ich wollte. Also hier ist, wie ich dies für ein 2D-Array tunndimage.generic_function auf 3D-Array

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

def most_frequent(arr): 
    def most_frequent(val): 
     return Counter(val).most_common(1)[0][0] 

    footprint = [[1, 1, 1], [1, 1, 1], [1, 1, 1]] 
    return ndimage.generic_filter(arr, most_frequent, footprint=footprint, mode='constant') 

print most_frequent(arr) 

Das gibt mir

[[0 0 0] 
[0 4 0] 
[0 0 0]] 

Ignorieren Sie die Elemente am Rand. Wie Sie sehen, ist das mittlere Element 4, da dies ein häufigstes Element unter den Nachbarn und dem Wert ist.


Das große Problem ist, dass ich das gleiche für eine 3D-Matrix tun muss. Also für eine Matrix wie diese

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

erwarte ich [0, 0] überall und [4, 4] in der Mitte zu bekommen. Dies schlägt mit RuntimeError('filter footprint array has incorrect shape.') fehl. Das Schlimmste, was ich Zweifel habe, dass ich generic_filter hier verwenden kann, weil die docs sagen:

CVAL: Skalar, optional Wert auf vergangene Kanten des Eingangs zu füllen, wenn Modus ‚constant‘ ist.

Also, wie kann ich mein Problem lösen?

+0

Enthält das 3D-Eingabearray nur ganze Zahlen? – Divakar

+0

Noch eine Frage - Sie beabsichtigen, einen solchen Filter gleitend über das Entir auszuführen Das 3D-Array könnte beliebig lang sein und nicht nur '(3,3,2)', oder? – Divakar

Antwort

1

Hier eine voll vektorisierte Lösung.

Erste make abgeflacht Nachbarschaften:

(n,m,_)=M.shape 
(sn,sm,s2)=M.strides 
newshape=(n-2,m-2,9,2) 
newstrides=(sn,sm,2*s2,s2) 
neighborhoods=np.lib.stride_tricks.as_strided(M,newshape,newstrides) 
""" 
array([[[[1, 1], 
     [2, 2], 
     [4, 1], 
     [1, 1], 
     [5, 5], 
     [6, 6], 
     [7, 7], 
     [2, 3], 
     [2, 2]], 

     [[2, 2], 
     [4, 1], 
     [1, 1], 
     [5, 5], 
     [6, 6], 
     [7, 7], 
     [2, 3], 
     [2, 2], 
     [4, 1]]]]) 
    """ 

Dann müssen Sie die beiden Komponenten packen np.unique, die mit 1D-Arrays arbeiten zu verwenden. vorausgesetzt M.dtype ist int32, können Sie tun, indem Sie Ansicht:

packed_neighborhoods=np.ascontiguousarray(neighborhoods).view(int64) 
In [5]: packed_neighborhoods.shape 
Out[5]: (1, 2, 9, 1) 

Nun definieren wir eine Funktion, die ein 1D-Array nehmen und die indice der am häufigsten finden, basierend auf np.unique:

def mostfreq(arr): 
    _,index,counts=unique(arr, return_index=True, return_counts=True) 
    return index[counts.argmax()] 

Wenden Sie es auf der guten Achse an:

ind2=apply_along_axis(mostfreq,2,packed_neighborhoods).squeeze() 

Und es gibt das Ergebnis, einschließlich anderer Indizes.

ind0,ind1=indices(neighborhoods.shape[:2]) 
print(neighborhoods[ind0,ind1,ind2]) 
""" 
[[[1 1] 
    [4 1]]] 
""" 

Aber Ihre Lösung gleiche Leistung im Moment hat;. (

+0

wow, es sieht aus wie es funktioniert. Ich werde etwas Zeit brauchen, um das zu untersuchen.Wie sind Sie auf die Idee gekommen, einen Fußabdruck auf diese Weise zu verändern? –

+0

Weil das Filter-Footprint-Array eine falsche Form hat. . Sie müssen eine Dimension hinzufügen, um den Daten ndim zu passen. Bearbeitet. –

+0

Es sieht so aus, als gäbe es dort ein Problem. Zum Beispiel für dieses Array arr = 'np.array ([ [[1, 1], [2, 2], [4, 1], [1, 1]], [[5, 5], [6 , 6], [7, 7], [2, 3]], [[2, 2], [4, 1], [4, 4], [2, 3]] ]) Ergebnisse sind "[2 1]", aber ein solches Element existiert nicht einmal in dem Array. –

0

Ein Weg, die ich gefunden habe, wie dies zu erreichen ist, indem für eine bessere Lösung, so etwas wie

def most_frequent(M): 
    x, y, _ = arr.shape 
    res = np.zeros((x - 2, y - 2, 2)) 
    for i in xrange(1, x - 1): 
     for j in xrange(1, y - 1): 
      neighbors = [M[i - 1, j - 1], M[i - 1, j], M[i - 1, j + 1], M[i, j - 1], M[i, j], M[i, j + 1], M[i + 1, j - 1], M[i + 1, j], M[i + 1, j + 1]] 
      res[i - 1, j - 1] = Counter([tuple(_) for _ in neighbors]).most_common(1)[0][0] 

    return res 

Noch tun (die meine 2 Schleifen nicht einbezieht)

+0

Ist 'M' dasselbe wie das Eingabe-3D-Array? – Divakar

+0

@Divakar ja, ist es –

Verwandte Themen