Trouver des lignes uniques dans numpy.array

199

J'ai besoin de trouver des lignes uniques dans un fichier numpy.array.

Par exemple:

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

Je sais que je peux créer un ensemble et une boucle sur le tableau, mais je recherche une numpysolution pure efficace . Je crois qu'il existe un moyen de définir le type de données sur void et que je pourrais simplement l'utiliser numpy.unique, mais je ne pouvais pas comprendre comment le faire fonctionner.

Akavall
la source
11
pandas a une méthode dataframe.drop_duplicates (). Voir stackoverflow.com/questions/12322779/pandas-unique-dataframe et pandas.pydata.org/pandas-docs/dev/generated/…
codeape
Merci, mais je ne peux pas utiliser de pandas.
Akavall
1
@Andy Hayden, malgré le titre, ce n'est pas un doublon à cette question. Le lien de codeape est un doublon cependant.
Wai Yip Tung
5
Cette fonctionnalité arrive nativement à 1.13: github.com/numpy/numpy/pull/7742
Eric

Réponses:

115

Depuis NumPy 1.13, on peut simplement choisir l'axe pour la sélection de valeurs uniques dans n'importe quel tableau N-dim. Pour obtenir des lignes uniques, on peut faire:

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

aiwabdn
la source
12
Attention à cette fonction. np.unique(list_cor, axis=0)vous obtient le tableau avec les lignes en double supprimées ; il ne filtre pas le tableau en éléments uniques dans le tableau d'origine . Voir ici , par exemple ..
Brad Solomon
Notez que si vous voulez que les lignes uniques ignorent l'ordre des valeurs dans la ligne, vous pouvez d'abord trier le tableau d'origine dans les colonnes:original_array.sort(axis=1)
mangecoeur
140

Encore une autre solution possible

np.vstack({tuple(row) for row in a})
Greg von Winckel
la source
20
+1 C'est clair, court et pythonique. À moins que la vitesse ne soit un réel problème, ce type de solutions devrait prendre le pas sur les réponses complexes et plus votées à cette question de l'OMI.
Bill Cheatham
3
Excellent! Les accolades ou la fonction set () font l'affaire.
Tian He
2
@Greg von Winckel Pouvez-vous suggérer quelque chose qui ne change pas l'ordre.
Laschet Jain, le
Oui, mais pas dans une seule commande: x = []; [x.append (tuple (r)) pour r dans un if tuple (r) pas dans x]; a_unique = array (x);
Greg von Winckel
1
Pour éviter un FutureWarning, convertissez l'ensemble en une liste comme: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: les tableaux à empiler doivent être passés en tant que type "séquence" tel que liste ou tuple. La prise en charge des itérables non séquentiels tels que les générateurs est obsolète à partir de NumPy 1.16 et générera une erreur à l'avenir.
leermeester
111

Une autre option pour l'utilisation de tableaux structurés consiste à utiliser une vue d'un voidtype qui joint la ligne entière en un seul élément:

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 Ajouté np.ascontiguousarraysuite à la recommandation de @ seberg. Cela ralentira la méthode si le tableau n'est pas déjà contigu.

EDIT Ce qui précède peut être légèrement accéléré, peut-être au détriment de la clarté, en faisant:

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

De plus, au moins sur mon système, en termes de performances, il est au même niveau, voire meilleur, que la méthode 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
la source
3
Merci beaucoup. C'est la réponse que je cherchais, pouvez-vous expliquer ce qui se passe dans cette étape b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1]))):?
Akavall
3
@Akavall Il crée une vue de vos données avec un np.voidtype de données de la taille du nombre d'octets sur une ligne complète. C'est similaire à ce que vous obtenez si vous avez un tableau de np.uint8s et que vous le voyez comme np.uint16s, qui combine toutes les deux colonnes en une seule, mais plus flexible.
Jaime
3
@Jaime, pouvez-vous ajouter un np.ascontiguousarrayou similaire pour être généralement sûr (je sais que c'est un peu plus restrictif que nécessaire, mais ...). Les lignes doivent être contiguës pour que la vue fonctionne comme prévu.
seberg
2
@ConstantineEvans Il s'agit d'un ajout récent: dans numpy 1.6, en essayant d'exécuter np.uniquesur un tableau de np.voidretours, une erreur liée au fusionnement n'est pas implémenté pour ce type. Cela fonctionne bien en 1.7.
Jaime
9
Il convient de noter que si cette méthode est utilisée pour les nombres à virgule flottante, il y a un crochet qui -0.ne sera pas égal à +0., alors qu'une comparaison élément par élément aurait -0.==+0.(comme spécifié par la norme float ieee). Voir stackoverflow.com/questions/26782038/…
tom10
29

Si vous voulez éviter les frais de mémoire liés à la conversion en une série de tuples ou une autre structure de données similaire, vous pouvez exploiter les tableaux structurés de numpy.

L'astuce consiste à afficher votre tableau d'origine comme un tableau structuré où chaque élément correspond à une ligne du tableau d'origine. Cela ne fait pas de copie et est assez efficace.

Comme exemple rapide:

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

Pour comprendre ce qui se passe, jetez un œil aux résultats intermédiaires.

Une fois que nous considérons les choses comme un tableau structuré, chaque élément du tableau est une ligne de votre tableau d'origine. (Fondamentalement, c'est une structure de données similaire à une liste de tuples.)

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

Une fois que nous avons exécuté numpy.unique, nous récupérons un tableau structuré:

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 nous devons ensuite voir comme un tableau "normal" ( _stocke le résultat du dernier calcul dans ipython, c'est pourquoi vous voyez _.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])

Et puis remodeler en un tableau 2D ( -1est un espace réservé qui indique à numpy de calculer le nombre correct de lignes, donnez le nombre de colonnes):

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

Évidemment, si vous vouliez être plus concis, vous pourriez l'écrire comme suit:

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)

Ce qui se traduit par:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
Joe Kington
la source
Cela semble en fait très lent, presque aussi lent que l'utilisation de tuples. Apparemment, le tri d'un tableau structuré est lent.
cge
3
@cge - Essayez-le avec des tableaux de plus grande taille. Oui, le tri d'un tableau numpy est plus lent que le tri d'une liste. La vitesse n'est cependant pas la considération principale dans la plupart des cas où vous utilisez des ndarrays. C'est l'utilisation de la mémoire. Une liste de tuples utilisera beaucoup plus de mémoire que cette solution. Même si vous avez suffisamment de mémoire, avec un tableau raisonnablement grand, sa conversion en une liste de tuples a un surcoût supérieur à l'avantage de la vitesse.
Joe Kington
@cge - Ah, je n'ai pas remarqué que vous utilisiez lexsort. Je pensais que vous faisiez référence à l'utilisation d'une liste de tuples. Oui, lexsortc'est probablement la meilleure option dans ce cas. Je l'avais oublié et je suis passé à une solution trop complexe.
Joe Kington
20

np.uniquequand je l'exécute np.random.random(100).reshape(10,10)retourne tous les éléments individuels uniques, mais vous voulez les lignes uniques, donc vous devez d'abord les mettre en tuples:

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

C'est la seule façon dont je vous vois changer les types pour faire ce que vous voulez, et je ne suis pas sûr si l'itération de la liste pour changer en tuples est d'accord avec votre "pas de boucle"

Ryan Saxe
la source
5
+1 C'est clair, court et pythonique. À moins que la vitesse ne soit un réel problème, ce type de solutions devrait prendre le pas sur les réponses complexes et plus votées à cette question de l'OMI.
Bill Cheatham
Je préfère cela à la solution acceptée. La vitesse n'est pas un problème pour moi car je n'ai peut-être que des < 100lignes par appel. Cela décrit précisément la façon dont l'exécution sur des lignes uniques est effectuée.
rayryeng
4
Cela ne fonctionne pas pour mes données, uniquescontient des éléments uniques. Potentiellement, je comprends mal la forme attendue de array- pourriez-vous être plus précis ici?
FooBar
@ ryan-saxe J'aime que ce soit pythonique mais ce n'est pas une bonne solution car la ligne retournée à uniquesest triée (et donc différente des lignes de 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 fonctionne en triant un tableau aplati, puis en vérifiant si chaque élément est égal au précédent. Cela peut être fait manuellement sans aplatir:

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

Cette méthode n'utilise pas de tuples et devrait être beaucoup plus rapide et plus simple que les autres méthodes présentées ici.

Remarque: une version précédente de cela n'avait pas l'ind juste après un [, ce qui signifie que les mauvais indices ont été utilisés. De plus, Joe Kington souligne que cela fait une variété de copies intermédiaires. La méthode suivante fait moins, en faisant une copie triée puis en utilisant des vues de celle-ci:

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

C'est plus rapide et utilise moins de mémoire.

En outre, si vous souhaitez rechercher des lignes uniques dans un ndarray quel que soit le nombre de dimensions dans le tableau, les éléments suivants fonctionneront:

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 problème restant intéressant serait si vous vouliez trier / unique le long d'un axe arbitraire d'un tableau de dimensions arbitraires, ce qui serait plus difficile.

Éditer:

Pour démontrer les différences de vitesse, j'ai exécuté quelques tests en ipython des trois méthodes différentes décrites dans les réponses. Avec votre a exact, il n'y a pas trop de différence, bien que cette version soit un peu plus rapide:

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

Avec un plus grand, cependant, cette version finit par être beaucoup, beaucoup plus rapide:

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
la source
Très agréable! D'un autre côté, cependant, il fait plusieurs copies intermédiaires. (par exemple, a[ind[1:]]une copie, etc.) D'un autre côté, votre solution est généralement 2 à 3 fois plus rapide que la mienne jusqu'à ce que vous soyez à court de RAM.
Joe Kington
Bon point. En fin de compte, ma tentative de supprimer des copies intermédiaires en utilisant uniquement les index a fait que ma méthode utilise plus de mémoire et finit plus lentement que la simple copie triée du tableau, car a_sorted [1:] n'est pas une copie de a_sorted .
cge
Quel est dtypevotre timing? Je pense que vous vous êtes trompé. Sur mon système, appeler np.uniquecomme décrit dans ma réponse est légèrement plus rapide que d'utiliser l'une de vos deux versions np.lexsort. Et il est environ 5 fois plus rapide si le tableau pour trouver des uniques a une forme (10000, 100). Même si vous décidez de réimplémenter ce qui réduit le np.uniquetemps d'exécution (mineur), le regroupement de chaque ligne en un seul objet exécute des comparaisons plus rapides que d'avoir à faire appel np.anyà la comparaison des colonnes, en particulier pour les nombres de colonnes plus élevés.
Jaime
@cge: vous vouliez probablement dire 'np.any' au lieu du standard 'any' qui ne prend pas d'argument de mot clé.
M. Toya
@Jaime - Je crois que dtypec'est juste a.dtype, c'est-à-dire le type de données consultées, comme l'a fait Joe Kington dans sa réponse. S'il y a beaucoup de colonnes, une autre façon (imparfaite!) De faire avancer les choses lexsortest de ne trier que sur quelques colonnes. C'est spécifique aux données car il faut savoir quelles colonnes fournissent suffisamment de variance pour trier parfaitement. Par exemple , a.shape = (60000, 500)- tri sur les 3 premières colonnes: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). Le gain de temps est assez important, mais l'avertissement encore une fois: il pourrait ne pas attraper tous les cas - cela dépend des données.
n1k31t4
9

Voici une autre variante de la réponse pythonique @Greg

np.vstack(set(map(tuple, a)))
divenex
la source
9

J'ai comparé l'alternative suggérée pour la vitesse et j'ai découvert que, étonnamment, la uniquesolution de vue nulle est même un peu plus rapide que le natif de numpy uniqueavec l' axisargument. Si vous cherchez de la vitesse, vous voudrez

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

entrez la description de l'image ici


Code pour reproduire l'intrigue:

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
la source
1
Très belle réponse, un point mineur vstack_dict:, n'utilise jamais de dict, les accolades sont une compréhension d'ensemble, et donc son comportement est presque identique à vstatck_set. Depuis, la vstack_dictligne de performance est manquante pour le graphique, il semble qu'elle soit simplement couverte par le vstack_setgraphique de performance, car ils sont tellement similaires!
Akavall
Merci pour la réponse. J'ai amélioré l'intrigue pour inclure une seule vstackvariante.
Nico Schlömer
8

Je n'ai aimé aucune de ces réponses, car aucune ne gère les tableaux à virgule flottante dans un sens d'algèbre linéaire ou d'espace vectoriel, où deux lignes étant «égales» signifie «à l'intérieur d'un some». La seule réponse qui a un seuil de tolérance, https://stackoverflow.com/a/26867764/500207 , a pris le seuil pour être à la fois précision élémentaire et décimale , ce qui fonctionne dans certains cas mais n'est pas aussi mathématiquement général qu'un vraie distance vectorielle.

Voici ma version:

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 fonction du domaine public ci-dessus utilise scipy.spatial.distance.pdistpour trouver la distance euclidienne (personnalisable) entre chaque paire de lignes. Ensuite, il compare chaque distance à une threshancienne pour trouver les lignes qui sont à l'intérieur les threshunes des autres, et renvoie une seule ligne de chaque threshcluster.

Comme indiqué, la distance metricn'a pas besoin d'être euclidienne - pdistpeut calculer diverses distances, y compris cityblock(norme Manhattan) et cosine(l'angle entre les vecteurs).

Si thresh=0(par défaut), les lignes doivent être à peu près exactes pour être considérées comme «uniques». Autres bonnes valeurs pour threshune précision machine à l'échelle, c'est-à-dire thresh=np.spacing(1)*1e3.

Ahmed Fasih
la source
Meilleure réponse. Merci. C'est la réponse la plus (mathématiquement) généralisée écrite jusqu'à présent. Il considère une matrice comme un ensemble de points de données ou d'échantillons dans l'espace à N dimensions et trouve une collection de points identiques ou similaires (la similitude étant définie par la distance euclidienne ou par toute autre méthode). Ces points peuvent être des points de données qui se chevauchent ou des quartiers très proches. À la fin, une collection de points identiques ou similaires est remplacée par l'un des points (dans la réponse ci-dessus par un premier point) appartenant au même ensemble. Cela permet de réduire la redondance à partir d'un nuage de points.
Sanchit
@Sanchit aha, c'est un bon point, au lieu de choisir le «premier» point (en fait, il pourrait être effectivement aléatoire, car cela dépend de la façon dont Python stocke les points dans a set) comme représentatif de chaque threshquartier de taille, la fonction pourrait permettre à la l'utilisateur pour spécifier comment choisir ce point, par exemple, utiliser la «médiane» ou le point le plus proche du centroïde, etc.
Ahmed Fasih
Sûr. Sans aucun doute. Je viens de mentionner le premier point, car c'est ce que fait votre programme, ce qui est tout à fait correct.
Sanchit
Juste une correction - j'ai dit à tort ci-dessus que la ligne qui serait choisie pour chaque thresh-cluster serait aléatoire en raison de la nature non ordonnée de set. Bien sûr , c'est un brainfart de ma part, les settuples stocke des index qui sont thresh-neighborhood, donc cela findRows fait en retour de fait, pour chaque thresh-cluster, la première ligne en elle.
Ahmed Fasih
3

Pourquoi ne pas utiliser drop_duplicatesde pandas:

>>> 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
la source
J'adore cette réponse. Bien sûr, il n'utilise pas numpy directement, mais pour moi, c'est celui qui est le plus facile à comprendre tout en étant rapide.
noctilux
3

Le paquet numpy_indexed (avertissement: je suis son auteur) enveloppe la solution publiée par Jaime dans une interface agréable et testée, ainsi que de nombreuses autres fonctionnalités:

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

np.unique fonctionne avec une liste de tuples:

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

Avec une liste de listes, il soulève un TypeError: unhashable type: 'list'

codeape
la source
ne semble pas fonctionner sur le mien. Chaque tuple est
composé de
ne fonctionne pas, il renvoie une liste d'éléments non tuples
Mohanad Kaleia
1

Sur la base de la réponse de cette page, j'ai écrit une fonction qui reproduit la capacité de la fonction de MATLAB unique(input,'rows'), avec la fonctionnalité supplémentaire d'accepter la tolérance pour vérifier l'unicité. Il renvoie également les indices tels que c = data[ia,:]et data = c[ic,:]. Veuillez signaler si vous constatez des écarts ou des erreurs.

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
la source
1

Au-delà de @Jaime excellente réponse, une autre façon de réduire une ligne consiste à utiliser a.strides[0](en supposant qu'il aest C-contigu) qui est égal à a.dtype.itemsize*a.shape[0]. En outre void(n)est un raccourci pour dtype((void,n)). on arrive enfin à cette version la plus courte:

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

Pour

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
BM
la source
0

Pour un usage général comme les tableaux imbriqués multidimensionnels 3D ou supérieurs, essayez ceci:

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

qui satisfait votre jeu de données 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)

donne:

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

Mais aussi des tableaux 3D comme:

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)

donne:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
Tara
la source
Utiliser unique return_indexcomme Jaime devrait returnsimplifier cette dernière ligne. Il suffit d'indexer l'original arsur l'axe droit.
hpaulj
0

Aucune de ces réponses n'a fonctionné pour moi. Je suppose que mes lignes uniques contiennent des chaînes et non des nombres. Cependant, cette réponse d'un autre fil a fonctionné:

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

Vous pouvez utiliser les méthodes de la liste .count () et .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
la source
0

Nous pouvons réellement transformer le tableau numpy numérique mxn en tableau de chaînes numpy mx 1, veuillez essayer d'utiliser la fonction suivante, elle fournit count , inverse_idx et etc, tout comme 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

Exemple:

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
la source
-1

Permet d'obtenir l'intégralité de la matrice numpy sous forme de liste, puis de supprimer les doublons de cette liste, et enfin de renvoyer notre liste unique dans une 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]])
Mahdi Ghelichi
la source
-3

La solution la plus simple consiste à faire des lignes un élément unique en les transformant en chaînes. Chaque ligne peut ensuite être comparée dans son ensemble pour son caractère unique à l'aide de numpy. Cette solution est généralisable, il vous suffit de remodeler et de transposer votre tableau pour d'autres combinaisons. Voici la solution au problème fourni.

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)    

Va donner:

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

Envoyer mon prix Nobel par la poste

Dave Pena
la source
Très inefficace et sujet aux erreurs, par exemple avec différentes options d'impression. Les autres options sont clairement préférables.
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
la source