Encuentra filas únicas en numpy.array

199

Necesito encontrar filas únicas en a numpy.array.

Por ejemplo:

>>> 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]])

Sé que puedo crear un conjunto y un bucle sobre la matriz, pero estoy buscando una numpysolución pura eficiente . Creo que hay una manera de configurar el tipo de datos para anular y luego podría usar numpy.unique, pero no pude encontrar la manera de hacerlo funcionar.

Akavall
fuente
11
pandas tiene un método dataframe.drop_duplicates (). Ver stackoverflow.com/questions/12322779/pandas-unique-dataframe y pandas.pydata.org/pandas-docs/dev/generated/…
codeape
Gracias, pero no puedo usar pandas.
Akavall
2
posible duplicado de Eliminar duplicados en cada fila de una matriz numpy
Andy Hayden
1
@Andy Hayden, a pesar del título, no es un duplicado de esta pregunta. Sin embargo, el enlace de codeape es un duplicado.
Wai Yip Tung el
55
Esta característica llega de forma nativa a 1.13: github.com/numpy/numpy/pull/7742
Eric

Respuestas:

115

A partir de NumPy 1.13, uno simplemente puede elegir el eje para la selección de valores únicos en cualquier matriz N-dim. Para obtener filas únicas, uno puede hacer:

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

aiwabdn
fuente
12
Cuidado con esta función. np.unique(list_cor, axis=0)obtiene la matriz con filas duplicadas eliminadas ; no filtra la matriz a elementos que son únicos en la matriz original . Ver aquí , por ejemplo ..
Brad Solomon
Tenga en cuenta que si desea filas únicas que ignoren el orden de los valores en la fila, puede ordenar primero la matriz original en las columnas directamente:original_array.sort(axis=1)
mangecoeur
140

Otra posible solución

np.vstack({tuple(row) for row in a})
Greg von Winckel
fuente
20
+1 Esto es claro, corto y pitónico. A menos que la velocidad sea un problema real, este tipo de soluciones deberían preferir las respuestas complejas y con mayor voto a esta pregunta de la OMI.
Bill Cheatham
3
¡Excelente! Las llaves o la función set () hacen el truco.
Tian He
2
@ Greg von Winckel ¿Puede sugerir algo que no cambie el orden?
Laschet Jain
Sí, pero no en un solo comando: x = []; [x.append (tuple (r)) para r en una if tuple (r) no en x]; a_unique = matriz (x);
Greg von Winckel
1
Para evitar un FutureWarning, convierta el conjunto en una lista como: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: las matrices para apilar deben pasarse como un tipo de "secuencia" como lista o tupla. El soporte para iterables no secuenciales, como los generadores, está en desuso a partir de NumPy 1.16 y generará un error en el futuro.
leermeester
111

Otra opción para el uso de matrices estructuradas es usar una vista de un voidtipo que une toda la fila en un solo 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]])

EDITAR Añadido np.ascontiguousarraysiguiendo la recomendación de @ seberg. Esto ralentizará el método si la matriz aún no es contigua.

EDITAR Lo anterior se puede acelerar un poco, quizás a costa de la claridad, haciendo:

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

Además, al menos en mi sistema, en cuanto al rendimiento, está a la par, o incluso mejor, que el método 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
Jaime
fuente
3
Muchas gracias. Esta es la respuesta que estaba buscando, ¿puede explicar lo que está sucediendo en este paso b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))?
Akavall
3
@Akavall Está creando una vista de sus datos con un np.voidtipo de datos del tamaño del número de bytes en una fila completa. Es similar a lo que obtienes si tienes una matriz de np.uint8s y la ves como np.uint16s, que combina cada dos columnas en una sola, pero más flexible.
Jaime
3
@Jaime, ¿puedes agregar un np.ascontiguousarrayo similar para ser generalmente seguro (sé que es un poco más restrictivo de lo necesario, pero ...). Las filas deben ser contiguas para que la vista funcione como se espera.
seberg
2
@ConstantineEvans Es una adición reciente: en numpy 1.6, al intentar ejecutar np.uniqueen una matriz de np.voidretornos , aparece un error relacionado con mergesort no implementado para ese tipo. Sin embargo, funciona bien en 1.7.
Jaime
9
Vale la pena señalar que si este método se usa para números de coma flotante, hay una captura que -0.no se comparará como igual +0., mientras que una comparación elemento por elemento sí -0.==+0.(como lo especifica el estándar de flotación ieee). Ver stackoverflow.com/questions/26782038/…
tom10
29

Si desea evitar el gasto de memoria de convertir a una serie de tuplas u otra estructura de datos similar, puede explotar las matrices estructuradas de numpy.

El truco consiste en ver su matriz original como una matriz estructurada donde cada elemento corresponde a una fila de la matriz original. Esto no hace una copia, y es bastante eficiente.

Como un ejemplo rápido:

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

Para comprender lo que está sucediendo, eche un vistazo a los resultados intermedios.

Una vez que vemos las cosas como una matriz estructurada, cada elemento de la matriz es una fila en su matriz original. (Básicamente, es una estructura de datos similar a una lista de tuplas).

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 vez que ejecutamos numpy.unique, obtendremos una matriz estructurada:

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')])

Que luego debemos ver como una matriz "normal" ( _almacena el resultado del último cálculo en ipython, por lo que está viendo _.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])

Y luego vuelva a formar una matriz 2D ( -1es un marcador de posición que le dice a numpy que calcule el número correcto de filas, proporcione el número de columnas):

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]])

Obviamente, si quieres ser más conciso, puedes escribirlo como:

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)

Lo que resulta en:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
Joe Kington
fuente
Esto en realidad parece muy lento, casi tan lento como usar tuplas. Ordenar una matriz estructurada como esta es lento, aparentemente.
cge
3
@cge: pruébelo con matrices de mayor tamaño. Sí, ordenar una matriz numpy es más lento que ordenar una lista. Sin embargo, la velocidad no es la consideración principal en la mayoría de los casos en los que usa ndarrays. Es uso de memoria. Una lista de tuplas usará mucha más memoria que esta solución. Incluso si tiene suficiente memoria, con una matriz razonablemente grande, convertirla en una lista de tuplas tiene una sobrecarga mayor que la ventaja de velocidad.
Joe Kington
@cge - Ah, no me di cuenta de que estabas usando lexsort. Pensé que te referías a usar una lista de tuplas. Sí, lexsortes probablemente la mejor opción en este caso. Me olvidé de eso y salté a una solución demasiado compleja.
Joe Kington
20

np.uniquecuando lo ejecuto np.random.random(100).reshape(10,10)devuelve todos los elementos individuales únicos, pero desea las filas únicas, por lo que primero debe ponerlas en tuplas:

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

Esa es la única forma en que veo que cambias los tipos para hacer lo que quieres, y no estoy seguro de si la iteración de la lista para cambiar a tuplas está bien con tu "no recorrer"

Ryan Saxe
fuente
55
+1 Esto es claro, corto y pitónico. A menos que la velocidad sea un problema real, este tipo de soluciones deberían preferir las respuestas complejas y con mayor voto a esta pregunta de la OMI.
Bill Cheatham
Prefiero esto sobre la solución aceptada. La velocidad no es un problema para mí porque solo tengo quizás < 100filas por invocación. Esto describe con precisión cómo se realiza la realización de filas únicas.
rayryeng 01 de
44
Esto en realidad no funciona para mis datos, uniquescontiene elementos únicos. Potencialmente, no entiendo la forma esperada de array¿podría ser más preciso aquí?
FooBar
@ ryan-saxe Me gusta que esto sea pitónico, pero esta no es una buena solución porque la fila devuelta uniquesestá ordenada (y, por lo tanto, es diferente de las filas 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 funciona ordenando una matriz aplanada y luego observando si cada elemento es igual al anterior. Esto se puede hacer manualmente sin aplanar:

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

Este método no usa tuplas, y debería ser mucho más rápido y simple que otros métodos dados aquí.

NOTA: Una versión anterior de esto no tenía el ind justo después de un [, lo que significa que se usaron los índices incorrectos. Además, Joe Kington señala que esto hace una variedad de copias intermedias. El siguiente método genera menos, haciendo una copia ordenada y luego usando vistas de ella:

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

Esto es más rápido y usa menos memoria.

Además, si desea encontrar filas únicas en un ndarray independientemente de cuántas dimensiones hay en la matriz, lo siguiente funcionará:

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 problema pendiente interesante sería si quisiera ordenar / único a lo largo de un eje arbitrario de una matriz de dimensiones arbitrarias, algo que sería más difícil.

Editar:

Para demostrar las diferencias de velocidad, ejecuté algunas pruebas en ipython de los tres métodos diferentes descritos en las respuestas. Con su a exacta, no hay demasiada diferencia, aunque esta versión es un poco más rápida:

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

Sin embargo, con una versión más grande, esta versión termina siendo mucho, mucho más rápida:

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
cge
fuente
¡Muy agradable! En una nota al margen, sin embargo, hace varias copias intermedias. (por ejemplo, a[ind[1:]]es una copia, etc.) Por otro lado, su solución es generalmente 2-3 veces más rápida que la mía hasta que se queda sin ram.
Joe Kington
Buen punto. Como resultado, mi intento de sacar copias intermedias usando solo los índices hizo que mi método usara más memoria y terminara más lento que simplemente hacer una copia ordenada de la matriz, ya que a_sorted [1:] no es una copia de a_sorted .
cge
¿Qué hay dtypeen tus tiempos? Creo que te equivocaste. En mi sistema, llamar np.uniquecomo se describe en mi respuesta es un poco más rápido que usar cualquiera de los dos tipos de np.lexsort. Y es aproximadamente 5 veces más rápido si la matriz para encontrar elementos únicos tiene forma (10000, 100). Incluso si decide volver a implementar lo que np.uniquehace para recortar un tiempo de ejecución (menor), al colapsar cada fila en un solo objeto se realizan comparaciones más rápidas que tener que recurrir np.anya la comparación de las columnas, especialmente para conteos de columnas más altos.
Jaime
@cge: probablemente quisiste decir 'np.any' en lugar del estándar 'any' que no toma argumentos de palabras clave.
M. Toya
@Jaime: creo que dtypees justo a.dtype, es decir, el tipo de datos de los datos que se están viendo, como lo hizo Joe Kington en su respuesta. Si hay muchas columnas, otra forma (¡imperfecta!) De mantener las cosas rápidas usando lexsortes ordenar solo en unas pocas columnas. Esto es específico de los datos, ya que uno necesita saber qué columnas proporcionan suficiente variación para ordenar perfectamente. Por ejemplo a.shape = (60000, 500)- más o menos en las primeras 3 columnas: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). El ahorro de tiempo es bastante considerable, pero nuevamente el descargo de responsabilidad: es posible que no atrape todos los casos, depende de los datos.
n1k31t4
9

Aquí hay otra variación para @Greg respuesta pitónica

np.vstack(set(map(tuple, a)))
Divenex
fuente
9

Comparé la alternativa sugerida para la velocidad y descubrí que, sorprendentemente, la uniquesolución de vista vacía es incluso un poco más rápida que la nativa de numpy uniquecon el axisargumento. Si buscas velocidad, querrás

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

ingrese la descripción de la imagen aquí


Código para reproducir 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
    )
Nico Schlömer
fuente
1
Muy buena respuesta, un punto menor: vstack_dictnunca utiliza un dict, las llaves son una comprensión fija y, por lo tanto, su comportamiento es casi idéntico vstatck_set. Dado que vstack_dictfalta la línea de rendimiento para el gráfico para, parece que solo está siendo cubierta por el vstack_setgráfico de rendimiento, ¡ya que son muy similares!
Akavall
Gracias por la respuesta. He mejorado la trama para incluir solo una vstackvariante.
Nico Schlömer
8

No me gustó ninguna de estas respuestas porque ninguna maneja matrices de punto flotante en un álgebra lineal o sentido de espacio vectorial, donde dos filas siendo "iguales" significa "dentro de algún 𝜀". La única respuesta que tiene un umbral de tolerancia, https://stackoverflow.com/a/26867764/500207 , consideró que el umbral era tanto de precisión de elementos como de precisión decimal , lo que funciona en algunos casos, pero no es tan matemáticamente general como un verdadera distancia vectorial.

Aquí está mi versión:

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 función de dominio público anterior utiliza scipy.spatial.distance.pdistpara encontrar la distancia euclidiana (personalizable) entre cada par de filas. Luego, compara cada distancia con una threshantigua para encontrar las filas que están dentro threshde cada una, y devuelve solo una fila de cada threshgrupo.

Como se insinuó, la distancia metricno necesita ser euclidiana: pdistpuede calcular varias distancias, incluyendo cityblock(norma de Manhattan) y cosine(el ángulo entre vectores).

Si thresh=0(el valor predeterminado), entonces las filas deben ser exactas para ser consideradas "únicas". Otros valores buenos para threshel uso escalados máquina de precisión, es decir, thresh=np.spacing(1)*1e3.

Ahmed Fasih
fuente
La mejor respuesta. Gracias. Es la respuesta más generalizada (matemáticamente) escrita hasta ahora. Considera una matriz como un conjunto de puntos de datos o muestras en el espacio N-dimensional y encuentra una colección de puntos iguales o similares (la similitud se define por la distancia euclidiana o por cualquier otro método). Estos puntos pueden ser puntos de datos superpuestos o vecindarios muy cercanos. Al final, una colección de puntos iguales o similares se reemplaza por cualquiera de los puntos (en la respuesta anterior por un primer punto) que pertenece al mismo conjunto. Esto ayuda a reducir la redundancia de una nube de puntos.
Sanchit
@Sanchit aha, ese es un buen punto, en lugar de elegir el "primer" punto (en realidad podría ser efectivamente aleatorio, ya que depende de cómo Python almacena los puntos en a set) como representante de cada threshvecindario de tamaño, la función podría permitir usuario para especificar cómo elegir ese punto, por ejemplo, usar la "mediana" o el punto más cercano al centroide, etc.
Ahmed Fasih
Por supuesto. Sin duda. Acabo de mencionar el primer punto ya que esto es lo que está haciendo su programa, que está completamente bien.
Sanchit
Solo una corrección: dije erróneamente anteriormente que la fila que se elegiría para cada threshgrupo sería aleatoria debido a la naturaleza desordenada de set. Por supuesto, es una idea mental de mi parte, las settuplas de las tiendas almacenan índices que se encuentran en el threshvecindario, por lo findRows que, de hecho, esto devuelve, para cada threshgrupo, la primera fila.
Ahmed Fasih
3

¿Por qué no usar drop_duplicatespandas?

>>> 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
kalu
fuente
De hecho, me encanta esta respuesta. Claro, no usa numpy directamente, pero para mí es el que es más fácil de entender mientras es rápido.
noctilux
3

El paquete numpy_indexed (descargo de responsabilidad: soy su autor) envuelve la solución publicada por Jaime en una interfaz agradable y probada, además de muchas más características:

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

np.unique funciona dada una lista de tuplas:

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

Con una lista de listas plantea un TypeError: unhashable type: 'list'

codeape
fuente
No parece funcionar en el mío. Cada tupla es dos cadenas en lugar de dos números flotantes
mjp
no funciona, devuelve una lista de elementos, no tuplas
Mohanad Kaleia
1

Basado en la respuesta en esta página, he escrito una función que replica la capacidad de la unique(input,'rows')función de MATLAB , con la característica adicional de aceptar tolerancia para verificar la unicidad. También devuelve los índices tales que c = data[ia,:]y data = c[ic,:]. Informe si ve alguna discrepancia o error.

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
Arash_D_B
fuente
1

Más allá de la excelente respuesta de @Jaime, otra forma de colapsar una fila es usar a.strides[0](suponiendo que asea ​​C-contiguo) que es igual a a.dtype.itemsize*a.shape[0]. Además void(n)es un atajo para dtype((void,n)). llegamos finalmente a esta versión más corta:

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

por

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
BM
fuente
0

Para fines generales como 3D o matrices anidadas multidimensionales superiores, intente esto:

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:])

que satisface su conjunto de datos 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)

da:

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

Pero también matrices 3D como:

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)

da:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
Tara
fuente
Usar el unique return_indexcomo hace Jaime debería simplificar esa última returnlínea. Simplemente indexe el original aren el eje derecho.
hpaulj
0

Ninguna de estas respuestas funcionó para mí. Supongo que mis filas únicas contenían cadenas y no números. Sin embargo, esta respuesta de otro hilo funcionó:

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

Puede usar los métodos de la lista .count () y .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]
mjp
fuente
0

En realidad, podemos convertir la matriz numérica numérica mxn en una matriz de cadenas numpy mx 1, intente utilizar la siguiente función, proporciona count , inverse_idx y etc., al igual que 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

Ejemplo:

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]
Ting On Chan
fuente
-1

Consigamos toda la matriz numpy como una lista, luego eliminemos los duplicados de esta lista y finalmente regresemos nuestra lista única a una matriz 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]])
Mahdi Ghelichi
fuente
-3

La solución más sencilla es hacer que las filas sean un solo elemento convirtiéndolas en cadenas. Cada fila se puede comparar como un todo por su singularidad usando numpy. Esta solución es generalizable, solo necesita remodelar y transponer su matriz para otras combinaciones. Aquí está la solución para el problema proporcionado.

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]])

Envía mi premio nobel por correo

Dave Pena
fuente
Muy ineficiente y propenso a errores, por ejemplo, con diferentes opciones de impresión. Las otras opciones son claramente preferibles.
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])
YoungLearnsToCoding
fuente