Mittels for-Schleife Elemente in einer multidimensionalen Liste ändern

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
cjteclab
User
Beiträge: 6
Registriert: Dienstag 14. Dezember 2021, 13:35

Hey Pythonianer,

ich sitze zur Zeit vor folgendem Problem und hoffe, dass ihr mir mit eurem Wissen und eurer Erfahrung weiterhelfen könnt.

Gegeben sei eine Liste, die als Elemente wiederum Listen beinhaltet:

Code: Alles auswählen

t_liste = [["tag", "8", "20"], ["Nacht", "21", "7"]]
Das Ziel besteht nun darin, die str-Elemente innerhalb der sub-Listen ("8", "20") und ("21", "7") in int-Elemente umzuwandeln.

Phase 1: In einem ersten Schritt habe ich dies durch folgenden Code erreicht:

Code: Alles auswählen

for i in t_liste:
	i[1] = int(i[1])
	i[2] = int(i[2])

print(t_liste) #output:  [["tag", 8, 20], ["Nacht", 21, 7]]
Phase 2: In einem zweiten Schritt habe ich mir anschließend die Frage gestellt, ob ich die beiden Anweisungen "i[1] = int(i[1])" und "i[2] = int(i[2])" mittels einer for-Schleife zusammenfassen kann, wie im folgenden Beispiel:

Code: Alles auswählen

for i in t_liste:
	for n in i[1:]:
		n = int(n)
An diesem Punkt komme ich nicht weiter. Die Änderungen der inneren for-Schleife werden anscheinend nicht in die äußere for-Schleife übertragen:

Code: Alles auswählen

for i in t_liste:
	for n in i[1:]:
		n = int(n)
		print(n) #output: 8; 20; 21; 7 --- Elemente werden wie gewünscht als int-Elemente ausgegeben
print(t_liste) # output: [["tag", "8", "20"], ["Nacht", "21", "7"]] --- Elemente werden als str-Elemente ausgegeben
Auf stackoverflow wurden ähnliche Thema behandelt (https://stackoverflow.com/q/52483266/17674191, https://stackoverflow.com/q/13752461/17674191). Demnach liegt das Problem anscheinend darin, dass bei der sub-Schleife eine Kopie der Liste erstellt wird. Jedoch gelang es mir nicht, die dortigen Lösungsansätze nachzuvollziehen oder speziell auf meine Fragestellung zu übertragen.

Ich stehe nun vor den Fragen:
(1) Was ist die Erklärung für meine Problemstellung?
(2) Falls das Problem tatsächlich darin liegt, dass in der sub-Schleife eine Kopie der Liste/des Elements erstellt wird, weshalb ist das nur in der sub-Schleife der Fall?
(3) Wie sieht eine mögliche Lösung für diese Problemstellung aus?

Ich freue mich sehr über eure Antworten und hoffe noch viel lernen zu können.

LG und ein schönes neues Jahr!
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es wird keine Kopie einer Liste erstellt. Der Unterschied liegt einfach darin, dass eine Zuweisung an die Teilliste, wie im ersten Code, im zweiten nunmal fehlt. Und in deinem Missverständnis, dass die Zuweisung eines Listenelements an den Namen n eine Art Referenz darstellen würde, die das entsprechende Element der Liste nachträglich ändern kann. Das stimmt nicht.

Die Lösung wäre zb eine List comprehension & Zuweisung:

i[1:] = [int(v) for v in i[1:]]

Ob das jetzt aber wirklich klarer als dein erster Ansatz ist, da wäre ich skeptisch. Es ist eh eine unglückliche Datenstruktur, heterogene Listen vermeidet man wenn möglich. Und noch mehr vermeidet man das verändern bestehender Datenstrukturen. Sondern baut die komplett neu auf.

Code: Alles auswählen

def convert(e):
    return [e[0], [int(v) for v in e[1:]]
t_liste = [convert(e) for e in t_liste]
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

Das `n` der for-Schleife referenziert auf den String (z.B. "8"); dass dieser String auch noch in einer Liste steht ist völlig unwichtig, und das ist auch gut so, denn sonst müßte man ja bei jeder Variable sich merken, wo diese schon überall benutzt worden ist, was technisch und gedanklich unmöglich wäre.
`n = int(n)` bindet dann ein neues Objekt (hier die Zahl 8) an den Namen `n`. Das weiß natürlich auch nicht, dass es mal an einen String gebunden war, der aus einer Liste kommt.

Ein genereller Grundsatz in Python ist es, wenn möglich keine Datenstrukturen zu ändern, sondern neue zu erzeugen:

Code: Alles auswählen

t_liste = [["tag", "8", "20"], ["Nacht", "21", "7"]]

zeitdefinitionen = []
for zeitbezeichnung, stunden, minuten in t_liste:
    zeitdefinitionen.append((zeitbezeichnung, int(stunden), int(minuten))
Hier schon mit sprechenden Variablennamen, weil t, i und n sind absolut nichts-sagend, was das Verstehen des Codes sehr erschwert.
Besser als ein Tuple wäre eine spezifischere Datenstruktur, und statt stunden und minuten getrennt zu speichern, passenderweise datetime.time zu verwenden.
cjteclab
User
Beiträge: 6
Registriert: Dienstag 14. Dezember 2021, 13:35

Hey,

vielen lieben Dank für eure Antworten. Ich glaube zu verstehen, wo mein Denkfehler bei den for-Schleifen gelegen hat.
Ich bin wirklich von dem Missverständnis ausgegangen, wie es @__deets__ geschildert hat, dass die Zuweisungen eine Art Referenz zu den eigentlichen Elementen herstellen.

Und danke @Sirius3 für deinen Lösungsvorschlag. Die Lösung gefällt mir, wäre aber selbst nicht auf die Idee gekommen. Ich war zu verbissen, die bestehnde Datenstruktur zu verändern.

Der Ursprung meiner Frage entsprang aus einem Kapitel (K21) in "Einführung in Data Science" von "Joel Grus". Im besagten Kapitel geht es darum die vorliegenden Daten zu einer "Wortwolke" zu verarbeiten. Die Daten liegen dabei tatsächlich als heterogene Liste vor (Name, x-Wert, y-Wert) um eine Zuordnung im Koordinatensystem zu ermöglichen.

Ich hatte in einem ersten Versuch die zu verarbeitenden Daten als csv.-Datei abgespeichert und mittels pandas eingelesen.

Code: Alles auswählen

data = pd.read_table('Verzeichnis/Dateiname.csv', header = None)
Das erhaltende Ergebnis war super. Pandas hat str-Variablen und int-Variablen von selbst erkannt. Das Problem war für mich, dass die pandas-Funktion wie eine Blackbox wirkt. Sie erspart mir Arbeit, fördert aber nicht wirklich mein Verständnis von der Materie.
Also hatte ich in einem zweiten Versuch die Daten ganz normal mittels "open" eingelesen und bin dann auf das genannte Problem gestoßen...

Danke für eure Anregungen und vermittelten Grundsätze.
Benutzeravatar
sparrow
User
Beiträge: 4540
Registriert: Freitag 17. April 2009, 10:28

Wenn du die Liste selbst erstellt hast, stellt sich aber die Frage, warum du die nicht gleich korrekt in die Liste speicherst sondern anschließend eine Nachbearbeitung nötig ist.
cjteclab
User
Beiträge: 6
Registriert: Dienstag 14. Dezember 2021, 13:35

Die csv.-Datei wurde mit dem folgenden Code eingelesen.

Code: Alles auswählen

daten = []
input_file = csv.reader(open('Verzeichnis/Dateiname.csv'))
for row in input_file:
    row[1] = int(row[1])
    row[2] = int(row[2])
    daten.append(row)
Dabei ist die obige Frage-/Problemstellung aufgekommen. Ich dachte, dass das obige abgeänderte Beispiel meine Fragestellung - Veränderungen von Elementen in einer sub-Liste - deutlicher wiedergibt.

Aber wenn du schon die Frage stellst, gibt es sicherlich eine elegantere Lösung, wie ich die csv.-Datei hätte einlesen können...

Wie hättet ihr die csv.-Datei eingelesen, um das gewünschte Ergebnis (str-Element, int-Element, int-Element) zu bekommen?
einfachTobi
User
Beiträge: 512
Registriert: Mittwoch 13. November 2019, 08:38

Code: Alles auswählen

import csv

with open("pfad", newline="") as csvfile:
    daten = [(row[0], int(row[1]), int(row[2])) for row in csv.reader(csvfile)]
Mit dem `with` wird sichergestellt, dass die Datei immer geschlossen wird. Nähere Infos findest du in der Doku zu csv in der Standard Library.
Sirius3
User
Beiträge: 18279
Registriert: Sonntag 21. Oktober 2012, 17:20

@einfachTobi: und auch hier gilt wieder, sprechende Variablennamen zu benutzen:

Code: Alles auswählen

import csv

with open("pfad", encoding="utf8", newline="") as csvfile:
    zeitdefinitionen = [
        (zeitbezeichnung, int(stunden), int(minuten))
        for zeitbezeichnung, stunden, minuten in csv.reader(csvfile)
    ]
cjteclab
User
Beiträge: 6
Registriert: Dienstag 14. Dezember 2021, 13:35

Ja jetzt fange ich an zu verstehen : D Comprehensions ist das Zauberwort.

@ Sirius3 : einfachTobi hat nur nichts-sagende Variablennamen benutzt, weil er auf mein letztes Beispiel geantwortet hat. Das geht auf meine Kappe :)

Super vielen lieben Dank an alle
einfachTobi
User
Beiträge: 512
Registriert: Mittwoch 13. November 2019, 08:38

Zu beidem ja. Ich habe die Namen benutzt um den Bezug zu deinem Beispiel herzustellen. Allerdings hat Sirius3 völlig Recht. Besser wären vernünftige Namen und eine Beschreibung im Post dazu gewesen.
Ohne Comprehension, falls es jemandem weiter hilft:

Code: Alles auswählen

import csv

zeitdefinitionen = []
with open("pfad", encoding="utf8", newline="") as csvfile:   
    for zeitbezeichnung, stunden, minuten in csv.reader(csvfile):
         zeitdefinitionen.append((zeitbezeichnung, int(stunden), int(minuten)))
Antworten