web-dev-qa-db-fra.com

Déterminer les valeurs en double dans un tableau

Supposons que j'ai un tableau

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

Comment puis-je (efficacement, Pythoniquement) trouver quels éléments de a sont des doublons (c'est-à-dire des valeurs non uniques)? Dans ce cas, le résultat serait array([1, 3, 3]) ou éventuellement array([1, 3]) s'il est efficace.

J'ai trouvé quelques méthodes qui semblent fonctionner:

Masquage

m = np.zeros_like(a, dtype=bool)
m[np.unique(a, return_index=True)[1]] = True
a[~m]

Définir les opérations

a[~np.in1d(np.arange(len(a)), np.unique(a, return_index=True)[1], assume_unique=True)]

Celui-ci est mignon mais probablement illégal (car a n'est pas réellement unique):

np.setxor1d(a, np.unique(a), assume_unique=True)

Histogrammes

u, i = np.unique(a, return_inverse=True)
u[np.bincount(i) > 1]

Tri

s = np.sort(a, axis=None)
s[:-1][s[1:] == s[:-1]]

Pandas

s = pd.Series(a)
s[s.duplicated()]

Y a-t-il quelque chose que j'ai manqué? Je ne cherche pas nécessairement une solution uniquement numpy, mais elle doit fonctionner avec des types de données numpy et être efficace sur des ensembles de données de taille moyenne (jusqu'à 10 millions de taille).


Conclusions

Test avec un ensemble de données de 10 millions de tailles (sur un Xeon à 2,8 GHz):

a = np.random.randint(10**7, size=10**7)

Le tri le plus rapide, à 1,1 s. Le douteux xor1d Est deuxième à 2,6 s, suivi du masquage et Pandas Series.duplicated À 3,1 s, bincount à 5,6 s et in1d et le setdiff1d de senderle à la fois à 7,3 s. Le Counter de Steven est seulement un peu plus lent, à 10,5 s; derrière le Counter.most_common de Burhan à 110 s et la soustraction Counter de DSM à 360 s.

Je vais utiliser le tri pour les performances, mais j'accepte la réponse de Steven parce que les performances sont acceptables et cela se sent plus clair et plus Pythonic.

Edit: découvert la solution Pandas. Si Pandas est disponible, il est clair et fonctionne bien.

46
ecatmur

Je pense que c'est plus clair fait en dehors de numpy. Vous devrez le synchroniser avec vos solutions numpy si vous êtes soucieux de la vitesse.

>>> import numpy as np
>>> from collections import Counter
>>> a = np.array([1, 2, 1, 3, 3, 3, 0])
>>> [item for item, count in Counter(a).iteritems() if count > 1]
[1, 3]

note: Ceci est similaire à la réponse de Burhan Khalid, mais l'utilisation de iteritems sans indice dans la condition devrait être plus rapide.

21
Steven Rumbalski

Depuis numpy version 1.9.0, np.unique a un argument return_counts ce qui simplifie grandement votre tâche:

u, c = np.unique(a, return_counts=True)
dup = u[c > 1]

Ceci est similaire à l'utilisation de Counter , sauf que vous obtenez une paire de tableaux au lieu d'un mappage. Je serais curieux de voir comment ils fonctionnent les uns par rapport aux autres.

18
Mad Physicist

Les gens ont déjà suggéré Counter variantes, mais en voici une qui n'utilise pas un listcomp:

>>> from collections import Counter
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> (Counter(a) - Counter(set(a))).keys()
[1, 3]

[Publié non pas parce qu'il est efficace - ce n'est pas le cas - mais parce que je pense que c'est mignon que vous puissiez soustraire Counter instances.]

12
DSM

Pour Python 2.7+

>>> import numpy
>>> from collections import Counter
>>> n = numpy.array([1,1,2,3,3,3,0])
>>> [x[1] for x in Counter(n).most_common() if x[0] > 1]
[3, 1]
7
Burhan Khalid

Voici une autre approche utilisant des opérations d'ensemble qui, je pense, est un peu plus simple que celles que vous proposez:

>>> indices = np.setdiff1d(np.arange(len(a)), np.unique(a, return_index=True)[1])
>>> a[indices]
array([1, 3, 3])

Je suppose que vous demandez des solutions numpy uniquement, car si ce n'est pas le cas, il est très difficile de discuter avec simplement l'utilisation d'un Counter à la place. Je pense que vous devriez cependant rendre cette exigence explicite.

5
senderle

Si a est composé de petits entiers, vous pouvez utiliser numpy.bincount directement:

import numpy as np

a = np.array([3, 2, 2, 0, 4, 3])
counts = np.bincount(a)
print np.where(counts > 1)[0]
# array([2, 3])

C'est très similaire à votre méthode "histogramme", qui est celle que j'utiliserais si a n'était pas composé de petits entiers.

4
Bi Rico

Si le tableau est un tableau numpy trié, faites simplement:

a = np.array([1, 2, 2, 3, 4, 5, 5, 6])
rep_el = a[np.diff(a) == 0]
3
Thomas Antony

J'ajoute ma solution à la pile pour cette question de 3 ans parce qu'aucune des solutions ne correspond à ce que je voulais ou utilisais des bibliothèques à part numpy. Cette méthode trouve à la fois les indices des doublons et les valeurs des ensembles de doublons distinct.

import numpy as np

A = np.array([1,2,3,4,4,4,5,6,6,7,8])

# Record the indices where each unique element occurs.
list_of_dup_inds = [np.where(a == A)[0] for a in np.unique(A)]

# Filter out non-duplicates.
list_of_dup_inds = filter(lambda inds: len(inds) > 1, list_of_dup_inds)

for inds in list_of_dup_inds: print inds, A[inds]
# >> [3 4 5] [4 4 4]
# >> [7 8] [6 6]
3
Matt Hancock
>>> import numpy as np

>>> a=np.array([1,2,2,2,2,3])

>>> uniques, uniq_idx, counts = np.unique(a,return_index=True,return_counts=True)
>>> duplicates = a[ uniq_idx[counts>=2] ]  # <--- Get duplicates

Si vous souhaitez également récupérer les orphelins:

>>> orphans = a[ uniq_idx[counts==1] ] 
0
user563325