Action unknown: copypageplugin__copy

Datenverarbeitung mit Python

Slides: gfif_datenverarbeitung_python.pdf

Lernziele

Datei einlesen

Das Code-Beispiel unten zeigt, wie man eine Textdatei einliest und die Anzahl Zeilen zählt, die es hat:

import io
 
# Oeffne File
with io.open('dateiname.txt', 'r', encoding='utf-8') as infile:
    line_count = 0 # Counter, um Anz. Zeilen zu zaehlen
    for line in infile: # gehe der Reihe nach alle Zeilen des Files durch
        line_count += 1
    print(line_count) # gebe am Schluss Anz. Zeilen aus

Bemerkungen:

  • Wenn Dateien gelesen oder geschrieben werden, müssen sie beim Betriebssystem reserviert werden. Mit with wird sichergestellt, dass die Datei nicht offen bleibt sondern nach dem Lesevorgang wieder geschlossen wird.
  • Die Datei (hier 'dateiname.txt') muss sich im gleichen Ordner befinden wie das Python-File.
  • 'r' bedeutet read, dass das angegebene File nur gelesen und nicht verändert wird.

Datei schreiben

Schreiben (also in Datei speichern) funktioniert ganz ähnlich wie lesen, aber als zweites Argument muss 'w' angegeben werden:

import io
 
with io.open('dateiname.txt', 'w', encoding='utf-8') as outfile:
    for i in range(10):
        line = "Zeile " + str(i) + "\n"
        outfile.write(line)

Bemerkungen:

  • str() verwandelt die Zahl in einen Text.
  • '\n' ist der ASCII-Code für einen Zeilenumbruch. Lässt man diese am Ende der Zeile weg, werden keine Zeilenumbrüche gemacht (mehr dazu unten).
  • 'w' steht für write: es wird in das File geschrieben.
  • Neben io.open(...) gibt auch die Funktion open(...), die praktisch identisch angewendet wird. Der wichtige Unterschied ist, dass man bei der ersteren die Codierung (typischerweise UTF-8) angeben kann. Dadurch werden z.B. Umlaute wie ä,ö,ü richtig dargestellt.

Zeilenumbrüche

Liest man ein Textfile ein, so endet jede Linie mit einem Zeilenumbruch. Achtung: Je nach System können dafür unterschiedliche ASCII/Unicode-Steuerzeichen verwendet werden. Der Code unten wandelt die Symbole im String line in die zugehörigen Positionen in der Unicode-Zeichentabelle um:

# String
line = "Hello Wörld\n"
print(' '.join(str(ord(c)) for c in list(line)))
# Output: 72 101 108 108 111 32 87 246 114 108 100 10

Wahrscheinlich wird die letzte Zahl eine $10$ sein, welche für das ASCII/Unicode-Steuerzeichen „Line Feed (LF)“ steht.

Liest man nun ein Textfile ein und printed jede Zeile, so wird es zwischen zwei eigentlichen Zeilen immer noch eine leere haben, da zwei Zeilenumbrüche eingefügt werden: Einen vom letzten Symbol jeder Linie und einen, den der print()-Befehl einfügt. Um dies zu vermeiden, gibt es zwei Möglichkeiten:

  1. Zeilenumbrüche am Ende der Zeile entfernen: line = line.replace('\n','')
  2. print ohne zusätzlichen Zeilenumbruch: print(line,end='')

Aufgabe A1: File Einlesen

  1. Erstelle mit einem beliebigen Editor ein Textfile mit Endung .txt und schreibe einige Zeilen Text hinein.
  2. Speichere es im gleichen Ordner, in dem du das zugehörige Python-File speichern wirst.
  3. Schreibe einen Python-Code, der dieses File einliest und folgendes macht:
    1. Anzahl Zeilen Text bestimmen und ausgeben.
    2. Jede Zeile in der Konsole ausgibt. Stelle sicher, dass keine überflüssigen Zeilenumbrüche angezeigt werden (siehe Theorie oben).

Aufgabe A2: File schreiben

  1. Schreibe einen Python-Code, der eine Liste mit mehreren Strings beinhaltet: s = ['Hallo Welt','Wie geht es dir?',...]
  2. Die Strings in dieser Liste sollen dann in ein neues Textfile geschrieben werden. Jeder Eintrag der Liste steht in einer eigenen Zeile.

Aufgabe A3: Buch einlesen

  1. Lade die Bibel herunter:
    1. Lade die Text Version (4.93 MB) herunter.
    2. Oder direkt hier vom Wiki. Achtung: Muss zuerst entzippt werden!
  2. Lese sie in Python ein.
  3. Wie viele Zeilen enthält das Buch?
  4. Gebe die ersten 20 Zeilen aus. Stelle wieder sicher, dass keine überflüssigen Zeilenumbrüche angezeigt werden.
  5. Erstelle eine Version des Buchs, welche nur aus Grossbuchstaben besteht (siehe Tipps unten).

Tipps

Zusatzaufgaben

  • Mache eine Häufigkeitsanalyse der Buchstaben in der Bibel. Unterscheide nicht zwischen Gross-/Kleinbuchstaben, eliminiere Umlaute und andere Spezialbuchstaben (z.B. a zu ae, ß zu ss). Welcher Buchstabe kommt am seltenesten vor? Tipp: Verwende ein Dictionary.

  • In der Theorie oben gibt es eine sehr kompakte Zeile Code, mit dem man von einem String die zugehörigen Unicode-Zeichen ermitteln kann. Schreibe selbst (ohne diese Zeile anzuschauen) einen mehrzeiligen Code, der dies macht. Vergleiche dann deinen Code mit dieser Zeile und versuche diese nachzuvollziehen.

CSV-Files

Daten werden oft in CSV-Files gespeichert, wobei CSV für Comma Separated Values steht. Ein CSV-File ist ein einfaches Textfile, in dem die Werte durch ein Sonderzeichen, genannt Delimiter, getrennt werden. Wie der Name suggeriert, verwendet man dafür oft ein Komma. Dies ist problematisch, wenn man das Komma bereits verwendet, um Nachkommastellen von Zahlen abzutrennen. Gerne verwendet man deshalb ein Semikolon.

String-Manipulation

Folgende Befehle sind sehr hilfreich, um die Strings, die man aus Dateien einliest, so zu manipulieren, dass man gut mit ihnen arbeiten kann:

  • x = li[3]: Bestimmtes Element aus Liste auslesen
  • teil_li = li[3:8]: Teilliste auslesen
  • int(s): String s der Zahl beinhaltet in ganze Zahl (integer) umwandeln
  • float(s): String s der Zahl beinhaltet in Zahl mit Nachkommastellen (float) umwandeln
  • str(z): Zahl z in String umwandeln
  • s = s.replace('a','A'): Zeichen in String s durch anderes ersetzen
  • l = s.split(','): String s immer dort wo ein , (oder welches Zeichen auch immer man angibt) auftritt aufgeteilt und in Liste l gespeichert
  • s = s.strip() entfernt links und rechts im String alle Leerschläge. Funktioniert auch für andere Zeichen, z.B. s = s.strip('_'). Mit lstrip resp. rstrip nur von links, resp. rechts, Zeichen entfernen.

CSV-Files lesen & schreiben

Mithilfe des csv-Moduls können CSV-Dateien einfach eingelesen werden:

import csv
import io
 
with io.open('dateiname.csv', 'r', encoding='utf-8', newline='') as csv_file:
    # Create a CSV reader object, change delimiter if elements are separated by different symbol than comma ','
    reader = csv.reader(csv_file,delimiter=',')
 
    # Read each row in the CSV file, creates list of all elements (separated by delimiter) in line
    for row in reader:
        print(row)

Beachte den Parameter newline=''. Dieser ist optional, vermeidet aber Probleme, die auftreten können, wenn CSV-Dateien zwischen verschiedenen Systemen (z.B. Windows und Mac) ausgetauscht werden, da diese beiden Plattformen Zeilenumbrüche anders handeln.

In ein CSV-File schreiben geht dann wie folgt. Erstelle zuerst eine Liste, die die Daten beinhaltet: Diese besteht wiederum aus Listen, die die Inhalte jeder Zeile beinhalten sollen:

data = [['Hans',42],['Monika',37]]

Diese Liste kann nun ganz einfach in ein CSV-File geschrieben werden

import csv
import io
 
with io.open('dateiname.csv', 'w', , encoding='utf-8', newline='') as csv_file:
    # Create a CSV writer object
    writer = csv.writer(csv_file,delimiter=',')
 
    # Write the data to the CSV file
    writer.writerows(data)

Das Beispiel oben erzeugt dann ein CSV-File mit Inhalt

Hans,42
Monika,37

Zusatzinformationen

  • Neben der Codierung „UTF-8“ gibt es auch „UTF-8 with BOM“. UTF-8 with BOM (Byte Order Mark) ist eine Variante der UTF-8-Kodierung, die einen speziellen Marker am Anfang der kodierten Daten enthält. Der Byte Order Mark ist eine Byte-Folge (0xEF, 0xBB, 0xBF), die die Byte-Reihenfolge und das verwendete Kodierungsschema angibt.

Schreibe ein Programm, welches eine Notentabelle im CSV-Format einliest. Für jedes Fach wird (falls möglich) der Durchschnitt berechnet und auf halbe Noten gerundet. Daraufhin werden berechnet/ermittelt:

  • Anzahl UGs
  • Anzahl Pluspunkte
  • Anzahl Minuspunkte doppelt
  • Promotion ('promoviert' oder 'nicht promoviert')

Sämtliche Resultate, inkl. gerundeter Noten der einzelnen Fächer, werden in ein neues CSV-File geschrieben.

Beispiel

Das Programm wird am Schluss einige Zeilen Code haben, weshalb es wichtig ist, dass du die Sache strukturiert angehst. Daher teilen wir den Auftrag in mehrere Teile ein.

Teil I

Ziel: Für jedes Fach durchschnitt berechnen und ausgeben (printen).

Befolge die Schritte und Tipps unten:

  1. Erstelle ein CSV-File mit einer Notentabelle. Achte darauf, dass unterschiedliche Fächer unterschiedlich viele Noten haben und es Fächer ohne Noten gibt. Verwende z.B. die Notentabelle aus dem Beispiel oben.
  2. Lese das CSV-File mit der Notentabelle ein. Verwende dazu das csv-Module (siehe Theorie oben).
  3. Gehe mit einer Schleife jede Zeile durch.
  4. Für jede Zeile erhält man eine Liste à la ['Mathematik', '3', '2', '3', '']
  5. Extrahiere das Fach und die Noten (siehe Tipp A)
  6. Berechne für das Fach den Notendurchschnitt (siehe Tipp B)
  7. Runde den Durchschnitt auf halbe Noten (siehe Tipp C)
  8. Gebe Fach und Durchschnitt in der Konsole aus

Tipps

Teil II

Ziel: Durchschnitte in neuem File speichern

Speichere die in Teil I berechneten Notendurchschnitte inkl. Fächernamen in einem neuen File notentabelle_resultate.csv.

Speichere dazu die Fächernamen und Durchschnitte in einer Daten-Liste: Jeder Eintrag dieser Liste soll dann einen Eintrag im neuen CSV-File sein, z.B.: data = [['Deutsch',5.0],['Französisch',5.0]]. Mit csv.writer kann diese Liste mit einem einzigen Befehl ins File geschrieben werden (siehe Tipps oben).

Teil III

Ziel: Restliche Dinge berechnen und in File speichern.

  1. Bestimme die Anzahl UGs, speichere in Variable
  2. Bestimme die Pluspunkte, speichere in Variable (siehe Tipps unten)
  3. Bestimme die Minuspunkte (doppelt), speichere in Variable
  4. Bestimme Promotion, speichere in Variable
  5. Füge passende Einträge zu data-Liste hinzu
  6. Sollte jetzt automatisch in CSV-File geschrieben werden (mit Code von Teil II)
  7. Das erwartete Output-CSV sollte etwa wie unten angegeben aussehen

Tipps

Erwartetes Output-CSV

Try-except

Wir haben gesehen, dass man mit int(...) und float(...) Zahlenstrings (z.B. '5' oder '3.14159') in Zahlen umwandeln kann. Dies geht aber nur, wenn man diese Funktionen auf passende Strings anwendet, ansonsten gibt es einen Fehler und das Programm bricht ab. Beispiele:

  • int('6'): funktioniert
  • float('4.5'): funktioniert
  • int('4.5'): Fehler! Weil $4.5$ keine ganze Zahl
  • int('') oder float('ksr') Fehler! Weil String leer resp. Text beinhaltet.

Ist man nicht sicher, ob man einen Fehler erhalten wird, so kann man try-except anwenden, mit dem man Fehler abfangen kann:

st = '4.5' # define string that may or may not contain a number
try: # try ...
    nr = float(st) # ... to convert string into float
    print(nr) # if works, print number
except: # if float(st) raises error (exception), code in except is executed
    print('String does not contain number')

Teil IV

Studiere die Theorie oben zum Thema Try-Except und mache dich mit dieser vertraut:

  1. Tippe das entsprechende Code-Snippet ab (nicht copy-paste).
  2. Teste es für verschiedene Strings, wie z.B. st = '3.14159', st = 'ksr', s = 4cm.
  3. Verwende es, um eine Notenliste wie ['5.5','6','5','','',''] umzuwandeln in [5.5,6,5]. Mit try-except ist es nicht mehr nötig, zu überprüfen, ob man float() überhaupt auf ein Element anwenden kann. Man macht es einfach und schaut dann, ob ein Fehler auftritt oder nicht.

Teil V

  1. Mache eine Kopie deines Codes zur Notentabelle und passe diesen wie unten beschrieben an.
  2. Verwende try-except für die …
    1. Aufbereitung der Notenlisten (['5.5','6','5','','',''] zu [5.5,6,5])
    2. Berechnung des Durchschnitts (ergibt Fehler, Division durch 0, wenn keine Noten beinhaltet)

In diesem Auftrag geht es darum, einen grösseren Datensatz, der Informationen zu allen Gemeinden der Schweiz beinhaltet, einzulesen und auszuwerten.

CSV-Datensätze haben typischerweise einen Header, der angibt, welche Daten sich in welchen Spalten befinden. Zum Beispiel sehen die ersten Zeilen des Datensatzes dieser Übung wie folgt aus:

['Gemeinde', 'Kanton', 'Einwohner', 'Fläche']
['Aadorf', 'TG', '9216', '19.93']
['Aarau', 'AG', '21726', '12.36']
['Aarberg', 'BE', '4626', '7.94']
['Aarburg', 'AG', '8577', '4.40']

Die erste Zeile ist der Header und muss für die Datenverarbeitung ignoriert werden:

with open(...) ...:
    reader = csv.reader(csv_file)
    header = next(reader)
    ...

Der Header wird so in der Variablen header gespeichert. Nachher kann man wie bisher mit einer for-Schleife den Datensatz durchgehen.

  • Lade die Datei gemeinden.csv.zip herunter und entzippe sie.
  • Lese die Datei in Python ein.
  • Schreibe einen Code, der die Fragen unten beantwortet und die Antworten in passend formatierten Strings in eine Textdatei gemeinden_resultate.txt schreibt (alle Antworten zusammen in einem File).

Fragen:

  1. Wie viele Gemeinden gibt es in der Schweiz?
  2. Wie viele Einwohner hat die Schweiz?
  3. Welche Gemeinde hat am meisten Einwohner?
  4. Welche Gemeinde hat die grösste Fläche?
  5. Wie viele Städte (mehr als 10000 EW) gibt es in der Schweiz?

Zusatzaufgaben:

  1. Auf welchem Platz im Ranking 'Gemeinden mit den meisten Einwohnern' steht Romanshorn?
  2. Es gibt mehrere Ortschaften, die gleich viele Einwohner haben. Welches sind die beiden Orte mit den meiden Einwohnern, die identisch sind?

Antworten ohne Code

Dictionary

Ein Dictionary (kurz Dict) ist eine Datenstruktur in Python, die es ermöglicht, Daten als Schlüssel-Wert-Paare (key:value) zu speichern. Jeder Schlüssel in einem Dictionary muss eindeutig sein, und er wird mit einem Wert verknüpft. Dictionaries sind äusserst vielseitig und nützlich, um verschiedene Arten von Daten zu organisieren. Ein Dictionary findet für einen Key den zugehörigen Value sehr schnell, da Dictionaries mit Hash-Tables arbeiten (mehr dazu später).

Mit einem Dictionary kannst du zum Beispiel speichern, wie viele Früchte du noch hast:

fruits = {'Apfel': 5, 'Banane': 3, 'Orange': 2}

Jedem Key wird also ein Wert zugeordnet. Zum Beispiel hat der Key „Banane“ den Wert $3$.

Über den Key kann man nun auf die Werte zugreifen:

print("Ich habe noch " + str(fruits['Banane']) + " Bananen.")

Isst man eine Banane, kann man den zugehörigen Wert anpassen:

fruits['Banane'] = 2
print(fruits) # -> {'Banane': 2, 'Orange': 2, 'Apfel': 5}

Wenn ein Schlüssel nicht vorhanden ist, führt der Zugriff zu einem Fehler. Um dies zu vermeiden, können Sie die Methode get() verwenden, die einen Standardwert zurückgibt, wenn der Schlüssel nicht gefunden wird:

print(fruits.get('Birne', 0))  # Gibt den Wert 0 aus, da 'Birne' nicht im Dictionary enthalten ist

Mit dem Keyword in kann man überprüfen, ob ein Key im Dictionary vorhanden is:

if 'Banane' in fruits:
    print('Ich habe noch Bananen zuhause.')
else:
    print('Ich muss Bananen kaufen.')

Neue Einträge können direkt über den neuen Key generiert werden:

fruits['Kiwi'] = 7
print(fruits) # -> {'Banane': 2, 'Orange': 2, 'Kiwi': 7, 'Apfel': 5}

Mit fruits.keys() und fruits.values() kann man alle Key-Value-Paare systematisch durchzugehen:

# Option 1
for key in fruits: # oder for key in fruits.keys()
    print(key, fruits[key])
 
# Option 2
for key,value in fruits.items():
    print(key,value)

Die Befehle .keys() und .values() erstellen „dict_key“ Objekte (zumindest in neueren Python Versionen). Möchte man aber eine Liste mit allen Keys oder Values eines Dicts haben, muss man diese mit der list()-Funktion noch in solche umwandeln:

key_list = list(fruits.keys())
value_list = list(fruits.values())

Interessiert man sich nur für die Werte, kann man auch einfach diese durchgehen:

for value in fruits.values():
    print(value)

Mit dem Keyword del kann man Einträge aus dem Dictionary löschen:

del fruits['Banane']
print(fruits) # -> {'Orange': 2, 'Kiwi': 7, 'Apfel': 5}

JSON

JSON steht für JavaScript Object Notation und ist ein Datenformat (my_file.json), welches sich zur Speicherung von Daten eignet. Es ist eine Alternative zu CSV und besonders für etwas komplexere, z.B. verschachtelte, Datensätze geeignet.

JSON repräsentiert Daten in Form von Schlüssel:Wert-Paaren (Key:Value) und unterstützt verschiedene Datentypen wie Strings, Zahlen, Booleans (True/False), Listen und Dictionaries. Es ähnelt daher der Struktur von Python-Dictionaries.

Hier zwei Beispiele für JSON-Daten:

  • Einfacher Datensatz (Key: Gemeinde, Value: PLZ):
{
    "Bern": 3000, 
    "Lausanne": 1000, 
    "Zurich": 8000, 
    "Basel": 4000, 
    "Genf": 1200
}
  • Komplexerer Datensatz (Key: Gemeinde, Value: Dict mit Infos zur Gemeinde):
{
    "Genthod": {
        "area": 2.81, 
        "inhabitants": 2893, 
        "canton": "GE"
    }, 
    "Gams": {
        "area": 22.28, 
        "inhabitants": 3587, 
        "canton": "SG"
    }, 
    "Rorbas": {
        "area": 4.5, 
        "inhabitants": 2885, 
        "canton": "ZH"
    }, 
    ...
}

JSON-Files können in Python ganz einfach in Dictinaries eingelesen werden:

import json
 
# LESEN von JSON-File
with open("my_file", "r") as json_file:
    data = json.load(json_file) # lade File in Dictionary data

Hat man in Python ein Dictionary, so kann man dieses ganz einfach in ein JSON-File schreiben:

import json
 
data = {} # wichtig: muss DICTIONARY sein! 
 
# SCHREIBEN von JSON-File
with open('my_file.json', 'w', encoding='utf-8') as json_file:
    json.dump(data, json_file, indent=4, ensure_ascii=False) # indent: Anz. Leerschlaege zum Einruecken

Aufgabe D1

Studiere die Theorie oben zu Dictionaries und JSON und löse dann die Aufgabe unten:

Die fünf bevölkerungsstärksten Städte mit ihrer jeweiligen Postleitzahl sind Zurich (8000), Genf (1200), Basel (4000), Lausanne (1000) und Bern (3000). Starte mit dem Dictionary:

big_5 = {'Zurich': 9999, 'Weinfelden': 8570, 'Basel': 4000, 'P3Rn': 3000}
  1. Korrigiere das Dictionary in möglichst wenigen Schritten.
  2. Speichere das fertige Dictionary in einem JSON-File.

Aufgabe D2: Voci-Trainer

Ziel ist, einen einfachen Voci-Trainer zu implementieren.

Version 1
  1. Erstelle zuerst ein Dictionary word_pairs. Der Key ist immer das Deutsche Wort (z.B. 'Katze') und der Value die englische Übersetzung ('cat'). Füge deinem Dictionary mindestens zehn solche Key-Value-Paare hinzu.
  2. Gehe nun einmal das Dictionary durch (Tipp: In der Theorie oben wird erklärt, wie man ein Dict durchgehen kann.). Fordere die Benutzerin auf, das deutsche Wort auf Englisch zu übersetzen.
  3. Die Eingabe wird dann mit der korrekten Antwort verglichen …
  4. … und es wird eine entsprechende Ausgabe gemacht, z.B. „Antwort korrekt“, „Antwort leider falsch“.
  5. Speichere den Score. Speichere dazu in zwei Variablen die Anzahl korrekten und falschen Antworten. Gebe den Score nach jeder Ausgabe aus.
Version 2
  1. Mache eine Kopie der letzten Version.
  2. Das Spiel soll nun unendlich lange gespielt werden können. In jedem Durchgang soll ein zufälliges Wort ausgewählt werden. Siehe Tipp unten.
  3. Alles andere soll gleich bleiben wie bei der letzten Version.

Tipp

Version 3 (optional, Pflicht für TALITs)

Mache eine Kopie der letzten Version und erweitere den Code wie folgt:

  1. Für jedes Wort soll unabhängig der Fortschritt (Progress) gespeichert werden (siehe Tipps unten).
  2. Übersetzt man ein Wort korrekt/falsch, so soll dessen Progress um eins erhöht/verringert werden. Negativer Progress soll verhindert werden.
  3. Hat ein Wort einen maximalen Progress (z.B. $3$) erreicht, wird es aus dem Dict entfernt und nicht mehr trainiert.
  4. Hat man alle Wörter vollständig gelernt, wird einem gratuliert und das Programm ist zu Ende.
  5. Optional:
    1. Nach jeder Frage soll man abbrechen können. Aktueller Stand wird dann in JSON-File geschrieben. Lösche keine Wörter aus dem File.
    2. Soll am Anfang des Spiels den Progress zurücksetzen können, damit man alle Wörter wieder lernen kann.
    3. Implementiere eigene Features.

Tipp

Aufgabe D3: Gemeinden

Teil I

Lese den Gemeinden-Datensatz aus dem CSV-File von weiter oben ein und speichere die Daten in einem Dictionary mit der folgenden Form:

{
    "Genthod": {
        "area": 2.81, 
        "inhabitants": 2893, 
        "canton": "GE"
    }, 
    "Gams": {
        "area": 22.28, 
        "inhabitants": 3587, 
        "canton": "SG"
    }, 
    "Rorbas": {
        "area": 4.5, 
        "inhabitants": 2885, 
        "canton": "ZH"
    }, 
    ...
}

Speichere dann das Dict in einem JSON-File gemeinden.json.

Teil II

Öffne nun in einem neuen Python-File das JSON-File gemeinden.json aus dem ersten Teil der Aufgabe und lese dessen Daten in ein Dictionary ein. Beantworte mit dessen Hilfe die folgenden Fragen:

  1. Wie viele Einwohner hat Aarwangen?
  2. Welche Fläche hat Estavayer?
  3. Welche Gemeinde hat $314$ Einwohner?
  4. Wie viele Gemeinden liegen im Kanton Tessin?

Lösungen kurz

  • gf_informatik/daten_sca/datenverarbeitung_python.txt
  • Zuletzt geändert: 2025-03-09 06:14
  • von sca