Trova righe univoche in numpy.array


199

Devo trovare righe univoche in a numpy.array.

Per esempio:

>>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

So che posso creare un set e eseguire il loop sull'array, ma sto cercando una numpysoluzione pura ed efficiente . Credo che ci sia un modo per impostare il tipo di dati su void e quindi potrei semplicemente usare numpy.unique, ma non sono riuscito a capire come farlo funzionare.



Grazie, ma non posso usare i panda.
Akavall,


1
@Andy Hayden, nonostante il titolo, non è un duplicato di questa domanda. Il link di codeape è però un duplicato.
Wai Yip Tung,

5
Questa funzionalità arriverà in modo nativo all'1.13: github.com/numpy/numpy/pull/7742
Eric

Risposte:


115

A partire da NumPy 1.13, si può semplicemente scegliere l'asse per la selezione di valori univoci in qualsiasi array N-dim. Per ottenere righe uniche, puoi fare:

unique_rows = np.unique(original_array, axis=0)


12
Attento con questa funzione. np.unique(list_cor, axis=0)ottiene l' array con le righe duplicate rimosse ; non filtra l'array in base a elementi univoci dell'array originale . Vedi qui , per esempio ..
Brad Solomon,

Si noti che se si desidera che le righe univoche ignorino l'ordine dei valori nella riga, è possibile ordinare prima l'array originale nelle colonne:original_array.sort(axis=1)
mangecoeur

140

Ancora un'altra possibile soluzione

np.vstack({tuple(row) for row in a})

20
+1 Questo è chiaro, breve e pitonico. A meno che la velocità non sia un vero problema, questo tipo di soluzioni dovrebbe privilegiare le risposte complesse e più votate a questa domanda IMO.
Bill Cheatham,

3
Eccellente! Le parentesi graffe o la funzione set () fanno il trucco.
Tian He,

2
@Greg von Winckel Puoi suggerire qualcosa che non fa qualcosa che non cambia ordine.
Laschet Jain,

Sì, ma non in un singolo comando: x = []; [x.append (tuple (r)) per r in a if tuple (r) non in x]; a_unique = array (x);
Greg von Winckel,

1
Per evitare un FutureWarning, converti il ​​set in un elenco come: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: le matrici da impilare devono essere passate come tipo "sequenza" come lista o tupla. Il supporto per iterabili non in sequenza come i generatori è obsoleto a partire da NumPy 1.16 e genererà un errore in futuro.
Leermeester,

111

Un'altra opzione per l'uso di array strutturati è l'utilizzo di una vista di un voidtipo che unisce l'intera riga in un singolo elemento:

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

EDIT Aggiunto np.ascontiguousarrayseguendo la raccomandazione di @ seberg. Questo rallenterà il metodo se l'array non è già contiguo.

MODIFICA Quanto sopra può essere leggermente accelerato, forse a costo di chiarezza, facendo:

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

Inoltre, almeno sul mio sistema, per quanto riguarda le prestazioni è alla pari, o addirittura migliore, rispetto al metodo lexsort:

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop

3
Molte grazie. Questa è la risposta che stavo cercando, puoi spiegare cosa sta succedendo in questo passaggio b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1]))):?
Akavall,

3
@Akavall Sta creando una vista dei tuoi dati con un np.voidtipo di dati di dimensioni pari al numero di byte in una riga intera. È simile a quello che ottieni se hai una matrice di se np.uint8la vedi come se np.uint16, che combina ogni due colonne in una sola, ma più flessibile.
Jaime

3
@Jaime, puoi aggiungere un np.ascontiguousarrayo simile per essere generalmente sicuro (so che è un po 'più restrittivo del necessario, ma ...). Le righe devono essere contigue affinché la vista funzioni come previsto.
seberg,

2
@ConstantineEvans È un'aggiunta recente: in numpy 1.6, il tentativo di eseguire np.uniquesu una matrice di np.voidresi restituisce un errore relativo a mergesort non implementato per quel tipo. Funziona bene in 1.7 però.
Jaime

9
Vale la pena notare che se questo metodo viene utilizzato per i numeri in virgola mobile, esiste un problema che -0.non si confronta con uguale a +0., mentre avrebbe un confronto elemento per elemento -0.==+0.(come specificato dallo standard float ieee). Vedere stackoverflow.com/questions/26782038/...
tom10

29

Se si desidera evitare la spesa in memoria della conversione in una serie di tuple o in un'altra struttura di dati simile, è possibile sfruttare le matrici strutturate di numpy.

Il trucco è visualizzare l'array originale come un array strutturato in cui ogni elemento corrisponde a una riga dell'array originale. Questo non ne fa una copia ed è abbastanza efficiente.

A titolo di esempio:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

Per capire cosa sta succedendo, dai un'occhiata ai risultati intermedi.

Una volta che vediamo le cose come una matrice strutturata, ogni elemento nella matrice è una riga nella matrice originale. (Fondamentalmente, è una struttura di dati simile a un elenco di tuple.)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Una volta eseguito numpy.unique, avremo indietro un array strutturato:

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Che dobbiamo quindi vedere come un array "normale" ( _memorizza il risultato dell'ultimo calcolo ipython, motivo per cui stai vedendo _.view...):

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

E quindi rimodellare in un array 2D ( -1è un segnaposto che dice a numpy di calcolare il numero corretto di righe, fornire il numero di colonne):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Ovviamente, se volessi essere più conciso, potresti scriverlo come:

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

Che si traduce in:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]

Questo in realtà sembra molto lento, quasi lento quanto l'uso delle tuple. L'ordinamento di un array strutturato come questo è lento, a quanto pare.
cge

3
@cge - Provalo con array di dimensioni maggiori. Sì, l'ordinamento di un array numpy è più lento dell'ordinamento di un elenco. La velocità non è la considerazione principale nella maggior parte dei casi in cui si utilizza ndarrays. È l'utilizzo della memoria. Un elenco di tuple utilizzerà molta più memoria di questa soluzione. Anche se hai abbastanza memoria, con un array ragionevolmente grande, convertirlo in un elenco di tuple ha un sovraccarico maggiore rispetto al vantaggio della velocità.
Joe Kington,

@cge - Ah, non ho notato che stavi usando lexsort. Pensavo ti riferissi all'utilizzo di un elenco di tuple. Sì, lexsortprobabilmente è l'opzione migliore in questo caso. Me ne ero dimenticato e sono saltato a una soluzione troppo complessa.
Joe Kington,

20

np.uniquequando lo eseguo np.random.random(100).reshape(10,10)restituisce tutti i singoli elementi univoci, ma vuoi le righe univoche, quindi prima devi metterle in tuple:

array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)

Questo è l'unico modo in cui ti vedo cambiare i tipi per fare quello che vuoi, e non sono sicuro che l'iterazione dell'elenco da cambiare in tuple vada bene con il tuo "non looping"


5
+1 Questo è chiaro, breve e pitonico. A meno che la velocità non sia un vero problema, questo tipo di soluzioni dovrebbe privilegiare le risposte complesse e più votate a questa domanda IMO.
Bill Cheatham,

Preferisco questo rispetto alla soluzione accettata. La velocità non è un problema per me perché ho forse solo < 100righe per chiamata. Questo descrive esattamente come viene eseguita la prestazione unica su righe.
Rayryeng

4
Questo in realtà non funziona per i miei dati, uniquescontiene elementi unici. Potenzialmente fraintendere la forma prevista di array- potresti essere più preciso qui?
FooBar,

@ ryan-saxe Mi piace il fatto che sia pitonico ma questa non è una buona soluzione perché le righe restituite uniquessono ordinate (e quindi diverse dalle righe in array). B = np.array([[1,2],[2,1]]); A = np.unique([tuple(row) for row in B]); print(A) = array([[1, 2],[1, 2]])
jmlarson,

16

np.unique funziona ordinando un array appiattito, quindi verificando se ciascun elemento è uguale al precedente. Questo può essere fatto manualmente senza appiattire:

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

Questo metodo non utilizza le tuple e dovrebbe essere molto più veloce e più semplice rispetto ad altri metodi indicati qui.

NOTA: una versione precedente di questo non aveva l'ind giusto dopo un [, il che significa che venivano usati indici errati. Inoltre, Joe Kington sottolinea che questo fa una varietà di copie intermedie. Il seguente metodo rende meno, facendo una copia ordinata e quindi usando le viste di esso:

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

Questo è più veloce e utilizza meno memoria.

Inoltre, se si desidera trovare righe univoche in un ndarray indipendentemente da quante dimensioni sono presenti nell'array, funzionerà quanto segue:

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]

Un interessante problema rimanente sarebbe se si volesse ordinare / unificare lungo un asse arbitrario di un array di dimensioni arbitrarie, qualcosa che sarebbe più difficile.

Modificare:

Per dimostrare le differenze di velocità, ho eseguito alcuni test in ipython dei tre diversi metodi descritti nelle risposte. Con il tuo esatta a, non c'è troppa differenza, anche se questa versione è un po 'più veloce:

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

Con una più grande, tuttavia, questa versione finisce per essere molto, molto più veloce:

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop

Molto bella! Ad ogni modo, fa diverse copie intermedie. (ad es. a[ind[1:]]una copia, ecc.) D'altra parte, la tua soluzione è generalmente 2-3 volte più veloce della mia fino a quando non esaurisci il ram.
Joe Kington,

Buon punto. A quanto pare, il mio tentativo di estrarre copie intermedie usando solo gli indici ha fatto sì che il mio metodo usasse più memoria e finisse più lentamente rispetto a fare una copia ordinata dell'array, poiché a_sorted [1:] non è una copia di a_sorted .
cge

Cosa c'è dtypenei tuoi tempi? Penso che tu abbia sbagliato quello. Sul mio sistema, chiamare np.uniquecome descritto nella mia risposta è leggermente più veloce rispetto all'utilizzo di uno dei due tipi di np.lexsort. Ed è circa 5 volte più veloce se l'array per trovare elementi unici ha forma (10000, 100). Anche se decidi di reimplementare ciò che np.uniqueserve per tagliare un po 'di tempo di esecuzione (minore), comprimere ogni riga in un singolo oggetto esegue confronti più rapidi rispetto a dover ricorrere np.anyal confronto delle colonne, soprattutto per conteggi di colonne più elevati.
Jaime

@cge: probabilmente intendevi 'np.any' anziché lo standard 'any' che non accetta l'argomento della parola chiave.
M. Toya,

@Jaime - Credo dtypesia giusto a.dtype, cioè il tipo di dati dei dati visualizzati, come è stato fatto da Joe Kington nella sua risposta. Se ci sono molte colonne, un altro modo (imperfetto!) Per mantenere le cose veloci usando lexsortè ordinare solo su alcune colonne. Questo è specifico per i dati in quanto è necessario sapere quali colonne forniscono una varianza sufficiente per ordinare perfettamente. Ad esempio a.shape = (60000, 500)- specie sui primi 3 colonne: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). I risparmi di tempo sono piuttosto sostanziali, ma la dichiarazione di non responsabilità: potrebbe non catturare tutti i casi - dipende dai dati.
n1k31t4,

9

Ecco un'altra variante della risposta pythonic di @Greg

np.vstack(set(map(tuple, a)))

9

Ho confrontato l'alternativa suggerita per la velocità e ho scoperto che, sorprendentemente, la uniquesoluzione di visualizzazione del vuoto è persino un po 'più veloce del nativo di Numpy uniquecon l' axisargomento. Se stai cercando velocità, ti consigliamo

numpy.unique(
    a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
    ).view(a.dtype).reshape(-1, a.shape[1])

inserisci qui la descrizione dell'immagine


Codice per riprodurre la trama:

import numpy
import perfplot


def unique_void_view(a):
    return numpy.unique(
        a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
        ).view(a.dtype).reshape(-1, a.shape[1])


def lexsort(a):
    ind = numpy.lexsort(a.T)
    return a[ind[
        numpy.concatenate((
            [True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
            ))
        ]]


def vstack(a):
    return numpy.vstack({tuple(row) for row in a})


def unique_axis(a):
    return numpy.unique(a, axis=0)


perfplot.show(
    setup=lambda n: numpy.random.randint(2, size=(n, 20)),
    kernels=[unique_void_view, lexsort, vstack, unique_axis],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    xlabel='len(a)',
    equality_check=None
    )

1
Risposta molto bella, un punto minore vstack_dict:, non usa mai un dict, le parentesi graffe sono una comprensione stabilita, e quindi il suo comportamento è quasi identico a vstatck_set. Dal momento vstack_dictche manca la linea delle prestazioni per il grafico, sembra che sia appena coperta dal vstack_setgrafico delle prestazioni, poiché sono così simili!
Akavall,

Grazie per la risposta. Ho migliorato la trama per includere solo una vstackvariante.
Nico Schlömer,

8

Non mi è piaciuta nessuna di queste risposte perché nessuna gestisce matrici a virgola mobile in un'algebra lineare o senso dello spazio vettoriale, dove due file "uguali" significano "all'interno di alcuni 𝜀". L'unica risposta che ha una soglia di tolleranza, https://stackoverflow.com/a/26867764/500207 , ha considerato la soglia sia in termini di elementi che decimali precisione che , che funziona per alcuni casi ma non è matematicamente generale come un vera distanza vettoriale.

Ecco la mia versione:

from scipy.spatial.distance import squareform, pdist

def uniqueRows(arr, thresh=0.0, metric='euclidean'):
    "Returns subset of rows that are unique, in terms of Euclidean distance"
    distances = squareform(pdist(arr, metric=metric))
    idxset = {tuple(np.nonzero(v)[0]) for v in distances <= thresh}
    return arr[[x[0] for x in idxset]]

# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
    return uniqueRows(arr.T, *args, **kwargs)

La funzione di dominio pubblico sopra utilizza scipy.spatial.distance.pdistper trovare la distanza euclidea (personalizzabile) tra ogni coppia di file. Quindi confronta ciascuna distanza con una threshvecchia per trovare le righe che si trovano l' threshuna nell'altra e restituisce solo una riga da ciascun threshcluster.

Come accennato, la distanza metricnon deve essere euclidea - pdistpuò calcolare varie distanze tra cui cityblock(norma di Manhattan) ecosine (angolo tra i vettori).

Se thresh=0(impostazione predefinita), le righe devono essere bit-esatte per essere considerate "uniche". Altri buoni valori per l' threshuso della precisione della macchina in scala, ad es thresh=np.spacing(1)*1e3.


Migliore risposta. Grazie. È la risposta più (matematicamente) generalizzata finora scritta. Considera una matrice come un insieme di punti dati o campioni nello spazio N-dimensionale e trova una raccolta di punti uguali o simili (la somiglianza è definita dalla distanza euclidea o da qualsiasi altro metodo). Questi punti possono essere punti dati sovrapposti o quartieri molto vicini. Alla fine, una raccolta di punti uguali o simili viene sostituita da uno qualsiasi dei punti (nella risposta precedente con un primo punto) appartenente allo stesso insieme. Questo aiuta a ridurre la ridondanza da una nuvola di punti.
Sanchit

@Sanchit aha, questo è un buon punto, invece di scegliere il "primo" punto (in realtà potrebbe essere effettivamente casuale, poiché dipende da come Python memorizza i punti in a set) come rappresentante di ciascun threshquartiere di dimensioni, la funzione potrebbe consentire al l'utente per specificare come selezionare quel punto, ad esempio, utilizzare la "mediana" o il punto più vicino al centroide, ecc.
Ahmed Fasih

Sicuro. Nessun dubbio. Ho appena citato il primo punto poiché questo è ciò che sta facendo il tuo programma, il che è completamente soddisfacente.
Sanchit,

Solo una correzione - ho detto erroneamente sopra che la riga che sarebbe stata scelta per ogni threshcluster sarebbe casuale a causa della natura non ordinata di set. Naturalmente questa è una brainfart da parte mia, i setnegozi di tuple di indici che si trovano nella thresh-neighborhood, quindi questo findRows fa infatti ritorno, per ogni thresh-cluster, la prima riga in esso.
Ahmed Fasih,

3

Perché non usare drop_duplicatesdai panda:

>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop

>>> timeit np.vstack({tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop

In realtà adoro questa risposta. Certo, non usa direttamente intorpidimento, ma per me è quello che è più facile da capire pur essendo veloce.
noctilux,

3

Il pacchetto numpy_indexed (dichiarazione di non responsabilità: sono il suo autore) racchiude la soluzione pubblicata da Jaime in un'interfaccia piacevole e testata, oltre a molte altre funzionalità:

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default

1

np.unique funziona con un elenco di tuple:

>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]: 
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

Con un elenco di elenchi genera a TypeError: unhashable type: 'list'


non sembra funzionare sul mio. Ogni tupla è di due stringhe invece di due numeri float
mjp

non funziona, restituisce un elenco di elementi non tuple
Mohanad Kaleia,

1

Sulla base della risposta in questa pagina ho scritto una funzione che replica la capacità della unique(input,'rows')funzione MATLAB , con la funzione aggiuntiva di accettare la tolleranza per il controllo dell'unicità. Restituisce anche gli indici in modo tale che c = data[ia,:]e data = c[ic,:]. Si prega di segnalare se si riscontrano discrepanze o errori.

def unique_rows(data, prec=5):
    import numpy as np
    d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
    b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
    _, ia = np.unique(b, return_index=True)
    _, ic = np.unique(b, return_inverse=True)
    return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic

1

Oltre alla risposta eccellente di @Jaime, un altro modo per comprimere una riga è utilizzare a.strides[0](supponendo che asia contiguo C) che è uguale a a.dtype.itemsize*a.shape[0]. Inoltre void(n)è una scorciatoia per dtype((void,n)). arriviamo finalmente a questa versione più breve:

a[unique(a.view(void(a.strides[0])),1)[1]]

Per

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]

0

Per scopi generici come array nidificati multidimensionali 3D o superiori, provare questo:

import numpy as np

def unique_nested_arrays(ar):
    origin_shape = ar.shape
    origin_dtype = ar.dtype
    ar = ar.reshape(origin_shape[0], np.prod(origin_shape[1:]))
    ar = np.ascontiguousarray(ar)
    unique_ar = np.unique(ar.view([('', origin_dtype)]*np.prod(origin_shape[1:])))
    return unique_ar.view(origin_dtype).reshape((unique_ar.shape[0], ) + origin_shape[1:])

che soddisfa il tuo set di dati 2D:

a = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
unique_nested_arrays(a)

dà:

array([[0, 1, 1, 1, 0, 0],
   [1, 1, 1, 0, 0, 0],
   [1, 1, 1, 1, 1, 0]])

Ma anche array 3D come:

b = np.array([[[1, 1, 1], [0, 1, 1]],
              [[0, 1, 1], [1, 1, 1]],
              [[1, 1, 1], [0, 1, 1]],
              [[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)

dà:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])

L'uso unique return_indexdi Jaime dovrebbe rendere l'ultima returnriga più semplice. Basta indicizzare l'originale arsull'asse destro.
hpaulj,

0

Nessuna di queste risposte ha funzionato per me. Suppongo che le mie righe univoche contengano stringhe e non numeri. Tuttavia, questa risposta da un altro thread ha funzionato:

Fonte: https://stackoverflow.com/a/38461043/5402386

Puoi usare i metodi dell'elenco .count () e .index ()

coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]

0

Possiamo effettivamente trasformare un array numpy mxn numerico in un array di stringhe numx mx 1, prova ad usare la seguente funzione, fornisce count , inverse_idx ed ecc., Proprio come numpy.unique:

import numpy as np

def uniqueRow(a):
    #This function turn m x n numpy array into m x 1 numpy array storing 
    #string, and so the np.unique can be used

    #Input: an m x n numpy array (a)
    #Output unique m' x n numpy array (unique), inverse_indx, and counts 

    s = np.chararray((a.shape[0],1))
    s[:] = '-'

    b = (a).astype(np.str)

    s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)

    n = a.shape[1] - 2    

    for i in range(0,n):
         s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)

    s3, idx, inv_, c = np.unique(s2,return_index = True,  return_inverse = True, return_counts = True)

    return a[idx], inv_, c

Esempio:

A = np.array([[ 3.17   9.502  3.291],
  [ 9.984  2.773  6.852],
  [ 1.172  8.885  4.258],
  [ 9.73   7.518  3.227],
  [ 8.113  9.563  9.117],
  [ 9.984  2.773  6.852],
  [ 9.73   7.518  3.227]])

B, inv_, c = uniqueRow(A)

Results:

B:
[[ 1.172  8.885  4.258]
[ 3.17   9.502  3.291]
[ 8.113  9.563  9.117]
[ 9.73   7.518  3.227]
[ 9.984  2.773  6.852]]

inv_:
[3 4 1 0 2 4 0]

c:
[2 1 1 1 2]

-1

Consente di ottenere l'intera matrice numpy come elenco, quindi eliminare i duplicati da questo elenco e infine restituire il nostro elenco univoco in una matrice numpy:

matrix_as_list=data.tolist() 
matrix_as_list:
[[1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0]]

uniq_list=list()
uniq_list.append(matrix_as_list[0])

[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]

unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

-3

La soluzione più semplice è rendere le righe un singolo elemento rendendole stringhe. Ogni riga può quindi essere confrontata nel suo insieme per la sua unicità usando numpy. Questa soluzione è generalizzabile, devi solo rimodellare e trasporre l'array per altre combinazioni. Ecco la soluzione per il problema fornito.

import numpy as np

original = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

uniques, index = np.unique([str(i) for i in original], return_index=True)
cleaned = original[index]
print(cleaned)    

Darà:

 array([[0, 1, 1, 1, 0, 0],
        [1, 1, 1, 0, 0, 0],
        [1, 1, 1, 1, 1, 0]])

Invia il mio premio nobel per posta


Molto inefficiente e soggetto a errori, ad esempio con diverse opzioni di stampa. Le altre opzioni sono chiaramente preferibili.
Michael,

-3
import numpy as np
original = np.array([[1, 1, 1, 0, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [1, 1, 1, 0, 0, 0],
                     [1, 1, 1, 1, 1, 0]])
# create a view that the subarray as tuple and return unique indeies.
_, unique_index = np.unique(original.view(original.dtype.descr * original.shape[1]),
                            return_index=True)
# get unique set
print(original[unique_index])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.