Konvertieren List to dict or json

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.
mit
User
Beiträge: 285
Registriert: Dienstag 16. September 2008, 10:00

Die Eingabe Daten sind sortiert wie in Beispiel gezeigt, aber die echten Daten sind in einer Datei gespeichert.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@mit: nochmal die Grundlagen. Der Key darf nicht veränderlich sein, daher soll er nur alle nicht veränderlichen Teile enthalten; und Du willst keine Zuordnung Key -> Key sondern Key -> Liste.
BlackJack

@mit: Wenn die Eingabe tatsächlich sortiert ist, dann ist das doch absolut trivial und man braucht diese ganzen `defaultdict`-Spielchen gar nicht. Mann braucht auch gar nicht alles in den Speicher laden sondern immer nur alles was zu einem zusammengesetzten Schlüssel gehört.

Edit:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from itertools import groupby


DATA = [["Test", "A", "B01", 828288,  1,    7, 'C', 5],
        ["Test", "A", "B01", 828288,  1,    7, 'T', 6],
        ["Test", "A", "B01", 171878,  3,    8, 'C', 5],
        ["Test", "A", "B01", 171878,  3,    8, 'T', 6],
        ["Test", "A", "B01", 871963,  3,    9, 'A', 5],
        ["Test", "A", "B01", 871963,  3,    9, 'G', 6],
        ["Test", "A", "B01", 1932523, 1,   10, 'T', 4],
        ["Test", "A", "B01", 1932523, 1,   10, 'A', 5],
        ["Test", "A", "B01", 1932523, 1,   10, 'X', 6],
        ["Test", "A", "B01", 667214,  1,   14, 'T', 4],
        ["Test", "A", "B01", 667214,  1,   14, 'G', 5],
        ["Test", "A", "B01", 667214,  1,   14, 'G', 6]]


def iter_something(rows):
    key_names = ['type', 'name', 'sub_name', 'pos', 's_type', 'x_type']
    chr_key_names = ['letter', 'no']
    for keys, group in groupby(rows, lambda row: row[:6]):
        result = dict(zip(key_names, keys))
        result['chr'] = [dict(zip(chr_key_names, row[6:])) for row in group]
        yield result


def main():
    for object_ in iter_something(DATA):
        print(object_)


if __name__ == '__main__':
    main()
mit
User
Beiträge: 285
Registriert: Dienstag 16. September 2008, 10:00

@ BlackJack: Danke für deine Lösung welche auch prima funktioniert, aber ich werde nicht in der Lage sein die ganze Datei im Speicher zu laden. Ich hoffe nur das viele Zeilen zusammen gefasst werden koennen so das die dict im Speicher passen.

@Sirius3:
nochmal die Grundlagen. Der Key darf nicht veränderlich sein, daher soll er nur alle nicht veränderlichen Teile enthalten; und Du willst keine Zuordnung Key -> Key sondern Key -> Liste.
Genau, aber leider weiß ich immer noch nicht wie man die Aufgabe mit namedtuples lösen koennte.
BlackJack

@mit: Du musst ja nicht alles in den Speicher laden. Die Funktion lädt immer nur so viele Zeilen aus der Eingabedatei wie für *ein* Wörterbuch gebraucht werden.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@mit: falls Du sehen willst, wie nahe Du schon an der Lösung warst:

Code: Alles auswählen

from collections import namedtuple, defaultdict
from pprint import pprint
 
DATA = [
    ["Test", "A", "B01", 828288,  1,    7, 'C', 4],
    ["Test", "A", "B01", 828288,  1,    7, 'C', 5],
    ["Test", "A", "B01", 828288,  1,    7, 'T', 6],
    ["Test", "A", "B01", 171878,  3,    8, 'C', 5],
    ["Test", "A", "B01", 171878,  3,    8, 'T', 6],
    ["Test", "A", "B01", 871963,  3,    9, 'A', 5],
    ["Test", "A", "B01", 871963,  3,    9, 'G', 6],
    ["Test", "A", "B01", 1932523, 1,   10, 'T', 4],
    ["Test", "A", "B01", 1932523, 1,   10, 'A', 5],
    ["Test", "A", "B01", 1932523, 1,   10, 'X', 6],
    ["Test", "A", "B01", 667214,  1,   14, 'T', 4],
    ["Test", "A", "B01", 667214,  1,   14, 'G', 5],
    ["Test", "A", "B01", 667214,  1,   14, 'G', 6],
]
 
Key = namedtuple('Key', "type name sub_name pos s_type x_type")
Value = namedtuple('Value', "letter no")

def main():
    grouped_data = defaultdict(list)
    for row in DATA:
        grouped_data[Key(*row[:6])].append(Value(*row[6:]))
    pprint(grouped_data)
    
if __name__ == '__main__':
    main()
mit
User
Beiträge: 285
Registriert: Dienstag 16. September 2008, 10:00

@BlackJack: Ich habe versucht die listen Inhalt in eine Datei gespeichert:

Code: Alles auswählen

Test, A, B01, 828288,  1,    7, C, 5
Test, A, B01, 828288,  1,    7, T, 6
Test, A, B01, 171878,  3,    7, C, 5
Test, A, B01, 171878,  3,    7, T, 6
Test, A, B01, 871963,  3,    9, A, 5
Test, A, B01, 871963,  3,    9, G, 6
Test, A, B01, 1932523, 1,   10, T, 4
Test, A, B01, 1932523, 1,   10, A, 5
Test, A, B01, 1932523, 1,   10, X, 6
Test, A, B01, 667214,  1,   14, T, 4
Test, A, B01, 667214,  1,   14, G, 5
Test, A, B01, 667214,  1,   14, G, 6
Ich verwende eine "convert" Funktion, welche Datentypen erkennt und diese umwandelt.

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from itertools import groupby


DATA = [["Test", "A", "B01", 828288,  1,    7, 'C', 5],
        ["Test", "A", "B01", 828288,  1,    7, 'T', 6],
        ["Test", "A", "B01", 171878,  3,    7, 'C', 5],
        ["Test", "A", "B01", 171878,  3,    7, 'T', 6],
        ["Test", "A", "B01", 871963,  3,    9, 'A', 5],
        ["Test", "A", "B01", 871963,  3,    9, 'G', 6],
        ["Test", "A", "B01", 1932523, 1,   10, 'T', 4],
        ["Test", "A", "B01", 1932523, 1,   10, 'A', 5],
        ["Test", "A", "B01", 1932523, 1,   10, 'X', 6],
        ["Test", "A", "B01", 667214,  1,   14, 'T', 4],
        ["Test", "A", "B01", 667214,  1,   14, 'G', 5],
        ["Test", "A", "B01", 667214,  1,   14, 'G', 6]]


def iter_something(rows):
    key_names = ['type', 'name', 'sub_name', 'pos', 's_type', 'x_type']
    chr_key_names = ['letter', 'no']
    for keys, group in groupby(rows, lambda row: row[:6]):
        result = dict(zip(key_names, keys))
        result['chr'] = [dict(zip(chr_key_names, row[6:])) for row in group]
        yield result


def convert(val):
    constructors = [int, str]
    for c in constructors:
        try:
            return c(val)
        except ValueError:
            pass


def main():
    with open("/home/mic/tmp/test.txt") as f:
        for line in f:
            try:
                data = [convert(part.strip()) for part in line.split(',')]
                print(data)
            except IndexError:
                continue
    
        for object_ in iter_something(data):
            print(object_)


if __name__ == '__main__':
    main()
Wie könnte man an "iter_something" jede Dateizeile übergeben in den oberen Code?
Gibt es eine bessere Methode wie man Elemente in einer Liste umwandeln könnte, da man in diesem Beispiel weiß welches Element welchen Typ hat?

@Sirius3: Danke, manchmal sieht man den Wald vor lauter Bäumen nicht.
BlackJack

@mit: Man könnte einen Iterator oder Generator schreiben der die Daten liefert und den übergeben. Die Funktion nimmt ja beliebige iterierbare Objekte die Listen mit entsprechend vielen Elementen liefern.

Und wieso musst Du so eine Typumwandlung auf Verdacht machen? Du kennst doch den Typ in jeder Spalte, oder nicht?
mit
User
Beiträge: 285
Registriert: Dienstag 16. September 2008, 10:00

Danke fuer den Tipp. Ich habe generator verwendet, aber wenn man part.strip() benutzt, bekomme ich AttributeError: 'list' object has no attribute 'strip'.

Code: Alles auswählen

def main():
    with open("/home/mic/tmp/test.txt") as f:
        parts = (line.split(',') for line in f)
        column = (part.strip() for part in parts)
        for object_ in iter_something(column):
            print(object_)
Ich möchte nicht eine Typumwandlung auf Verdacht machen und ich weiss den Typ in jeder Spalte, aber leider weiss ich nicht wie man es eleganter und effizienter macht.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

`parts` ist ein Generator von Listen, da `str.split` eine Liste zurueckgibt. Darum ist `part` auch eine Liste und kein String.
Demnach brauchst du fuer `column` eine verschachtelte Generator Expression, zB

Code: Alles auswählen

column = (entry.strip() for part in parts for entry in part)
mit
User
Beiträge: 285
Registriert: Dienstag 16. September 2008, 10:00

cofi hat geschrieben:`parts` ist ein Generator von Listen, da `str.split` eine Liste zurueckgibt. Darum ist `part` auch eine Liste und kein String.
Demnach brauchst du fuer `column` eine verschachtelte Generator Expression, zB

Code: Alles auswählen

column = (entry.strip() for part in parts for entry in part)
Leider bekomme ich jetzt eine seltsame Ausgabe:

Code: Alles auswählen

{'sub_name': 's', 'chr': [{}], 'type': 'T', 'name': 'e', 'pos': 't'}
{'chr': [{}], 'type': 'A'}
{'sub_name': '1', 'chr': [{}], 'type': 'B', 'name': '0'}
{'sub_name': '8', 'name': '2', 'x_type': '8', 'pos': '2', 's_type': '8', 'chr': [{}], 'type': '8'}
{'chr': [{}], 'type': '1'}
{'chr': [{}], 'type': '7'}
{'chr': [{}], 'type': 'C'}
{'chr': [{}], 'type': '5'}
{'sub_name': 's', 'chr': [{}], 'type': 'T', 'name': 'e', 'pos': 't'}
{'chr': [{}], 'type': 'A'}
{'sub_name': '1', 'chr': [{}], 'type': 'B', 'name': '0'}
{'sub_name': '8', 'name': '2', 'x_type': '8', 'pos': '2', 's_type': '8', 'chr': [{}], 'type': '8'}
.....
Antworten