2015-05-06 9 views
5

Qual è il modo migliore per implementare una funzione che richiede un numero arbitrario di array 1d e restituisce una tupla contenente gli indici dei valori corrispondenti (se presenti).Il modo più efficace per implementare numpy.in1d ​​per gli array multipli

Ecco alcuni pseudo-codice di quello che voglio fare:

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

Ha sempre hanno valori unici all'interno di ciascuna di queste matrici di ingresso? – Divakar

+0

I valori non sono ordinati ma unici sì. – Lukas

risposta

5

È possibile utilizzare numpy.intersect1d con reduce per questo:

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

reduce può essere poco lento qui perché stiamo creando array NumPy intermedi qui (per il gran numero di ingresso può essere molto lento), possiamo prevenire questo se usiamo Python set e il suo metodo di .intersection():

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

Gith correlati biglietti ub: n-array versions of set operations, especially intersect1d

0

Per cominciare, mi piacerebbe provare:

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

Se posso aggiungere un d=np.array([1,0,4,3,0]) (ha solo 1 partita ; che cosa se non ci sono partite)

poi

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

produce:

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

Poiché la lunghezza di input e restituiti matrici possono differire, davvero non può vettorizzare il problema. Cioè, ci vuole un po 'di ginnastica speciale per eseguire l'operazione su tutti gli ingressi contemporaneamente. E se il numero di array è piccolo rispetto alla loro lunghezza tipica, non mi preoccuperei della velocità. L'iterazione di alcune volte non è costosa. Sta iterando oltre 100 valori che sono costosi.

È possibile, naturalmente, passare gli argomenti parola chiave su in1d.

Non è chiaro cosa si sta tentando di fare con il parametro sorted_by. È qualcosa che potresti facilmente applicare agli array prima di passarli a questa funzione?


versione di lista di questa iterazione:

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

posso immaginare concatenando le matrici in uno più lungo, applicando in1d, e poi dividere in su in sottoarray. C'è un np.split, ma è necessario che tu gli dica quanti elementi inserire in ogni sottolista. Ciò significa, in qualche modo, determinare quante partite ci sono per ogni argomento. Fare ciò senza il loop potrebbe essere complicato.

I pezzi per questo (che devono ancora essere imballato come funzione) sono:

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) 

Il (2,4,5) sono il numero di elementi di I fra valori successivi di C, cioè il numero di elementi che corrispondono a ciascuno di a, b, ...

+0

Come viene determinato 'd'? – dawg

+0

'd' era solo un altro esempio, per dare un caso in cui il numero di reperti non era 2. Stavo cercando di generalizzare il problema. – hpaulj

+0

Aww - gothcha. – 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

È intenzionalmente un approccio non intorpidito? – hpaulj

+0

Stranamente, in realtà è più veloce di tutti i numpy nei miei tempi, ma non direi che è ovvio. Il secondo suggerimento di Ashwini Chaudhary di usare 'set (a) .intersection (b, c)' è anche veloce, ma anche principalmente Python contro tutti i numpy ... – dawg

+0

'np.in1d' richiede tempo per ordinare e array' unique', quindi ha sovraccarico. Soprattutto per i test di piccole dimensioni, un'operazione di lista pura spesso è più veloce di quella "numpy". – hpaulj

0

Questa soluzione concatena praticamente tutto ingresso 1D array in un unico grande 1D matrice con l'intenzione di effettuare le operazioni richieste in un vectorized manner. L'unico punto in cui utilizza il ciclo è all'inizio dove ottiene la lunghezza degli array di input, che deve essere minimo sui costi di runtime.

Ecco l'implementazione della funzione -

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) 
Problemi correlati