Wie finde ich die Duplikate in einer Liste und erstelle damit eine weitere Liste?


437

Wie kann ich die Duplikate in einer Python-Liste finden und eine weitere Liste der Duplikate erstellen? Die Liste enthält nur Ganzzahlen.



1
Möchten Sie die Duplikate einmal oder jedes Mal wieder sehen?
Moooeeeep

Ich denke, dies wurde hier mit viel mehr Effizienz beantwortet. stackoverflow.com/a/642919/1748045 Kreuzung ist eine eingebaute Methode der Menge und sollte genau das tun, was erforderlich ist
Tom Smith

Antworten:


544

Verwenden Sie zum Entfernen von Duplikaten set(a). So drucken Sie Duplikate:

a = [1,2,3,2,1,5,6,5,5,5]

import collections
print([item for item, count in collections.Counter(a).items() if count > 1])

## [1, 2, 5]

Beachten Sie, dass dies Counternicht besonders effizient ist ( Timings ) und hier wahrscheinlich übertrieben ist. setwird besser abschneiden. Dieser Code berechnet eine Liste eindeutiger Elemente in der Quellreihenfolge:

seen = set()
uniq = []
for x in a:
    if x not in seen:
        uniq.append(x)
        seen.add(x)

oder genauer:

seen = set()
uniq = [x for x in a if x not in seen and not seen.add(x)]    

Ich empfehle den letzteren Stil nicht, da nicht klar ist, was er not seen.add(x)tut (die set- add()Methode gibt immer zurück None, daher ist dies erforderlichnot ).

So berechnen Sie die Liste der duplizierten Elemente ohne Bibliotheken:

seen = {}
dupes = []

for x in a:
    if x not in seen:
        seen[x] = 1
    else:
        if seen[x] == 1:
            dupes.append(x)
        seen[x] += 1

Wenn Listenelemente nicht hashbar sind, können Sie keine Mengen / Diktate verwenden und müssen auf eine quadratische Zeitlösung zurückgreifen (vergleichen Sie jede mit jeder). Zum Beispiel:

a = [[1], [2], [3], [1], [5], [3]]

no_dupes = [x for n, x in enumerate(a) if x not in a[:n]]
print no_dupes # [[1], [2], [3], [5]]

dupes = [x for n, x in enumerate(a) if x in a[:n]]
print dupes # [[1], [3]]

2
@eric: Ich denke, es ist O(n), weil es die Liste nur einmal wiederholt und Set-Lookups sind O(1).
Georg

3
@Hugo, um die Liste der Duplikate anzuzeigen, müssen wir nur eine neue Liste mit dem Namen dup erstellen und eine else-Anweisung hinzufügen. Zum Beispiel:dup = [] else: dup.append(x)
Chris Nielsen

4
@oxeimon: Sie haben dies wahrscheinlich, aber Ihr Druck wird mit Klammern in Python 3print()
Moberg

4
Konvertieren Sie Ihre Antwort für set (), um nur Duplikate zu erhalten. seen = set()danndupe = set(x for x in a if x in seen or seen.add(x))
Ta946

2
Für Python 3.x: print ([Element für Element, Anzahl in Sammlungen. Zähler (a) .items (), wenn Anzahl> 1])
kibitzforu

327
>>> l = [1,2,3,4,4,5,5,6,1]
>>> set([x for x in l if l.count(x) > 1])
set([1, 4, 5])

2
Gibt es einen Grund, warum Sie ein Listenverständnis anstelle eines Generatorverständnisses verwenden?

64
In der Tat eine einfache Lösung, aber die Komplexität wird quadriert, da jede count () die Liste erneut analysiert. Verwenden Sie sie daher nicht für große Listen.
Danuker

4
@ JohnJ, Blasensortierung ist auch einfach und funktioniert. Das heißt nicht, dass wir es benutzen sollten!
John La Rooy

@JohnLaRooy Das bedeutet eigentlich, dass wir es nicht verwenden sollten, da es fast immer eine effizientere (und einfachere) Art der Sortierung gibt.
lostsoul29

1
@watsonic: Ihr "einfacher Schalter" kann die Zeitkomplexität im allgemeinen Fall nicht von quadratisch auf quadratisch reduzieren. Ersetzen lmit set(l)nur reduziert die Worst-Case - Zeitkomplexität und damit nicht nichts mit dieser Antwort , die größere Effizienz Bedenken auszuräumen. Es war wahrscheinlich doch nicht so einfach. Kurz gesagt, tu das nicht.
Cecil Curry

82

Sie brauchen die Zählung nicht, nur ob der Gegenstand zuvor gesehen wurde oder nicht. Diese Antwort wurde an dieses Problem angepasst :

def list_duplicates(seq):
  seen = set()
  seen_add = seen.add
  # adds all elements it doesn't know yet to seen and all other to seen_twice
  seen_twice = set( x for x in seq if x in seen or seen_add(x) )
  # turn the set into a list (as requested)
  return list( seen_twice )

a = [1,2,3,2,1,5,6,5,5,5]
list_duplicates(a) # yields [1, 2, 5]

Nur für den Fall, dass Geschwindigkeit wichtig ist, hier einige Zeitpunkte:

# file: test.py
import collections

def thg435(l):
    return [x for x, y in collections.Counter(l).items() if y > 1]

def moooeeeep(l):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in l if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def RiteshKumar(l):
    return list(set([x for x in l if l.count(x) > 1]))

def JohnLaRooy(L):
    seen = set()
    seen2 = set()
    seen_add = seen.add
    seen2_add = seen2.add
    for item in L:
        if item in seen:
            seen2_add(item)
        else:
            seen_add(item)
    return list(seen2)

l = [1,2,3,2,1,5,6,5,5,5]*100

Hier sind die Ergebnisse: (gut gemacht @JohnLaRooy!)

$ python -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
10000 loops, best of 3: 74.6 usec per loop
$ python -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 91.3 usec per loop
$ python -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 266 usec per loop
$ python -mtimeit -s 'import test' 'test.RiteshKumar(test.l)'
100 loops, best of 3: 8.35 msec per loop

Interessanterweise ändert sich neben den Timings selbst auch das Ranking geringfügig, wenn Pypy verwendet wird. Am interessantesten ist, dass der Counter-basierte Ansatz enorm von den Optimierungen von pypy profitiert, während der von mir vorgeschlagene Methoden-Caching-Ansatz fast keine Wirkung zu haben scheint.

$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
100000 loops, best of 3: 17.8 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
10000 loops, best of 3: 23 usec per loop
$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
10000 loops, best of 3: 39.3 usec per loop

Anscheinend hängt dieser Effekt mit der "Duplizierung" der Eingabedaten zusammen. Ich habe l = [random.randrange(1000000) for i in xrange(10000)]diese Ergebnisse festgelegt und erhalten:

$ pypy -mtimeit -s 'import test' 'test.moooeeeep(test.l)'
1000 loops, best of 3: 495 usec per loop
$ pypy -mtimeit -s 'import test' 'test.JohnLaRooy(test.l)'
1000 loops, best of 3: 499 usec per loop
$ pypy -mtimeit -s 'import test' 'test.thg435(test.l)'
1000 loops, best of 3: 1.68 msec per loop

6
Nur neugierig - was ist der Zweck von found_add = found.add hier?
Rob

3
@Rob Auf diese Weise rufen Sie einfach die Funktion auf, die Sie zuvor einmal nachgeschlagen haben. Andernfalls müssten Sie die Mitgliedsfunktion addjedes Mal nachschlagen (eine Wörterbuchabfrage), wenn eine Einfügung erforderlich wäre.
Moooeeeep

überprüft mit meinen eigenen Daten und Ipythons% timeit sieht Ihre Methode im Test am schnellsten aus, ABER: "Der langsamste Lauf dauerte 4,34-mal länger als der schnellste. Dies könnte bedeuten, dass ein Zwischenergebnis zwischengespeichert wird"
Joop

1
@moooeeeep, ich habe Ihrem Skript eine weitere Version hinzugefügt, damit Sie es versuchen können :) Versuchen pypySie es auch, wenn Sie es zur Hand haben und Geschwindigkeit anstreben .
John La Rooy

@ JohnLaRooy Schöne Leistungsverbesserung! Interessanterweise verbessert sich der Counter-basierte Ansatz erheblich, wenn ich zur Bewertung der Ergebnisse Pypy verwendet habe.
Moooeeeep

42

Sie können verwenden iteration_utilities.duplicates:

>>> from iteration_utilities import duplicates

>>> list(duplicates([1,1,2,1,2,3,4,2]))
[1, 1, 2, 2]

oder wenn Sie nur eines von jedem Duplikat möchten, kann dies kombiniert werden mit iteration_utilities.unique_everseen:

>>> from iteration_utilities import unique_everseen

>>> list(unique_everseen(duplicates([1,1,2,1,2,3,4,2])))
[1, 2]

Es kann auch nicht zerhackbare Elemente verarbeiten (jedoch auf Kosten der Leistung):

>>> list(duplicates([[1], [2], [1], [3], [1]]))
[[1], [1]]

>>> list(unique_everseen(duplicates([[1], [2], [1], [3], [1]])))
[[1]]

Das ist etwas, das nur einige der anderen Ansätze hier bewältigen können.

Benchmarks

Ich habe einen schnellen Benchmark durchgeführt, der die meisten (aber nicht alle) der hier genannten Ansätze enthält.

Der erste Benchmark umfasste nur einen kleinen Bereich von Listenlängen, da einige Ansätze dies getan haben O(n**2) Verhalten aufweisen.

In den Diagrammen stellt die y-Achse die Zeit dar, daher bedeutet ein niedrigerer Wert besser. Es wird auch ein Protokoll-Protokoll aufgezeichnet, damit der breite Wertebereich besser visualisiert werden kann:

Geben Sie hier die Bildbeschreibung ein

Entfernen der O(n**2)Ansätze Ich habe einen weiteren Benchmark mit bis zu einer halben Million Elementen in einer Liste durchgeführt:

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, ist der iteration_utilities.duplicatesAnsatz schneller als jeder andere Ansatz und sogar verkettetunique_everseen(duplicates(...)) war schneller oder gleich schnell als die anderen Ansätze.

Eine weitere interessante Sache, die hier zu beachten ist, ist, dass die Pandas-Ansätze für kleine Listen sehr langsam sind, aber leicht für längere Listen konkurrieren können.

Da diese Benchmarks jedoch zeigen, dass die meisten Ansätze ungefähr gleich gut abschneiden, spielt es keine Rolle, welcher verwendet wird (mit Ausnahme der 3, die O(n**2)Laufzeit hatten).

from iteration_utilities import duplicates, unique_everseen
from collections import Counter
import pandas as pd
import itertools

def georg_counter(it):
    return [item for item, count in Counter(it).items() if count > 1]

def georg_set(it):
    seen = set()
    uniq = []
    for x in it:
        if x not in seen:
            uniq.append(x)
            seen.add(x)

def georg_set2(it):
    seen = set()
    return [x for x in it if x not in seen and not seen.add(x)]   

def georg_set3(it):
    seen = {}
    dupes = []

    for x in it:
        if x not in seen:
            seen[x] = 1
        else:
            if seen[x] == 1:
                dupes.append(x)
            seen[x] += 1

def RiteshKumar_count(l):
    return set([x for x in l if l.count(x) > 1])

def moooeeeep(seq):
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    seen_twice = set( x for x in seq if x in seen or seen_add(x) )
    # turn the set into a list (as requested)
    return list( seen_twice )

def F1Rumors_implementation(c):
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in zip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def F1Rumors(c):
    return list(F1Rumors_implementation(c))

def Edward(a):
    d = {}
    for elem in a:
        if elem in d:
            d[elem] += 1
        else:
            d[elem] = 1
    return [x for x, y in d.items() if y > 1]

def wordsmith(a):
    return pd.Series(a)[pd.Series(a).duplicated()].values

def NikhilPrabhu(li):
    li = li.copy()
    for x in set(li):
        li.remove(x)

    return list(set(li))

def firelynx(a):
    vc = pd.Series(a).value_counts()
    return vc[vc > 1].index.tolist()

def HenryDev(myList):
    newList = set()

    for i in myList:
        if myList.count(i) >= 2:
            newList.add(i)

    return list(newList)

def yota(number_lst):
    seen_set = set()
    duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
    return seen_set - duplicate_set

def IgorVishnevskiy(l):
    s=set(l)
    d=[]
    for x in l:
        if x in s:
            s.remove(x)
        else:
            d.append(x)
    return d

def it_duplicates(l):
    return list(duplicates(l))

def it_unique_duplicates(l):
    return list(unique_everseen(duplicates(l)))

Benchmark 1

from simple_benchmark import benchmark
import random

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, RiteshKumar_count, moooeeeep, 
    F1Rumors, Edward, wordsmith, NikhilPrabhu, firelynx,
    HenryDev, yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 12)}

b = benchmark(funcs, args, 'list size')

b.plot()

Benchmark 2

funcs = [
    georg_counter, georg_set, georg_set2, georg_set3, moooeeeep, 
    F1Rumors, Edward, wordsmith, firelynx,
    yota, IgorVishnevskiy, it_duplicates, it_unique_duplicates
]

args = {2**i: [random.randint(0, 2**(i-1)) for _ in range(2**i)] for i in range(2, 20)}

b = benchmark(funcs, args, 'list size')
b.plot()

Haftungsausschluss

1 Dies ist aus einer Drittanbieter-Bibliothek, die ich geschrieben habe : iteration_utilities.


1
Ich werde hier meinen Hals rausstrecken und vorschlagen, eine maßgeschneiderte Bibliothek zu schreiben, um die Arbeit in C statt in Python zu erledigen. Dies ist wahrscheinlich nicht der Geist der Antwort, nach der gesucht wurde - aber das ist ein legitimer Ansatz! Ich mag die Breite der Antwort und die grafische Darstellung der Ergebnisse - sehr schön zu sehen, dass sie konvergieren. Ich frage mich, ob sie sich jemals kreuzen, wenn die Eingaben weiter zunehmen! Frage: Was ist das Ergebnis bei meist sortierten Listen im Gegensatz zu völlig zufälligen Listen?
F1Rumors

30

Ich bin auf diese Frage gestoßen, als ich mich mit etwas Ähnlichem befasst habe - und habe mich gefragt, warum niemand eine generatorbasierte Lösung angeboten hat? Die Lösung dieses Problems wäre:

>>> print list(getDupes_9([1,2,3,2,1,5,6,5,5,5]))
[1, 2, 5]

Ich befasste mich mit der Skalierbarkeit und testete daher verschiedene Ansätze, einschließlich naiver Elemente, die auf kleinen Listen gut funktionieren, aber schrecklich skalieren, wenn Listen größer werden (Hinweis: Es wäre besser gewesen, timeit zu verwenden, aber dies ist illustrativ).

Ich habe @moooeeeep zum Vergleich hinzugefügt (es ist beeindruckend schnell: am schnellsten, wenn die Eingabeliste völlig zufällig ist) und einen itertools-Ansatz, der für meist sortierte Listen noch schneller ist ... Enthält jetzt den Pandas-Ansatz von @firelynx - langsam, aber nicht schrecklich und einfach. Hinweis - Der Sortier- / Abschlag- / Zip-Ansatz ist auf meinem Computer für große, meist geordnete Listen durchweg am schnellsten. Für gemischte Listen ist moooeeeep am schnellsten, aber Ihr Kilometerstand kann variieren.

Vorteile

  • Sehr schnell und einfach mit demselben Code auf 'beliebige' Duplikate zu testen

Annahmen

  • Duplikate sollten nur einmal gemeldet werden
  • Doppelte Reihenfolge muss nicht beibehalten werden
  • Duplikate befinden sich möglicherweise an einer beliebigen Stelle in der Liste

Schnellste Lösung, 1m Einträge:

def getDupes(c):
        '''sort/tee/izip'''
        a, b = itertools.tee(sorted(c))
        next(b, None)
        r = None
        for k, g in itertools.izip(a, b):
            if k != g: continue
            if k != r:
                yield k
                r = k

Ansätze getestet

import itertools
import time
import random

def getDupes_1(c):
    '''naive'''
    for i in xrange(0, len(c)):
        if c[i] in c[:i]:
            yield c[i]

def getDupes_2(c):
    '''set len change'''
    s = set()
    for i in c:
        l = len(s)
        s.add(i)
        if len(s) == l:
            yield i

def getDupes_3(c):
    '''in dict'''
    d = {}
    for i in c:
        if i in d:
            if d[i]:
                yield i
                d[i] = False
        else:
            d[i] = True

def getDupes_4(c):
    '''in set'''
    s,r = set(),set()
    for i in c:
        if i not in s:
            s.add(i)
        elif i not in r:
            r.add(i)
            yield i

def getDupes_5(c):
    '''sort/adjacent'''
    c = sorted(c)
    r = None
    for i in xrange(1, len(c)):
        if c[i] == c[i - 1]:
            if c[i] != r:
                yield c[i]
                r = c[i]

def getDupes_6(c):
    '''sort/groupby'''
    def multiple(x):
        try:
            x.next()
            x.next()
            return True
        except:
            return False
    for k, g in itertools.ifilter(lambda x: multiple(x[1]), itertools.groupby(sorted(c))):
        yield k

def getDupes_7(c):
    '''sort/zip'''
    c = sorted(c)
    r = None
    for k, g in zip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_8(c):
    '''sort/izip'''
    c = sorted(c)
    r = None
    for k, g in itertools.izip(c[:-1],c[1:]):
        if k == g:
            if k != r:
                yield k
                r = k

def getDupes_9(c):
    '''sort/tee/izip'''
    a, b = itertools.tee(sorted(c))
    next(b, None)
    r = None
    for k, g in itertools.izip(a, b):
        if k != g: continue
        if k != r:
            yield k
            r = k

def getDupes_a(l):
    '''moooeeeep'''
    seen = set()
    seen_add = seen.add
    # adds all elements it doesn't know yet to seen and all other to seen_twice
    for x in l:
        if x in seen or seen_add(x):
            yield x

def getDupes_b(x):
    '''iter*/sorted'''
    x = sorted(x)
    def _matches():
        for k,g in itertools.izip(x[:-1],x[1:]):
            if k == g:
                yield k
    for k, n in itertools.groupby(_matches()):
        yield k

def getDupes_c(a):
    '''pandas'''
    import pandas as pd
    vc = pd.Series(a).value_counts()
    i = vc[vc > 1].index
    for _ in i:
        yield _

def hasDupes(fn,c):
    try:
        if fn(c).next(): return True    # Found a dupe
    except StopIteration:
        pass
    return False

def getDupes(fn,c):
    return list(fn(c))

STABLE = True
if STABLE:
    print 'Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array'
else:
    print 'Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array'
for location in (50,250000,500000,750000,999999):
    for test in (getDupes_2, getDupes_3, getDupes_4, getDupes_5, getDupes_6,
                 getDupes_8, getDupes_9, getDupes_a, getDupes_b, getDupes_c):
        print 'Test %-15s:%10d - '%(test.__doc__ or test.__name__,location),
        deltas = []
        for FIRST in (True,False):
            for i in xrange(0, 5):
                c = range(0,1000000)
                if STABLE:
                    c[0] = location
                else:
                    c.append(location)
                    random.shuffle(c)
                start = time.time()
                if FIRST:
                    print '.' if location == test(c).next() else '!',
                else:
                    print '.' if [location] == list(test(c)) else '!',
                deltas.append(time.time()-start)
            print ' -- %0.3f  '%(sum(deltas)/len(deltas)),
        print
    print

Die Ergebnisse für den Test "Alle Dupes" waren konsistent und ergaben "zuerst" Duplikate, dann "alle" Duplikate in diesem Array:

Finding FIRST then ALL duplicates, single dupe of "nth" placed element in 1m element array
Test set len change :    500000 -  . . . . .  -- 0.264   . . . . .  -- 0.402  
Test in dict        :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.250  
Test in set         :    500000 -  . . . . .  -- 0.163   . . . . .  -- 0.249  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.159   . . . . .  -- 0.229  
Test sort/groupby   :    500000 -  . . . . .  -- 0.860   . . . . .  -- 1.286  
Test sort/izip      :    500000 -  . . . . .  -- 0.165   . . . . .  -- 0.229  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.145   . . . . .  -- 0.206  *
Test moooeeeep      :    500000 -  . . . . .  -- 0.149   . . . . .  -- 0.232  
Test iter*/sorted   :    500000 -  . . . . .  -- 0.160   . . . . .  -- 0.221  
Test pandas         :    500000 -  . . . . .  -- 0.493   . . . . .  -- 0.499  

Wenn die Listen zuerst gemischt werden, wird der Preis der Sortierung offensichtlich - die Effizienz sinkt merklich und der @ moooeeeep-Ansatz dominiert, wobei Set & Dict-Ansätze ähnlich sind, aber weniger Leistungsträger sind:

Finding FIRST then ALL duplicates, single dupe of "n" included in randomised 1m element array
Test set len change :    500000 -  . . . . .  -- 0.321   . . . . .  -- 0.473  
Test in dict        :    500000 -  . . . . .  -- 0.285   . . . . .  -- 0.360  
Test in set         :    500000 -  . . . . .  -- 0.309   . . . . .  -- 0.365  
Test sort/adjacent  :    500000 -  . . . . .  -- 0.756   . . . . .  -- 0.823  
Test sort/groupby   :    500000 -  . . . . .  -- 1.459   . . . . .  -- 1.896  
Test sort/izip      :    500000 -  . . . . .  -- 0.786   . . . . .  -- 0.845  
Test sort/tee/izip  :    500000 -  . . . . .  -- 0.743   . . . . .  -- 0.804  
Test moooeeeep      :    500000 -  . . . . .  -- 0.234   . . . . .  -- 0.311  *
Test iter*/sorted   :    500000 -  . . . . .  -- 0.776   . . . . .  -- 0.840  
Test pandas         :    500000 -  . . . . .  -- 0.539   . . . . .  -- 0.540  

@moooeeeep - seien Sie interessiert, Ihre Ansichten zum ifilter / izip / tee-Ansatz zu sehen.
F1Rumors

1
Diese Antwort ist unglaublich gut. Ich verstehe nicht, dass es nicht mehr Punkte für die Erklärungen und Tests gab, die für diejenigen sehr nützlich sind, die sie brauchen würden.
Dlewin

1
Die Sortierung von Python ist O (n), wenn nur ein Element nicht in Ordnung ist. Sie sollten random.shuffle(c)das berücksichtigen. Außerdem kann ich Ihre Ergebnisse auch beim Ausführen des unveränderten Skripts nicht replizieren (völlig andere Reihenfolge), sodass dies möglicherweise auch von der CPU abhängt.
John La Rooy

Vielen Dank an John-La-Rooy, scharfsinnige Beobachtung, dass die CPU / der lokale Computer Auswirkungen hat - daher sollte ich den Punkt YYMV ändern . Die Verwendung der O (n) -Sortierung war beabsichtigt: Das duplizierende Element wird an verschiedenen Stellen eingefügt, um die Auswirkungen des Ansatzes zu sehen, wenn sich an einer guten (Anfang der Liste) oder schlechten (Ende der Liste) Stelle ein einziges Duplikat befindet nähert sich. Ich dachte über eine zufällige Liste nach - z. B. random.shuffle - aber entschied, dass dies nur sinnvoll wäre, wenn ich viel mehr Läufe machen würde! Ich muss zurückkehren und ein Mehrfach-Run / Shuffle-Äquivalent bewerten und sehen, wie sich das auswirkt.
F1Rumors

Geändert, um den @ firelynx-Pandas-Ansatz einzuschließen und auf einer vollständig gemischten Liste sowie einer sortierten Liste zu laufen. Dies liegt daran , die native Timsort verwendet von Python ist böse schnell auf meist sortierten Daten (best case) und die gemischten Listen sind das Worst - Case - Szenario - das schüttelt die Ergebnisse.
F1Rumors

13

Pandas benutzen:

>>> import pandas as pd
>>> a = [1, 2, 1, 3, 3, 3, 0]
>>> pd.Series(a)[pd.Series(a).duplicated()].values
array([1, 3, 3])

10

Sammlungen.Counter ist neu in Python 2.7:


Python 2.5.4 (r254:67916, May 31 2010, 15:03:39) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
a = [1,2,3,2,1,5,6,5,5,5]
import collections
print [x for x, y in collections.Counter(a).items() if y > 1]
Type "help", "copyright", "credits" or "license" for more information.
  File "", line 1, in 
AttributeError: 'module' object has no attribute 'Counter'
>>> 

In einer früheren Version können Sie stattdessen ein herkömmliches Diktat verwenden:

a = [1,2,3,2,1,5,6,5,5,5]
d = {}
for elem in a:
    if elem in d:
        d[elem] += 1
    else:
        d[elem] = 1

print [x for x, y in d.items() if y > 1]

9

Hier ist eine saubere und prägnante Lösung -

for x in set(li):
    li.remove(x)

li = list(set(li))

Die ursprüngliche Liste geht jedoch verloren. Dies kann behoben werden, indem der Inhalt in eine andere Liste kopiert wird - temp = li [:]
Nikhil Prabhu

3
Bei großen Listen ist das eine ziemlich üble Übung - das Entfernen von Elementen aus Listen ist ziemlich teuer!
F1Rumors

7

Ohne Konvertierung in eine Liste und wahrscheinlich der einfachste Weg wäre so etwas wie unten. Dies kann während eines Interviews nützlich sein, wenn sie darum bitten, keine Sets zu verwenden

a=[1,2,3,3,3]
dup=[]
for each in a:
  if each not in dup:
    dup.append(each)
print(dup)

======= sonst, um 2 separate Listen mit eindeutigen Werten und doppelten Werten zu erhalten

a=[1,2,3,3,3]
uniques=[]
dups=[]

for each in a:
  if each not in uniques:
    uniques.append(each)
  else:
    dups.append(each)
print("Unique values are below:")
print(uniques)
print("Duplicate values are below:")
print(dups)

1
Dies führt jedoch nicht zu einer Liste von Duplikaten einer (oder der ursprünglichen Liste), sondern zu einer Liste aller eindeutigen Elemente einer (oder der ursprünglichen Liste). Was würde jemand tun, nachdem er die Liste "dup" erstellt hat?
gameCoder95

6

Ich würde das mit Pandas machen, weil ich viel Pandas benutze

import pandas as pd
a = [1,2,3,3,3,4,5,6,6,7]
vc = pd.Series(a).value_counts()
vc[vc > 1].index.tolist()

Gibt

[3,6]

Wahrscheinlich ist es nicht sehr effizient, aber es ist sicher weniger Code als viele andere Antworten, also dachte ich, ich würde dazu beitragen


3
Beachten Sie auch, dass Pandas eine eingebaute Duplikat-Funktion enthält pda = pd.Series(a) print list(pda[pda.duplicated()])
Len Blokken

6

Das dritte Beispiel der akzeptierten Antwort gibt eine fehlerhafte Antwort und versucht nicht, Duplikate zu geben. Hier ist die richtige Version:

number_lst = [1, 1, 2, 3, 5, ...]

seen_set = set()
duplicate_set = set(x for x in number_lst if x in seen_set or seen_set.add(x))
unique_set = seen_set - duplicate_set

6

Wie wäre es, wenn Sie einfach jedes Element in der Liste durchlaufen, indem Sie die Anzahl der Vorkommen überprüfen und sie dann einem Satz hinzufügen, der dann die Duplikate druckt. Hoffe das hilft jemandem da draußen.

myList  = [2 ,4 , 6, 8, 4, 6, 12];
newList = set()

for i in myList:
    if myList.count(i) >= 2:
        newList.add(i)

print(list(newList))
## [4 , 6]

5

Wir können verwenden itertools.groupby, um alle Gegenstände zu finden, die Dups haben:

from itertools import groupby

myList  = [2, 4, 6, 8, 4, 6, 12]
# when the list is sorted, groupby groups by consecutive elements which are similar
for x, y in groupby(sorted(myList)):
    #  list(y) returns all the occurences of item x
    if len(list(y)) > 1:
        print x  

Die Ausgabe wird sein:

4
6

1
Oder genauer dupes = [x for x, y in groupby(sorted(myList)) if len(list(y)) > 1]
gesagt

5

Ich denke, der effektivste Weg, um Duplikate in einer Liste zu finden, ist:

from collections import Counter

def duplicates(values):
    dups = Counter(values) - Counter(set(values))
    return list(dups.keys())

print(duplicates([1,2,3,6,5,2]))

Es werden Counteralle Elemente und alle eindeutigen Elemente verwendet. Wenn Sie das erste vom zweiten subtrahieren, werden nur die Duplikate weggelassen.


4

Ein bisschen spät, aber vielleicht hilfreich für einige. Für eine größere Liste fand ich, dass dies für mich funktionierte.

l=[1,2,3,5,4,1,3,1]
s=set(l)
d=[]
for x in l:
    if x in s:
        s.remove(x)
    else:
        d.append(x)
d
[1,3,1]

Zeigt nur und alle Duplikate und behält die Ordnung bei.


3

Eine sehr einfache und schnelle Möglichkeit, Dupes mit einer Iteration in Python zu finden, ist:

testList = ['red', 'blue', 'red', 'green', 'blue', 'blue']

testListDict = {}

for item in testList:
  try:
    testListDict[item] += 1
  except:
    testListDict[item] = 1

print testListDict

Die Ausgabe erfolgt wie folgt:

>>> print testListDict
{'blue': 3, 'green': 1, 'red': 2}

Dies und mehr in meinem Blog http://www.howtoprogramwithpython.com


3

Ich komme viel zu spät in diese Diskussion. Auch wenn ich dieses Problem gerne mit einem Liner behandeln möchte. Denn das ist der Reiz von Python. Wenn wir nur die Duplikate in eine separate Liste (oder eine beliebige Sammlung) aufnehmen möchten, würde ich Folgendes vorschlagen. Wir haben eine doppelte Liste, die wir als "Ziel" bezeichnen können.

    target=[1,2,3,4,4,4,3,5,6,8,4,3]

Wenn wir nun die Duplikate erhalten möchten, können wir den einen Liner wie folgt verwenden:

    duplicates=dict(set((x,target.count(x)) for x in filter(lambda rec : target.count(rec)>1,target)))

Dieser Code fügt die duplizierten Datensätze als Schlüssel ein und zählt als Wert in das Wörterbuch "Duplikate". Das Wörterbuch "Duplikate" sieht wie folgt aus:

    {3: 3, 4: 4} #it saying 3 is repeated 3 times and 4 is 4 times

Wenn Sie nur alle Datensätze mit Duplikaten in einer Liste haben möchten, ist der Code wieder viel kürzer:

    duplicates=filter(lambda rec : target.count(rec)>1,target)

Ausgabe wird sein:

    [3, 4, 4, 4, 3, 4, 3]

Dies funktioniert perfekt in Python 2.7.x + Versionen


3

Python 3.8 Einzeiler, wenn Sie keinen eigenen Algorithmus schreiben oder Bibliotheken verwenden möchten:

l = [1,2,3,2,1,5,6,5,5,5]

res = [(x, count) for x, g in groupby(sorted(l)) if (count := len(list(g))) > 1]

print(res)

Druckt Artikel und Anzahl:

[(1, 2), (2, 2), (5, 4)]

groupbyübernimmt eine Gruppierungsfunktion, sodass Sie Ihre Gruppierungen auf unterschiedliche Weise definieren und Tuplebei Bedarf zusätzliche Felder zurückgeben können.

groupby ist faul, also sollte es nicht zu langsam sein.


2

Einige andere Tests. Natürlich zu tun ...

set([x for x in l if l.count(x) > 1])

... ist zu teuer. Die nächste endgültige Methode ist ungefähr 500-mal schneller (je länger das Array, desto bessere Ergebnisse):

def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()

Nur 2 Schleifen, keine sehr kostspieligen l.count()Operationen.

Hier ist ein Code zum Vergleichen der Methoden zum Beispiel. Der Code ist unten, hier ist die Ausgabe:

dups_count: 13.368s # this is a function which uses l.count()
dups_count_dict: 0.014s # this is a final best function (of the 3 functions)
dups_count_counter: 0.024s # collections.Counter

Der Testcode:

import numpy as np
from time import time
from collections import Counter

class TimerCounter(object):
    def __init__(self):
        self._time_sum = 0

    def start(self):
        self.time = time()

    def stop(self):
        self._time_sum += time() - self.time

    def get_time_sum(self):
        return self._time_sum


def dups_count(l):
    return set([x for x in l if l.count(x) > 1])


def dups_count_dict(l):
    d = {}

    for item in l:
        if item not in d:
            d[item] = 0

        d[item] += 1

    result_d = {key: val for key, val in d.iteritems() if val > 1}

    return result_d.keys()


def dups_counter(l):
    counter = Counter(l)    

    result_d = {key: val for key, val in counter.iteritems() if val > 1}

    return result_d.keys()



def gen_array():
    np.random.seed(17)
    return list(np.random.randint(0, 5000, 10000))


def assert_equal_results(*results):
    primary_result = results[0]
    other_results = results[1:]

    for other_result in other_results:
        assert set(primary_result) == set(other_result) and len(primary_result) == len(other_result)


if __name__ == '__main__':
    dups_count_time = TimerCounter()
    dups_count_dict_time = TimerCounter()
    dups_count_counter = TimerCounter()

    l = gen_array()

    for i in range(3):
        dups_count_time.start()
        result1 = dups_count(l)
        dups_count_time.stop()

        dups_count_dict_time.start()
        result2 = dups_count_dict(l)
        dups_count_dict_time.stop()

        dups_count_counter.start()
        result3 = dups_counter(l)
        dups_count_counter.stop()

        assert_equal_results(result1, result2, result3)

    print 'dups_count: %.3f' % dups_count_time.get_time_sum()
    print 'dups_count_dict: %.3f' % dups_count_dict_time.get_time_sum()
    print 'dups_count_counter: %.3f' % dups_count_counter.get_time_sum()

2

Methode 1:

list(set([val for idx, val in enumerate(input_list) if val in input_list[idx+1:]]))

Erläuterung: [val für idx, val in enumerate (input_list), wenn val in input_list [idx + 1:]] ein Listenverständnis ist, das ein Element zurückgibt, wenn dasselbe Element von seiner aktuellen Position in list, dem Index, vorhanden ist .

Beispiel: input_list = [42,31,42,31,3,31,31,5,6,6,6,6,6,7,42]

Beginnend mit dem ersten Element in der Liste 42 mit dem Index 0 wird geprüft, ob das Element 42 in der Eingabeliste [1:] vorhanden ist (dh von Index 1 bis zum Ende der Liste), da 42 in der Eingabeliste [1:] vorhanden ist. wird es 42 zurückgeben.

Dann geht es zum nächsten Element 31 mit Index 1 und prüft, ob Element 31 in der Eingabeliste [2:] vorhanden ist (dh von Index 2 bis zum Ende der Liste), da 31 in Eingabeliste [2:] vorhanden ist. es wird 31 zurückgeben.

In ähnlicher Weise werden alle Elemente in der Liste durchlaufen und nur die wiederholten / doppelten Elemente in eine Liste zurückgegeben.

Da wir dann Duplikate in einer Liste haben, müssen wir eines von jedem Duplikat auswählen, dh Duplikate unter Duplikaten entfernen. Dazu rufen wir ein in Python eingebautes namens set () auf und es entfernt die Duplikate.

Dann bleibt uns eine Menge übrig, aber keine Liste. Um von einer Menge in eine Liste zu konvertieren, verwenden wir typecasting, list () und konvertieren die Menge der Elemente in eine Liste.

Methode 2:

def dupes(ilist):
    temp_list = [] # initially, empty temporary list
    dupe_list = [] # initially, empty duplicate list
    for each in ilist:
        if each in temp_list: # Found a Duplicate element
            if not each in dupe_list: # Avoid duplicate elements in dupe_list
                dupe_list.append(each) # Add duplicate element to dupe_list
        else: 
            temp_list.append(each) # Add a new (non-duplicate) to temp_list

    return dupe_list

Erläuterung: Hier erstellen wir zunächst zwei leere Listen. Durchlaufen Sie dann alle Elemente der Liste, um festzustellen, ob sie in temp_list vorhanden sind (anfangs leer). Wenn es nicht in der temp_list vorhanden ist, fügen wir es mithilfe der append- Methode zur temp_list hinzu .

Wenn es bereits in temp_list vorhanden ist, bedeutet dies, dass das aktuelle Element der Liste ein Duplikat ist. Daher müssen wir es mit der Methode append zu dupe_list hinzufügen .


2
raw_list = [1,2,3,3,4,5,6,6,7,2,3,4,2,3,4,1,3,4,]

clean_list = list(set(raw_list))
duplicated_items = []

for item in raw_list:
    try:
        clean_list.remove(item)
    except ValueError:
        duplicated_items.append(item)


print(duplicated_items)
# [3, 6, 2, 3, 4, 2, 3, 4, 1, 3, 4]

Sie entfernen Duplikate im Grunde genommen, indem Sie sie in set ( clean_list) konvertieren und dann die iterieren raw_list, während Sie jedes itemin der Bereinigungsliste für das Auftreten in entfernen raw_list. Wird dies itemnicht gefunden, wird die ausgelöste ValueErrorAusnahme abgefangen und itemzur duplicated_itemsListe hinzugefügt .

Wenn der Index der duplizierten Elemente benötigt wird, nur enumeratedie Liste und spielen Sie mit dem Index herum. ( for index, item in enumerate(raw_list):) ist schneller und optimiert für große Listen (wie Tausende von Elementen)


2

Verwendung der list.count()Methode in der Liste, um die doppelten Elemente einer bestimmten Liste herauszufinden

arr=[]
dup =[]
for i in range(int(input("Enter range of list: "))):
    arr.append(int(input("Enter Element in a list: ")))
for i in arr:
    if arr.count(i)>1 and i not in dup:
        dup.append(i)
print(dup)

einfache Möglichkeit, die doppelten Elemente in der Liste mit der
Zählfunktion

2

Einzeiler, zum Spaß und wenn eine einzige Aussage erforderlich ist.

(lambda iterable: reduce(lambda (uniq, dup), item: (uniq, dup | {item}) if item in uniq else (uniq | {item}, dup), iterable, (set(), set())))(some_iterable)

1
list2 = [1, 2, 3, 4, 1, 2, 3]
lset = set()
[(lset.add(item), list2.append(item))
 for item in list2 if item not in lset]
print list(lset)

1

Einzeilige Lösung:

set([i for i in list if sum([1 for a in list if a == i]) > 1])

1

Hier oben gibt es viele Antworten, aber ich denke, dies ist ein relativ gut lesbarer und leicht verständlicher Ansatz:

def get_duplicates(sorted_list):
    duplicates = []
    last = sorted_list[0]
    for x in sorted_list[1:]:
        if x == last:
            duplicates.append(x)
        last = x
    return set(duplicates)

Anmerkungen:

  • Wenn Sie die Anzahl der Duplikate beibehalten möchten, entfernen Sie die Besetzung, die unten auf "Set" gesetzt ist, um die vollständige Liste zu erhalten
  • Wenn Sie lieber Generatoren verwenden möchten, ersetzen Sie duplicates.append (x) durch Yield x und die return-Anweisung unten (Sie können sie später umsetzen).

1

Hier ist ein schneller Generator, der ein Diktat verwendet, um jedes Element als Schlüssel mit einem booleschen Wert zu speichern, um zu überprüfen, ob das doppelte Element bereits ausgegeben wurde.

Für Listen mit allen Elementen, die Hash-Typen sind:

def gen_dupes(array):
    unique = {}
    for value in array:
        if value in unique and unique[value]:
            unique[value] = False
            yield value
        else:
            unique[value] = True

array = [1, 2, 2, 3, 4, 1, 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, 1, 6]

Für Listen, die möglicherweise Listen enthalten:

def gen_dupes(array):
    unique = {}
    for value in array:
        is_list = False
        if type(value) is list:
            value = tuple(value)
            is_list = True

        if value in unique and unique[value]:
            unique[value] = False
            if is_list:
                value = list(value)

            yield value
        else:
            unique[value] = True

array = [1, 2, 2, [1, 2], 3, 4, [1, 2], 5, 2, 6, 6]
print(list(gen_dupes(array)))
# => [2, [1, 2], 6]

1
def removeduplicates(a):
  seen = set()

  for i in a:
    if i not in seen:
      seen.add(i)
  return seen 

print(removeduplicates([1,1,2,2]))

Sie geben einen Satz und keine Liste wie gewünscht zurück. Eine Menge enthält nur eindeutige Elemente, daher ist die if-Anweisung nicht wirklich erforderlich. Sie sollten auch erklären, welchen Vorteil Ihre Lösung gegenüber der anderen hat.
Clemens

1

Bei Verwendung von toolz :

from toolz import frequencies, valfilter

a = [1,2,2,3,4,5,4]
>>> list(valfilter(lambda count: count > 1, frequencies(a)).keys())
[2,4] 

0

So musste ich es machen, weil ich mich herausforderte, keine anderen Methoden anzuwenden:

def dupList(oldlist):
    if type(oldlist)==type((2,2)):
        oldlist=[x for x in oldlist]
    newList=[]
    newList=newList+oldlist
    oldlist=oldlist
    forbidden=[]
    checkPoint=0
    for i in range(len(oldlist)):
        #print 'start i', i
        if i in forbidden:
            continue
        else:
            for j in range(len(oldlist)):
                #print 'start j', j
                if j in forbidden:
                    continue
                else:
                    #print 'after Else'
                    if i!=j: 
                        #print 'i,j', i,j
                        #print oldlist
                        #print newList
                        if oldlist[j]==oldlist[i]:
                            #print 'oldlist[i],oldlist[j]', oldlist[i],oldlist[j]
                            forbidden.append(j)
                            #print 'forbidden', forbidden
                            del newList[j-checkPoint]
                            #print newList
                            checkPoint=checkPoint+1
    return newList

Ihre Probe funktioniert also wie folgt:

>>>a = [1,2,3,3,3,4,5,6,6,7]
>>>dupList(a)
[1, 2, 3, 4, 5, 6, 7]

3
Dies ist nicht das, was das OP wollte. Er wollte eine Liste der Duplikate, keine Liste mit entfernten Duplikaten. Um eine Liste mit entfernten Duplikaten zu erstellen, würde ich vorschlagen duplist = list(set(a)).
Zondo
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.