La suppression des doublons dans une liste de listes
J'ai une liste de listes en Python:
k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
Et je veux supprimer dupliquer des éléments. A si c'est une liste normale pas de listes que je pouvais utilisé set
. Mais regrettable que la liste n'est pas hashable et ne peut pas faire de ensemble de listes. Seulement de tuples. Donc, je peux tourner toutes les listes les tuples puis utiliser ensemble et de retour à des listes. Mais ce n'est pas rapide.
Comment ce fait de la manière la plus efficace?
Le résultat de la liste ci-dessus devraient être:
k = [[5, 6, 2], [1, 2], [3], [4]]
Je ne se soucient pas de préserver l'ordre.
Remarque: cette question est similaire mais pas tout à fait ce dont j'ai besoin. Cherché mais n'ai pas trouver la copie exacte.
Benchmarking:
import itertools, time
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
if self.name:
print '[%s]' % self.name,
print 'Elapsed: %s' % (time.time() - self.tstart)
k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] * 5
N = 100000
print len(k)
with Timer('set'):
for i in xrange(N):
kt = [tuple(i) for i in k]
skt = set(kt)
kk = [list(i) for i in skt]
with Timer('sort'):
for i in xrange(N):
ks = sorted(k)
dedup = [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]
with Timer('groupby'):
for i in xrange(N):
k = sorted(k)
dedup = list(k for k, _ in itertools.groupby(k))
with Timer('loop in'):
for i in xrange(N):
new_k = []
for elem in k:
if elem not in new_k:
new_k.append(elem)
"boucle" (quadratique de la méthode) le plus rapide de tous pour de courtes listes. Pendant de longues listes, il est plus rapide que tout le monde sauf groupby méthode. Est-il logique?
Pour la courte liste (celle dans le code), 100000 itérations:
[set] Elapsed: 1.3900001049
[sort] Elapsed: 0.891000032425
[groupby] Elapsed: 0.780999898911
[loop in] Elapsed: 0.578000068665
Pour plus de la liste (celui dans le code dupliqué 5 fois):
[set] Elapsed: 3.68700003624
[sort] Elapsed: 3.43799996376
[groupby] Elapsed: 1.03099989891
[loop in] Elapsed: 1.85900020599
- Par "ce n'est pas rapide", vous voulez dire que vous avez chronométré et il n'est pas assez rapide pour votre application, ou que vous pensez qu'il n'est pas rapide?
- il semble juste comme trop de copie d'être intelligent méthode. désolé, le gut feeling. copie des listes de n-uplets, puis dans le jeu, puis retour à la liste de listes (copie de nouveau tuples, listes)
- ce n'est pas la façon Python fonctionne, rien ne pourra être copié, juste de nouveaux conteneurs pour les éléments existants ( bien que pour les entiers, c'est à peu près le même )
- 1. les horaires pour les méthodes à l'aide de tri sont dégonflés, parce que le "k" est le rebond de la triés variante. 2. La dernière méthode est plus rapide, car la méthode utilisée pour générer les données de test vous laisse avec au plus 4 éléments distincts. Essayez de qqch. comme K = [[int(u) pour u dans str(aléatoire.randrange(1, 1000))] for _ in range(100)]
- merci fixe. mais encore, la méthode de boucle est rapide, même lorsqu'il n'existe qu'un seul doublon dans la liste des 10
- Oui, pour les petits de saisie des listes, "boucle" sera plus rapide. Sa complexité est en O(mn), où m est le nombre d'éléments uniques (de n). De sorte que le moins unique, il y a d'éléments, plus il est (c'est à dire linéaire pour les listes avec un seul et unique élément). Pour de courtes listes, la constante de facteurs à la fois de "trier" et "groupe" sont trop élevés pour eux d'être plus rapide que la "boucle".
- pour mesurer la performance d'un cas spécifique, l'utilisation de la bibliothèque standard du module de
timeit
à l'invite du shell (plus simple et plus solide, beaucoup mieux que d'intégrer dans le code). Je vais modifier mon Un de le montrer. - comme Torsten dit, la boucle est rapide avec de petits échantillons. C'est tout à fait normal dans des algorithmes engager dans de petits coûts constants (création d'une liste vide vd. création d'une liste à partir d'un n-uplet). Mais il croit toujours quadratiquement, ce qui signifie que les méthodes linéaires sera plus rapide que la boucle à un certain point.
- Je voudrais juste faire remarquer que vous êtes seuls des essais sur ce que j'appelle "court" des listes. Une "longue" liste pour m'aurait peut-être ou 10k 100k éléments, peut-être quelques millions de dollars.
Vous devez vous connecter pour publier un commentaire.
itertools
offre souvent la manière la plus rapide et la plus puissante des solutions à ce genre de problèmes, et est bien vaut la peine de s'intimement familier avec!-)Modifier: comme je le mentionne dans un commentaire, normal optimisation des efforts se sont concentrés sur les grandes entrées (le big-O approche) parce que c'est tellement plus facile qu'il offre de bons rendements sur les efforts. Mais parfois (essentiellement pour "tragiquement crucial goulets d'étranglement" dans intérieure, profonde de boucles de code qui pousse les limites de la performance des limites), on peut avoir besoin d'aller dans beaucoup plus de détails, fournissant des distributions de probabilité, décider qui des mesures de rendement pour optimiser (peut-être la limite supérieure ou la 90e centile est plus important que la moyenne ou la médiane, selon les applications), en procédant éventuellement-heuristique vérifie au début pour choisir les différents algorithmes en fonction des données d'entrée caractéristiques, et ainsi de suite.
Des mesures précises de "point" de la performance (code vs code B pour une entrée spécifique) sont une partie de cette très coûteux, et de la bibliothèque standard du module de
timeit
aide ici. Cependant, il est plus facile de l'utiliser à l'invite du shell. Pour exemple, voici un court module de présenter l'approche générale de ce problème, enregistrez-le aunodup.py
:Remarque le test de cohérence (effectuée lorsque vous venez de le faire
python nodup.py
) et la base technique de levage (faire de la constante globale de noms local pour chaque fonction de la vitesse), de mettre les choses sur un pied d'égalité.Maintenant, nous pouvons exécuter des vérifications sur le petit exemple de liste:
confirmant que l'équation du second degré approche a petit-assez constantes pour le rendre attrayant pour les petites listes avec quelques valeurs dupliquées. Avec une courte liste sans doublons:
quadratiques approche n'est pas mauvaise, mais les trier et grouper sont les meilleures. Etc, etc.
Si (comme l'obsession de la performance suggère) cette opération est à un noyau boucle intérieure de votre repousser les limites de l'application, il vaut la peine d'essayer la même série de tests sur d'autres représentant des échantillons d'entrée, éventuellement détecter une simple mesure de manière heuristique vous permettent de choisir l'une ou l'autre approche (mais la mesure doit être rapide, bien sûr).
Il est également bien la peine d'envisager de garder une représentation différente pour
k
-- pourquoi a-t-elle à être une liste de listes plutôt que d'un ensemble de n-uplets en premier lieu? Si la double tâche de suppression est fréquent, et le profilage montre le programme du goulot d'étranglement des performances, la tenue d'un ensemble de n-uplets tout le temps et d'obtenir une liste de listes de si et, le cas échéant, pourrait être plus rapide dans l'ensemble, par exemple.k
que la liste du nom et de l'k
comme la variable d'itérateur?list(k for k,_ in itertools.groupby(k))
, je suis sûr que lek
danslist(k for k...
est différente de lak
dansgrouby(k)
, et si je ont été de plus en plus sûr que serais je suggère de renommer la liste. Ma question ci-dessus peut-être pas utiliser le bon vocabulairelist(x for x,_ in itertools.groupby(k))
?k.sort()
fonction supprime les ordres dans cette liste. Je ne savais pas ce et a dû passer des heures de débogage, comme dans mon cas d'utilisation des commandes de la matière. Pour le résoudre, avant de les trier vous pouvez utilisercopy.copy()
de faire une copie de la liste!De le faire manuellement, la création d'une nouvelle
k
liste et ajouter des entrées pas trouvé à ce jour:Simple à comprendre, et de vous conserver l'ordre de la première occurrence de chaque élément qui devrait être utile, mais je suppose que c'est quadratique en complexité à mesure que vous êtes à la recherche de l'ensemble de
new_k
pour chaque élément.k = ([[1, 2], [4], [5, 6, 2], [1, 2], [3], [5, 2], [6], [8], [9]] +[[x] for x in range(1000)]) *5
fera apparaître le comportement quadratique bienJe ne sais pas si c'est forcément plus rapide, mais vous n'avez pas à utiliser pour les tuples et les décors.
random
, et le temps avectime
.Même votre "longue", la liste est assez courte. Aussi, avez-vous choisis pour correspondre aux données réelles? Les performances varient avec ce que ces données ressemblent réellement. Par exemple, vous avez une courte liste répétés à plusieurs reprises de faire une liste plus longue. Cela signifie que l'équation de la solution est linéaire dans tes repères, mais pas dans la réalité.
Pour fait-listes de grande taille, le code est votre meilleur pari—c'est linéaire (même si l'espace-faim). Les trier et grouper les méthodes sont en O(n log n) et la boucle de la méthode est évidemment quadratique, afin de savoir comment ces sera mis à l'échelle en tant que n devient très grand. Si c'est la taille réelle des données que vous analysez, alors qui s'en soucie? Il est minuscule.
D'ailleurs, je vois une notable accélération si je n'ai pas la forme intermédiaire de la liste à faire le jeu, c'est-à-dire si je remplace
avec
La vraie solution peut dépendre plus d'informations: Êtes-vous sûr que d'une liste de listes est vraiment la représentation-vous besoin?
Liste de tuple et {} peut être utilisé pour supprimer les doublons
Tous les
set
des solutions à ce problème jusqu'à présent nécessitent la création d'un ensemble deset
avant itération.Il est possible de faire cette paresseux, et en même temps préserver l'ordre, par l'itération sur la liste de listes et de les ajouter à un "vu"
set
. Alors seulement le rendement d'une liste si elle n'est trouvé dans ce trackerset
.Ce
unique_everseen
la recette est disponible dans leitertools
docs. Il est également disponible dans la 3ème partietoolz
bibliothèque:Noter que
tuple
de conversion est nécessaire parce que les listes ne sont pas hashable.Créer un dictionnaire avec un tuple comme les clés, et d'imprimer les touches.
Cela devrait fonctionner.
Étrangement, les réponses ci-dessus supprime les "doublons" mais que si je veux enlever le double de la valeur aussi??
Les éléments suivants doivent être utiles et de ne pas créer un nouvel objet dans la mémoire!
et l'o/p est:
L'autre sans doute plus générique et plus simple solution consiste à créer un dictionnaire identifié par la chaîne de version de les objets et obtenir les valeurs de() à la fin:
Le hic, c'est que cela ne fonctionne que pour les objets dont la représentation sous forme de chaîne est suffisamment bons clé unique (ce qui est vrai pour la plupart des objets natifs).