programmierung.in python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
guy12
User
Beiträge: 4
Registriert: Mittwoch 28. Mai 2014, 16:12

Hallo ich habe ein frage: ich habe ein Datei oder liste dt=["DK"
"D14"
"D34"
"D14"
"D34"
"D50"
"D81"
"D81"
"D91"
"D91"]
wie kann ich machen mit der Python programm sodass für jede element ich nur eine erhalte , ich meine dt =["Dk","D34","D14","D50","D81","D91"]
Danke im voraus
BlackJack

@guy12: Was hast Du denn nun, eine Datei oder eine Liste? Falls das da der Inhalt einer Datei ist, sieht der *tatsächlich* genau so aus? Und falls ja, wie ist die Datei zustande gekommen?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@guy12
Wenn es sich um eine Liste handelt, dann erstellst Du mit dessen Elementen eine neue Liste und fügst jedes Element nur dann hinzu, wenn es noch nicht enthalten ist:

Code: Alles auswählen

>>> dt = ['DK', 'D14', 'D34', 'D14', 'D34', 'D50', 'D81', 'D81', 'D91', 'D91']
>>> def equalize(iterable):
...     new = []
...     for element in iterable:
...         if not element in new:
...             new.append(element)
...     return new
... 
>>> equalize(dt)
['DK', 'D14', 'D34', 'D50', 'D81', 'D91']
Falls die Reihenfolge keine Rolle spielt:

Code: Alles auswählen

>>> dt = ['DK', 'D14', 'D34', 'D14', 'D34', 'D50', 'D81', 'D81', 'D91', 'D91']
>>> list(set(dt))
['D14', 'DK', 'D34', 'D91', 'D81', 'D50']
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
darktrym
User
Beiträge: 784
Registriert: Freitag 24. April 2009, 09:26

@mutetella: Zu deinem ersten Ansatz, nicht unbedingt günstig hinsichtlich Laufzeit. Ein Dictionary wäre da die bessere Wahl.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

Wenn's auf die Reihenfolge ankommt ist die übliche vorgehensweise:

Code: Alles auswählen

def main():
    data = ['DK', 'D14', 'D34', 'D14', 'D34', 'D50', 'D81', 'D81', 'D91', 'D91']
    
    seen = set()
    result = list()
    for item in data:
        if item not in seen:
            result.append(item)
        seen.add(item)

    print result
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Wobei man prinzipiell noch ein paar ``add``-Operationen auf dem Set einsparen kann, wenn man sie nicht wie in deinem Beispiel für wirklich jedes Element ausführt, sondern nur dann, wenn der vorherige Test, den du ja ohnehin machst, ergeben hat, dass das Element bisher noch nicht in ``seen`` steckt. Mag sich in den meisten Fällen nicht allzu stark auf die Gesamtlaufzeit ausführen, tut aber auch nicht weh, wenn man's entsprechend ändert. ;)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Code: Alles auswählen

import sys
import timeit

test = [
    'DK', 'D14', 'D34', 'D14', 'D34', 'D50', 'D81', 'D81', 'D91', 'D91'
]

test_1 = '''\
new = []
for element in test:
    if not element in new:
        new.append(element)
'''

test_2 = '''\
seen = set()
result = list()
for item in test:
    if item not in seen:
        result.append(item)
    seen.add(item)
'''

if __name__ == '__main__':
    length = int(sys.argv[1])
    test = test * length
    print 'test without set: {}'.format(
        timeit.timeit(stmt=test_1,
                      setup='from __main__ import test',
                      number=10000))
    print 'test with    set: {}'.format(
        timeit.timeit(stmt=test_2,
                      setup='from __main__ import test',
                      number=10000))

Code: Alles auswählen

$ python ./equal_items.py 10
test without set: 0.0989220142365
test with    set: 0.145992994308

$ python ./equal_items.py 100
test without set: 0.925514936447
test with    set: 1.30377697945

$ python ./equal_items.py 1000
test without set: 9.0896999836
test with    set: 12.557033062

$ python ./equal_items.py 10000
test without set: 91.0298860073
test with    set: 125.113059044
Dann doch überraschend, oder?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Da Du 1. die Verbesserung nicht vorgenommen hast und 2. das nicht in einer Funktion steckt, überrascht es mich jetzt nicht wirklich.

Edit: Und es kommt natürlich auch auf die Anzahl der tatsächlich unterschiedlichen Elemente an.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: überhaupt nicht überraschend. Deine Ergebnisliste ist in jedem Fall 5 Elemente lang. 5 Elemente linear zu durchsuchen braucht eben ein bißchen weniger Zeit als zweimal einen Hash zu ermitteln und zwei Vergleiche. Dass die Zeit linear mit den Wiederholungen Deiner Liste zunimmt ist dann auch wenig überraschend.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack
Die Verbesserung reißt's nicht wirklich raus...

Code: Alles auswählen

test_2 = '''\
seen = set()
result = list()
for item in test:
    if item not in seen:
        result.append(item)
    else:
        seen.add(item)
'''

Code: Alles auswählen

$ python ./equal_items.py 100
test without set: 0.910356044769
test with    set: 1.21818304062

$ python ./equal_items.py 10000
test without set: 90.9470040798
test with    set: 121.787957191
... und was Du mit "in einer Funktion steckt" meinst, verstehe ich nicht.

@Sirius3
Ich dachte nur, dass BlackJack's Version performanter sei, da er von "üblicher Vorgehensweise" sprach. Deshalb war ich überrascht.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Es steckt nicht einer Funktion heisst der Code ist nicht in einer Funktion, sondern, naja, ausserhalb einer Funktion. In meinem Code werden mehr Namen aufgelöst und das ist ausserhalb von Funktionen ”teurer” als bei lokalen Namen in Funktionen.

Meine Version ist üblicherweise auch performanter. Klar kann man Fälle finden bei denen sie das nicht ist, aber generell kann man sagen das mit der Liste ist keine gute Idee weil ``a in b`` bei Listen linear von der Länge der Liste abhängt, während es bei Mengenobjekten völlig egal ist wie viele Objekte da schon drin stehen.
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: Sets haben einen etwas größeren Overhead, aber der Zugriff ist unabhängig von der Länge:

Code: Alles auswählen

>>> for x in range(10): data=set(range(x));print len(data), timeit.timeit(stmt=lambda: 50 in data, number=1000000)
... 
0 0.165304899216
1 0.161346912384
2 0.165688991547
3 0.184131145477
4 0.184000968933
5 0.172278881073
6 0.176582098007
7 0.172767877579
8 0.168383836746
9 0.177536964417
während der Aufwand bei Listen mit der Länge steigt:

Code: Alles auswählen

>>> for x in range(10): data=list(range(x));print len(data), timeit.timeit(stmt=lambda: 50 in data, number=1000000)
... 
0 0.164139032364
1 0.178289890289
2 0.211421012878
3 0.227061033249
4 0.257332086563
5 0.271213054657
6 0.296862840652
7 0.32412314415
8 0.342656135559
9 0.360295057297
Hinzu kommt, dass die Variante mit Set ein Operation pro gefundenem Element mehr hat, so dass der Vorteil von Sets nicht schon bei 1 elementigen Listen überwiegt.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

mutetella hat geschrieben:Die Verbesserung reißt's nicht wirklich raus...

Code: Alles auswählen

test_2 = '''\
seen = set()
result = list()
for item in test:
    if item not in seen:
        result.append(item)
    else:
        seen.add(item)
'''
Die Verbesserung war eher so gemeint, dass ein Element in ``seen`` gesteckt werden soll, wenn es noch *nicht* drin ist. Dein Code macht genau das Gegenteil. Er führt damit von der Funktionweise her zu einem völlig anderem Ergebnis. Zudem sind deine Testfälle ziemlich ungünstig gewählt, wie ja auch schon kommentiert wurde.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was man übrigens noch machen kann:

Code: Alles auswählen

from itertools import ifilterfalse

def get_unique(elems):
    result = []
    seen = set()
    have_seen = seen.__contains__
    for elem in ifilterfalse(have_seen, elems):
        seen.add(elem)
        result.append(elem)
    return result
Die Idee dahinter ist, dass Schleifendurchläufe in reinem Python eingespart werden, weil mittels ``ifilterfalse()`` die bereits gesehenen Elemente aus Python-Sicht übersprungen werden. Ob das tatsächlich soviel bringt, müsste man aber noch testen.

Wenn die ``add``-Operation auf Python-Sets einen Wahrheitswert zurückliefern würde, ob das Element tatsächlich hinzugefügt wurde (nämlich dann, wenn es noch nicht im Set vorhanden war), dann könnte man etwas deutlich Eleganteres mittels ``filter()`` hinbekommen. Der Code würde dann schrumpfen auf:

Code: Alles auswählen

def get_uniques(elems):
    seen = set()
    return filter(seen.add, elems)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wer braucht schon einen Kalender... :)
snafu hat geschrieben:Dein Code macht genau das Gegenteil. Er führt damit von der Funktionweise her zu einem völlig anderem Ergebnis.
Sag' ruhig, wie es ist: Mein Code ist hirnloser Bullshit!! Ich hab's verbessert...
snafu hat geschrieben:Zudem sind deine Testfälle ziemlich ungünstig gewählt, wie ja auch schon kommentiert wurde.
Ich verstehe nur noch nicht ganz, weshalb. Um zu vergleichen muss ich doch auch jedem Test dieselbe Liste geben. Oder meinst Du mit "Testfälle ziemlich ungünstig gewählt" nur das, was BlackJack mit innerhalb vs. außerhalb einer Funktion schreibt?

Code: Alles auswählen

from itertools import ifilterfalse
import sys
import timeit


test = [
    'DK', 'D14', 'D34', 'D14', 'D34', 'D50', 'D81', 'D81', 'D91', 'D91'
]

test_01 = '''\
new = []
for element in test:
    if not element in new:
        new.append(element)
'''

test_02 = '''\
seen = set()
result = list()
for item in test:
    if item not in seen:
        result.append(item)
        seen.add(item)
'''

test_03 = '''\
result = []
seen = set()
have_seen = seen.__contains__
for elem in ifilterfalse(have_seen, test):
    seen.add(elem)
    result.append(elem)
'''

def unique_01():
    new = []
    for element in test:
        if not element in new:
            new.append(element)

def unique_02():
    seen = set()
    result = list()
    for item in test:
        if item not in seen:
            result.append(item)
            seen.add(item)

def unique_03():
    result = []
    seen = set()
    have_seen = seen.__contains__
    for elem in ifilterfalse(have_seen, test):
        seen.add(elem)
        result.append(elem)

if __name__ == '__main__':
    length = int(sys.argv[1])
    test = test * length
    print 'test list      outside function: {}'.format(
        timeit.timeit(stmt=test_01,
                      setup='from __main__ import test',
                      number=10000))
    print 'test list      inside  function: {}'.format(
        timeit.timeit('unique_01()',
                      setup='from __main__ import test, unique_01',
                      number=10000))
    print 'test set       outside function: {}'.format(
        timeit.timeit(stmt=test_02,
                      setup='from __main__ import test',
                      number=10000))
    print 'test set       inside  function: {}'.format(
        timeit.timeit('unique_02()',
                      setup='from __main__ import test, unique_02',
                      number=10000))
    print 'test itertools outside function: {}'.format(
        timeit.timeit(stmt=test_03,
                      setup='from __main__ import test, ifilterfalse',
                      number=10000))
    print 'test itertools inside  function: {}'.format(
        timeit.timeit('unique_03()',
                      setup='from __main__ import test, unique_03, ifilterfalse',
                      number=10000))

Code: Alles auswählen

$ python ./equal_items.py 10
test list      outside function: 0.101276159286
test list      inside  function: 0.100987195969
test set       outside function: 0.0632679462433
test set       inside  function: 0.0638370513916
test itertools outside function: 0.0752058029175
test itertools inside  function: 0.0768110752106

$ python ./equal_items.py 100
test list      outside function: 0.917196989059
test list      inside  function: 1.02419400215
test set       outside function: 0.458462953568
test set       inside  function: 0.451676130295
test itertools outside function: 0.550910949707
test itertools inside  function: 0.557126998901

$ python ./equal_items.py 1000
test list      outside function: 9.1905798912
test list      inside  function: 9.39835596085
test set       outside function: 4.34967303276
test set       inside  function: 4.43209385872
test itertools outside function: 5.34194898605
test itertools inside  function: 5.87611699104
Sind das nun aussagekräftige Tests oder hab' ich euch tatsächlich noch nicht verstanden?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17753
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: ich werde gern ignoriert :evil: . Nochmal zum Mitlesen: Je länger die Ergebnisliste, desto schlechter schneidet die List-Variante gegenüber der Set-Version ab. Bei Deinen Tests mit 10, 100, 1000 wird aber die selbe Eingangsliste nur 10, 100 oder 1000 mal durchlaufen, die Ergebnisliste ist aber immer die selbe! Ein sinnvoller Test variiert die Anzahl der unterschiedlichen Elemente in der Eingangsliste nicht deren Länge. Dann hast Du nämlich nicht ein lineares Laufzeitverhalten für alle Varianten, sondern bei gleich Listenlänge konstante Laufzeit bei "Set" und lineare bei "List".
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@mutetella:
Ersetz doch mal Dein `test` durch so etwas und spiel mal mit dem hinteren Rangewert

Code: Alles auswählen

test = [''.join(random.choice(string.ascii_letters) for _ in range(3)) for _ in range(200)]
Wenn Du Dir überlegst, wie Sets und Listen intern ticken, wird auch klar, was da passiert - die angefragten Operationen (suchen, einfügen) haben verschiedene Laufzeiten in der O-Notation und unterschiedliche Kosten für eine Einzeloperation. Z.B. ist Hashen+Suche im Hashbaum zunächst erstmal teuerer als Suche in einer Liste, eben weil Hashen aufwändig ist. Da die Suche in der Liste aber eine schlechtere Laufzeit hat, gibt es einen break-even point - für alle größeren n gewinnt immer der Hashansatz.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Sirius3
Ich hab' Deine Erklärung nicht ignoriert. Ich verstehe nur den Zusammenhang zwischen `test` und der Ergebnisliste noch nicht. Wenn ich die Zufallsliste aus jerchs Beispiel verwende und mit einer multiplizierten Liste vergleiche...

Code: Alles auswählen

>>> ['aFy', 'Dab', 'HZT'] * 2
['aFy', 'Dab', 'HZT', 'aFy', 'Dab', 'HZT']
>>> [''.join(random.choice(string.ascii_letters) for _ in range(3)) for _ in range(6)]
['xyi', 'erR', 'dew', 'oTU', 'aYF', 'KKO']
... dann hab' ich unter Verwendung der Zufallsliste wahrscheinlich weniger Duplikate, sprich' eine potentiell größere Ergebnisliste.
Sirius3 hat geschrieben:Bei Deinen Tests mit 10, 100, 1000 wird aber die selbe Eingangsliste nur 10, 100 oder 1000 mal durchlaufen, die Ergebnisliste ist aber immer die selbe!
Aber sowohl mit der Zufalls- wie auch mit der multiplizierten Liste ist doch die Ergebnisliste auch immer gleich groß?

Also nicht, dass ich euch nicht glauben würde, zumal die Testergebnisse dasselbe ausdrücken...

Code: Alles auswählen

if __name__ == '__main__':
    length = int(sys.argv[1])
    test = [
        ''.join(random.choice(ascii_letters) for
                _ in range(3)) for _ in range(length)
    ]
    print 'test list      outside function: {}'.format(
        timeit.timeit(stmt=test_01,
                      setup='from __main__ import test',
                      number=10000))
    print 'test list      inside  function: {}'.format(
        timeit.timeit('unique_01()',
                      setup='from __main__ import test, unique_01',
                      number=10000))
    print 'test set       outside function: {}'.format(
        timeit.timeit(stmt=test_02,
                      setup='from __main__ import test',
                      number=10000))
    print 'test set       inside  function: {}'.format(
        timeit.timeit('unique_02()',
                      setup='from __main__ import test, unique_02',
                      number=10000))
    print 'test itertools outside function: {}'.format(
        timeit.timeit(stmt=test_03,
                      setup='from __main__ import test, ifilterfalse',
                      number=10000))
    print 'test itertools inside  function: {}'.format(
        timeit.timeit('unique_03()',
                      setup='from __main__ import test, unique_03, ifilterfalse',
                      number=10000))

Code: Alles auswählen

$ python ./equal_items.py 10
test list      outside function: 0.0219969749451
test list      inside  function: 0.0241479873657
test set       outside function: 0.0340809822083
test set       inside  function: 0.0344898700714
test itertools outside function: 0.0381298065186
test itertools inside  function: 0.0383148193359

$ python ./equal_items.py 100
test list      outside function: 0.745640993118
test list      inside  function: 0.752784013748
test set       outside function: 0.312932014465
test set       inside  function: 0.304243803024
test itertools outside function: 0.329273939133
test itertools inside  function: 0.340594768524

$ python ./equal_items.py 1000
test list      outside function: 62.3739390373
test list      inside  function: 62.4985778332
test set       outside function: 2.74161219597
test set       inside  function: 2.68796682358
test itertools outside function: 2.98557090759
test itertools inside  function: 3.06113815308
Ich verstehe sehr wohl, dass bei einer umso größeren Ergebnisliste die set-Lösung performanter wird...

Ok, ich glaub' ich hab's geschnallt: Das Problem war nicht, dass jeder Test dieselbe Liste abarbeitete, sondern dass durch die einfache Multiplikation der Eingangsliste letztlich die Ergebnisliste doch dieselbe blieb und dadurch die Listenlösung niemals in den Bereich vordrang, ab dem sie ihren Vorteil gegenüber der Setlösung verlor. Kann man das so sagen?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
guy12
User
Beiträge: 4
Registriert: Mittwoch 28. Mai 2014, 16:12

guy12 hat geschrieben:Hallo ich habe ein frage: ich habe ein Datei oder liste dt=["DK"
"D14"
"D34"
"D14"
"D34"
"D50"
"D81"
"D81"
"D91"
"D91"]
wie kann ich machen mit der Python programm sodass für jede element ich nur eine erhalte , ich meine dt =["Dk","D34","D14","D50","D81","D91"]
Danke im voraus
ja es ist ein datei die ich durch diese programm geöffnet habe.

Code: Alles auswählen

with open("kurz-ergebnis.csv", "r") as fd:
	
	for line in fd:
		splitline=line.split(',');
		print splitline[2]
fd.close()
aber wie man weiter macht bis das ergebnis dt=["DK","D34","D14","D50","D81","D91"]
weiss ich nicht
Zuletzt geändert von Hyperion am Freitag 30. Mai 2014, 12:39, insgesamt 1-mal geändert.
Grund: Code in Python-Code Tags gesetzt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Bitte benutze als Einrückung vier Leerzeichen anstelle eines Tabulators! (PEP8 sieht das so vor und man kann das in jedem guten Editor einstellen, dass dieser bei Druck auf Tab vier Leerzeichen in das Dokument übernimmt - es ist also *nicht* mehr Tipparbeit)

Das ``fd.close`` ist obsolete, da Du die Datei mittels ``with`` öffnest!

Wo genau hakt es denn? Was steht denn jeweils in ``splitline``?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten