2015-05-06 6 views
5

Was ist der beste Weg, um eine Funktion zu implementieren, die eine beliebige Anzahl von 1D-Arrays akzeptiert und ein Tupel zurückgibt, das die Indizes der passenden Werte enthält (falls vorhanden).Der effizienteste Weg zur Implementierung von numpy.in1d ​​für mehrere Arrays

Hier einige Pseudo-Code von dem, was ich tun möchte:

a = np.array([1, 0, 4, 3, 2]) 
b = np.array([1, 2, 3, 4, 5]) 
c = np.array([4, 2]) 

(ind_a, ind_b, ind_c) = return_equals(a, b, c) 
# ind_a = [2, 4] 
# ind_b = [1, 3] 
# ind_c = [0, 1] 

(ind_a, ind_b, ind_c) = return_equals(a, b, c, sorted_by=a) 
# ind_a = [2, 4] 
# ind_b = [3, 1] 
# ind_c = [0, 1] 

def return_equals(*args, sorted_by=None): 
    ... 
+0

Muss es immer eindeutige Werte innerhalb jeder dieser Eingabefelder? – Divakar

+0

Die Werte sind nicht sortiert, aber eindeutig ja. – Lukas

Antwort

5

Sie numpy.intersect1d mit reduce für diesen Einsatz:

def return_equals(*arrays): 
    matched = reduce(np.intersect1d, arrays) 
    return np.array([np.where(np.in1d(array, matched))[0] for array in arrays]) 

reduce wenig hier langsam sein kann, weil wir hier Zwischen NumPy Arrays erstellen (für große Anzahl von Eingangs kann es sehr langsam sein), können wir verhindern dies, wenn wir verwenden Python set und seine .intersection() Methode:

matched = np.array(list(set(arrays[0]).intersection(*arrays[1:]))) 

Verwandte Gith ub Ticket: n-array versions of set operations, especially intersect1d

0

Für Anfang, würde ich versuchen:

def return_equals(*args): 
    x=[] 
    c=args[-1] 
    for a in args: 
     x.append(np.nonzero(np.in1d(a,c))[0]) 
    return x 

Wenn ich einen d=np.array([1,0,4,3,0]) (es hat nur 1 Spiel hinzufügen ; was ist, wenn es keine Spiele)

dann

return_equals(a,b,d,c) 
0123?

produziert:

[array([2, 4], dtype=int32), 
array([1, 3], dtype=int32), 
array([2], dtype=int32), 
array([0, 1], dtype=int32)] 

Da die Länge beider Eingangs und zurückgegebenen Arrays unterscheiden können, die Sie wirklich nicht das Problem vektorisieren kann. Das heißt, es bedarf einer speziellen Gymnastik, um die Operation über alle Eingaben gleichzeitig durchzuführen. Und wenn die Anzahl der Arrays im Vergleich zu ihrer typischen Länge klein ist, würde ich mir keine Gedanken über die Geschwindigkeit machen. Iterieren ein paar Mal ist nicht teuer. Es ist über 100 Werte iterieren, die teuer ist.

Sie könnten die Schlüsselwortargumente natürlich an in1d übergeben.

Es ist nicht klar, was Sie mit dem Parameter sorted_by zu tun versuchen. Ist das etwas, das Sie genauso einfach auf die Arrays anwenden könnten, bevor Sie sie an diese Funktion übergeben?


Liste Verständnis Version dieser Iteration:

[np.nonzero(np.in1d(x,c))[0] for x in [a,b,d,c]] 

ich mir vorstellen kann, die Arrays in einem längeren verketten, Anwendung in1d, und es dann nach oben in Sub-Arrays aufgeteilt wird. Es gibt eine np.split, aber es erfordert, dass Sie ihm sagen, wie viele Elemente in jede Unterliste eingefügt werden sollen. Das bedeutet, irgendwie zu bestimmen, wie viele Übereinstimmungen es für jedes Argument gibt. Dies ohne Schleifen zu tun, könnte schwierig sein.

die Stücke dafür (die als Funktion müssen noch zu verpackende) sind:

args=[a,b,d,c] 
lens=[len(x) for x in args] 
abc=np.concatenate(args) 
C=np.cumsum(lens) 
I=np.nonzero(np.in1d(abc,c))[0] 
S=np.split(I,(2,4,5)) 
[S[0],S[1]-C[0],S[2]-C[1],S[3]-C[2]] 

I 
# array([ 2, 4, 6, 8, 12, 15, 16], dtype=int32) 
C 
# array([ 5, 10, 15, 17], dtype=int32) 

Die (2,4,5) sind die Anzahl der Elemente von I zwischen aufeinanderfolgenden Werten von C, dh die Anzahl von Elementen, die für Match jeder von a, b, ...

+0

Wie wird 'd' bestimmt? – dawg

+0

'd' war nur ein weiteres Beispiel, um einen Fall zu geben, in dem die Anzahl der Funde nicht 2 war. Ich versuchte das Problem zu verallgemeinern. – hpaulj

+0

Aww - Gotcha. – dawg

0

In Python:

def return_equal(*args): 
    rtr=[] 
    for i, arr in enumerate(args): 
     rtr.append([j for j, e in enumerate(arr) if 
        all(e in a for a in args[0:i]) and 
        all(e in a for a in args[i+1:])]) 
    return rtr  

>>> return_equal(a,b,c) 
[[2, 4], [1, 3], [0, 1]] 
+1

Ist das absichtlich ein nicht-numpy Ansatz? – hpaulj

+0

Seltsamerweise ist es tatsächlich schneller als alle in meinem Timing, aber ich würde nicht sagen, dass das offensichtlich ist. Ashwini Chaudhary's zweiter Vorschlag der Verwendung von 'set (a) .intersection (b, c)' ist auch schnell, aber auch meistens Python vs alle numpy ... – dawg

+0

'np.in1d' braucht Zeit zu sortieren und' unique' Arrays, also Es hat Overhead. Gerade bei kleinen Tests ist eine reine Listenoperation oft schneller als eine "numpige" Operation. – hpaulj

0

Diese Lösung verkettet grundsätzlich alle Eingänge 1D Arrays in einen großen 1D Array mit der Absicht, die erforderlichen Vorgänge in einem vectorized manner ausführt. Die einzige Stelle, an der es eine Schleife verwendet, ist am Anfang, wo es die Längen der Eingabearrays erhält, die zu den Laufzeitkosten minimal sein müssen.

Hier ist die Funktion Implementierung -

import numpy as np 

def return_equals(*argv): 
    # Concatenate input arrays into one big array for vectorized processing 
    A = np.concatenate((argv[:])) 

    # lengths of input arrays 
    narr = len(argv) 
    lens = np.zeros((1,narr),int).ravel() 
    for i in range(narr): 
     lens[i] = len(argv[i]) 

    N = A.size 

    # Start indices of each group of identical elements from different input arrays 
    # in a sorted version of the huge concatenated input array 
    start_idx = np.where(np.append([True],np.diff(np.sort(A))!=0))[0] 

    # Runlengths of islands of identical elements 
    runlens = np.diff(np.append(start_idx,N)) 

    # Starting and all indices of the positions in concatenate array that has 
    # islands of identical elements which are present across all input arrays 
    good_start_idx = start_idx[runlens==narr] 
    good_all_idx = good_start_idx[:,None] + np.arange(narr) 

    # Get offsetted indices and sort them to get the desired output 
    idx = np.argsort(A)[good_all_idx] - np.append([0],lens[:-1].cumsum()) 
    return np.sort(idx.T,1) 
Verwandte Themen