Doppelte Datensätze entfernen (Liste? Dictionary?)

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.
Antworten
halycon
User
Beiträge: 4
Registriert: Sonntag 25. Januar 2015, 01:49

Hallo zusammen,
ich bin ein blutiger python Anfänger, also bitte verzeiht mir, falls das eine dumme Frage ist.
Folgende Problemstellung: Ich habe eine Logdatei, aus der ich bestimmte Zeilen auslese mit readlines() Das Ganze zerstückel ich anschließend mit split(). So weit, so gut.

Ursprünglich war die Intension, die einzelnen Stücke einzeln in bestimmter Form in eine neue Datei zu schreiben, das ist kein Problem. Hier mal ein Beispiel einer solchen list:

Code: Alles auswählen

for zeile in input_list:
        print(zeile)
Output:
['Adding', 'car:', 'SID:0', 'name=Hans', 'model=ferrari', 'id=7972871910']
['Adding', 'car:', 'SID:0', 'name=Hans', 'model=ferrari', 'id=7972871910']
['Adding', 'car:', 'SID:0', 'name=Hans', 'model=ferrari', 'id=7972871910']
['Adding', 'car:', 'SID:1', 'name=Peter', 'model=ferrari', 'id=7991905259']
Nun ist es so, dass "Hans" drei mal in der Liste ist, eindeutig erkennbar durch seine id. Ich brauche "einfach" nur eine bereinigte Liste, wo die doppelten Einträge von Hans nicht vorkommen.
Habe nun schon versucht die List in ein (mehrdimensionales) dictionary zu konvertieren. Das hat auch geklappt. Als ich dann soweit war, habe ich festgestellt, dass mich das garnicht weiter bringt :)

Hat vielleicht jemand einen Tipp für mich wie ich die Duplikate loswerde?

Besten Dank und gute Nacht
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

halycon hat geschrieben:Ich brauche "einfach" nur eine bereinigte Liste, wo die doppelten Einträge von Hans nicht vorkommen.
Die Zeilen aus der Datei sehen merkwürdig aus. Es scheint. als wäre jede Zeile dadurch erzeugt worden, dass eine Liste einfach mit str in einen String überführt worden wäre. Dadurch enthältst du dann die eckigen Klammern vorne und hinten und verlässt dich beim Inhalt auf die repr-Darstelung. Das ist eine eher merkwürdige Art der Speicherung.

Wenn du jetzt einfach komplett doppelte Zeilen entfernen willst, dann bietet sich ein Set an.

Code: Alles auswählen

>>> data = ['a', 'b', 'c', 'b', 'a', 'c', 'd', 'a']
>>> set(data)
{'a', 'd', 'b', 'c'}
Auf deinen Fall übertragen:

Code: Alles auswählen

result = set(line for line in input_list)
Ein Set hat keine Sortierung. Wenn du jetzt eine Sortierung brauchst, dann kannst du das Set wieder sortiert in eine Liste überführen.

Code: Alles auswählen

result = list(sorted(result))
Falls es dir nicht um komplette Zeilen geht, sondern nur darum, dass der 'id=...'-Teil identisch ist, dann musst du die Zeile erst wieder in eine passende Struktur überführen auf der dann operiert werden kann.
Benutzeravatar
diesch
User
Beiträge: 80
Registriert: Dienstag 14. April 2009, 13:36
Wohnort: Brandenburg a.d. Havel
Kontaktdaten:

Wenn du die Reihenfolge beibehalten willst, kannst du alle Einträge durchgehen und dir in einem set merken, was du schon hattest. Wenn du für jede ID nur den ersten Eintrag haben willst also z.B.

Code: Alles auswählen

seen = set()
for line in input_list:
    if line[-1] not in seen:
        print(line)
        seen.add(line[-1])
oder etwas kürzer (und unübersichtlicher):

Code: Alles auswählen

seen = set()
print([seen.add(line[-1]) or line for line in input_list if line[-1] not in seen])
http://www.florian-diesch.de
halycon
User
Beiträge: 4
Registriert: Sonntag 25. Januar 2015, 01:49

Die Zeilen können sich durchaus unterscheiden, nur der id= wert bleibt immer gleich und es sollen die Zeilen entfernt werden, die im id= wert eine Übereinstimmung haben.
/me hat geschrieben:
Falls es dir nicht um komplette Zeilen geht, sondern nur darum, dass der 'id=...'-Teil identisch ist, dann musst du die Zeile erst wieder in eine passende Struktur überführen auf der dann operiert werden kann.
Welche Struktur würde sich da anbieten?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@halycon: Was willst Du denn mit den Daten weiter anstellen? Sind die einzelnen Elemente wirklich nur durch Leerzeichen getrennt? Was passiert wenn der Name oder das Model selbst ein Leerzeichen enthält?
Log-Dateien zu parsen ist nicht immer möglich, weil sie oft nicht eindeutig sind. Woher kommen die log-Dateien? Gibt es eine andere Möglichkeit an die Daten zu kommen?

Unter der Annahme, dass wir als Datenstruktur ein Dictionary mit id -> SID, name, model haben wollen:

Code: Alles auswählen

import re

cars = {}
with open('cars.log') as log:
    for line in log:
        match = re.match(r'Adding car:\s+SID:(\d+)\s+name=(.*?)\s+model=(.*?)\s+id=(\d+)', line)
        if match:
            sid, name, model, id = match.groups()
            cars[id] = {'sid': sid, 'name': name, 'model': model}
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

halycon hat geschrieben:Die Zeilen können sich durchaus unterscheiden, nur der id= wert bleibt immer gleich und es sollen die Zeilen entfernt werden, die im id= wert eine Übereinstimmung haben.
Dann stellt sich natürlich noch eine Frage. Nehmen wir an, die Daten sähen so aus:

Code: Alles auswählen

id    value
1    a
1    b
2    c
2    d
1    e
3    b
Jetzt möchtest du nur Werte mit eindeutiger id behalten. Welchen value nimmst du denn dann dazu? Brauchst du überhaupt einen? Und wenn ja, warum gerade den, den du jetzt auswählst?
halycon
User
Beiträge: 4
Registriert: Sonntag 25. Januar 2015, 01:49

Also grundsätzlich gibt es vier Werte, die mich interessieren, davon ist einer eindeutig, und zwar "guid". Das Feld SID= ist in der Realität unterschiedlich, einzig und allein die guid= bleibt immer gleich bei Duplikaten.

So stehen die Zeilen z.B. im Log:

Code: Alles auswählen

Adding car: SID:0 name=Hans model=ferrari_458_gt2 skin=2011_team_sofrev_asp guid=7972871910
Adding car: SID:1 name=Peter model=ferrari_458_gt2 skin=af_corse guid=7991905259
Ich lese die Textdatei so ein: (input_log ist die txt Datei)

Code: Alles auswählen

    
input_list = open(input_log).readlines()
for zeile in input_list:
        if(zeile.find("Adding car")!=-1):
            output_list.append(zeile)
    return output_list
Da kommt dann raus:

Code: Alles auswählen

for zeile in input_list:
        print(zeile)

Code: Alles auswählen

Adding car: SID:0 name=Hans model=ferrari_458_gt2 skin=2011_team_sofrev_asp guid=7972871910
Adding car: SID:1 name=Peter model=ferrari_458_gt2 skin=af_corse guid=7991905259
Das splite ich dann, weil ich mir dachte, einzeln kann man die Werte besser verarbeiten:

Code: Alles auswählen

bloecke = []

    for zeile in output_list:

        bloecke.append(zeile.split())

    return bloecke
Ergebnis ist:

Code: Alles auswählen

['Adding', 'car:', 'SID:0', 'name=Hans', 'model=ferrari_458_gt2', 'skin=2011_team_sofrev_asp', 'guid=7972871910']
['Adding', 'car:', 'SID:0', 'name=Hans', 'model=ferrari_458_gt2', 'skin=2011_team_sofrev_asp', 'guid=7972871910']
['Adding', 'car:', 'SID:1', 'name=NeRo2', 'model=ferrari_458_gt2', 'skin=af_corse', 'guid=7991905259']
Hier ist es natürlich schon sehr unschön, da ein name= durchaus ein Leerzeichen enthalten kann. Aber das wäre dann die nächste Baustelle.
Das wäre jetzt die "Basis", anhand der ich gerne die List-Einträge, wo die guid= den selben Wert hat, löschen würde.

Ich habe dann versucht das Ganze in ein DIC zu überführen:

Code: Alles auswählen

def convert_to_dic(input_list):
    dict1 ={}
    zahler = 0
    for zeile in input_list:
        dict1[zahler] = dict([ (zeile[3].split("=")) ,(zeile[4].split("=")), (zeile[5].split("=")), (zeile[6].split("=")) ])
        print(dict1[zahler])
        zahler=zahler+1
Das gibt mir quasi dict1[n] zurück, da habe ich dann die werte separiert:

Code: Alles auswählen

{'model': 'ferrari_458_gt2', 'guid': '7972871910', 'name': 'Hans', 'skin': '2011_team_sofrev_asp'}
{'model': 'ferrari_458_gt2', 'guid': '7972871910', 'name': 'Hans', 'skin': '2011_team_sofrev_asp'}
{'model': 'ferrari_458_gt2', 'guid': '7991905259', 'name': 'Peter', 'skin': 'af_corse'}
Entweder habe ich Dictionaries grundsätzlich nciht verstanden, oder man kann tatsächlich nur pro key genau ein value speichern...ist das richtig? Daher habe ich das mit dem zähler gemacht.

Sollte ich jetzt mit dem dict1[n] weiter machen um die Duplikate zu entfernen, oder mit der List einen Schritt vorher?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@halycon: Dateien die man öffnet sollte man auch wieder schließen, am besten benutzt Du dazu das with-Statement. Über die Zeilen einer Datei kann man direkt iterieren, da brauchst Du kein extra readlines, um sie alle auf einmal zu lesen. Statt find wäre hier ein startswith ideal. if ist keine Funktion also gehören da auch keine Klammern um den gesamten Ausdruck. Deine Einrückung ist kaputt.

Natürlich hat ein Dictionary nur einen Key. Und das ist die Größe, nach der normalerweise auf das Dictionary zugegriffen wird, in Deinem Fall also guid. Der Zähler ist so quatsch, weil dann kannst Du gleich bei einer Liste bleiben.
Für ein Beispiel, wie das alles aussehen könnte, schau Dir meinen ersten Post hier an.
halycon
User
Beiträge: 4
Registriert: Sonntag 25. Januar 2015, 01:49

Ich versuch das mal, danke!!
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@halycon: etwas generischer, wobei man hier aufpassen muß, dass das die Felder Log-File nicht ähnliche Pattern enthalten, die dann fälschlicherweise als Felder interpretiert werden:

Code: Alles auswählen

import re

def extract_cars(input_log):
    cars = {}
    with open(input_log) as log:
        for line in log:
            if line.startswith('Adding car:'):
                car = dict(re.findall('(\w+)=(.*?)(?=\s+\w+=|\s*$)', line))
                cars[car.pop('guid')] = car
    return cars
Ich würde bei Log-Files so explizit wie möglich matchen.
Antworten