Datenimport und Formate

Data Wrangling
R
Python
CSV, Excel, JSON und Pickle lesen und schreiben, Kodierung beachten.

Kernideen

  • Der erste Blick nach dem Import gehört den Spaltentypen, nicht den Werten.
  • Trennzeichen, Dezimalzeichen, Tausenderzeichen und Kodierung werden angegeben, nicht geraten.
  • Ein Datum wird beim Import umgewandelt. Als Text sortiert es falsch.
  • Eine Kennung wie 0815 ist Text, keine Zahl. Sonst fällt die führende Null weg.
  • CSV trägt keine Typen. Datum, Kategorie und ganze Zahl mit fehlendem Wert kommen als etwas anderes zurück.
  • Verschachteltes JSON wird flach gemacht, bevor damit gerechnet wird.

Erklärung

Vorwissen: R-Grundlagen oder pandas für Data Frames und Spaltentypen. Was danach kommt, ist die Kontrolle auf Datenqualität und das Umformen im Data Wrangling. Wie Dateien technisch gelesen und geschrieben werden, steht unter Fehlerbehandlung und Dateizugriff.

Der erste Blick gehört den Spaltentypen

Ein Import meldet selten einen Fehler. Er liefert eine Tabelle, die richtig aussieht, und der Schaden zeigt sich erst in der Auswertung: eine Spalte, die Text statt Zahl ist, ein Datum, das alphabetisch sortiert, eine Kennung ohne führende Null. Deshalb ist der erste Befehl nach jedem Import str() beziehungsweise df.dtypes, und erst der zweite schaut auf die Werte.

Die vier Einstellungen, die im deutschsprachigen Raum nötig sind

Einstellung Beispiel in der Datei R (readr) pandas
Spaltentrenner ; read_csv2() sep=";"
Dezimalzeichen 12,4 in read_csv2() enthalten decimal=","
Tausenderzeichen 1.234 in read_csv2() enthalten thousands="."
Kodierung Fräse locale(encoding = "UTF-8") encoding="utf-8"

read_csv2() bringt die ersten drei Einstellungen mit, in pandas werden sie einzeln angegeben. Das ist der häufigste Unterschied beim Übertragen von Importcode zwischen den Sprachen.

Typen vorgeben statt raten lassen

Beide Sprachen raten den Typ aus den ersten Zeilen. Das geht gut, solange die Daten sauber sind, und schiefgehen kann es auf zwei Arten: Eine Kennung mit führenden Nullen wird zur Zahl, oder ein einziger Sonderwert macht eine Zahlenspalte zu Text. Deshalb gibt man die Typen der wichtigen Spalten vor:

read_csv2("daten.csv", col_types = cols(
  plz = col_character(), menge = col_integer(), datum = col_date("%d.%m.%Y")))
pd.read_csv("daten.csv", sep=";", decimal=",",
            dtype={"plz": str}, parse_dates=["datum"], dayfirst=True)

Die Formate und wozu sie taugen

Format trägt Typen lesbar für wofür
CSV nein alles, auch Tabellenkalkulationen Austausch, Archiv
Excel teilweise Menschen Vorlagen, Handarbeit; readxl beziehungsweise openpyxl
JSON einfache Typen Menschen und Programme Schnittstellen, verschachtelte Antworten
Parquet ja viele Sprachen grosse Tabellen, spaltenweise, komprimiert
RDS, Pickle vollständig nur die eigene Sprache Zwischenstände einer Auswertung

Für einen Zwischenstand ist der Typverlust der wichtigere Punkt als die Dateigrösse. Wer eine bereinigte Tabelle als CSV ablegt, bereinigt sie beim nächsten Einlesen erneut.

Kurz nachgeschlagen

Aufgabe R Python
CSV mit Semikolon und Dezimalkomma read_csv2("d.csv") pd.read_csv("d.csv", sep=";", decimal=",")
Typen vorgeben col_types = cols(...) dtype={...}, parse_dates=[...]
nur die ersten Zeilen n_max = 1000 nrows=1000
nur bestimmte Spalten col_select = c(a, b) usecols=["a", "b"]
Excel readxl::read_excel("m.xlsx", sheet = "Daten") pd.read_excel("m.xlsx", sheet_name="Daten")
JSON flach machen jsonlite::flatten() pd.json_normalize()
schreiben ohne Zeilennamen write_csv(df, "e.csv") df.to_csv("e.csv", index=False)
mit Typen ablegen saveRDS(df, "d.rds") df.to_pickle("d.pkl")

Beispiele

Frage und Datenlage

Eine Datei mit drei Messungen, so wie sie aus einem deutschsprachigen Export kommt: Semikolon als Trenner, Komma als Dezimalzeichen, Datum als 01.03.2026. Was passiert ohne Einstellungen, was mit, und warum muss das Datum umgewandelt werden?

Rechnung

inhalt <- c("datum;typ;verbrauch",
            "01.03.2026;A;12,4",
            "02.03.2026;B;9,8",
            "03.03.2026;A;14,1")
pfad <- tempfile(fileext = ".csv")
writeLines(inhalt, pfad)

falsch <- read_csv(pfad, show_col_types = FALSE)     # Komma als Trenner
ncol(falsch)
[1] 1
richtig <- read_csv2(pfad, show_col_types = FALSE)   # Semikolon und Komma
sapply(richtig, class)
      datum         typ   verbrauch 
"character" "character"   "numeric" 
sum(richtig$verbrauch)
[1] 36.3
mit_datum <- mutate(richtig, datum = dmy(datum))
sapply(mit_datum, class)
      datum         typ   verbrauch 
     "Date" "character"   "numeric" 
sort(c("01.03.2026", "02.02.2026"))                  # als Text
[1] "01.03.2026" "02.02.2026"
sort(dmy(c("01.03.2026", "02.02.2026")))             # als Datum
[1] "2026-02-02" "2026-03-01"
inhalt = "\n".join(["datum;typ;verbrauch",
                    "01.03.2026;A;12,4",
                    "02.03.2026;B;9,8",
                    "03.03.2026;A;14,1"])
pfad = ordner / "messung.csv"
_ = pfad.write_text(inhalt, encoding="utf-8")

falsch = pd.read_csv(pfad)                              # Standardannahme
print(falsch.shape, list(falsch.columns))
(3, 1) ['datum;typ;verbrauch']
richtig = pd.read_csv(pfad, sep=";", decimal=",")
print(richtig.dtypes.astype(str).to_dict())
{'datum': 'object', 'typ': 'object', 'verbrauch': 'float64'}
print(richtig["verbrauch"].sum())
36.300000000000004
richtig["datum"] = pd.to_datetime(richtig["datum"], format="%d.%m.%Y")
print(richtig.dtypes.astype(str).to_dict())
{'datum': 'datetime64[ns]', 'typ': 'object', 'verbrauch': 'float64'}
print(sorted(["01.03.2026", "02.02.2026"]))
['01.03.2026', '02.02.2026']
print(list(richtig["datum"].sort_values().dt.strftime("%d.%m.%Y")))
['01.03.2026', '02.03.2026', '03.03.2026']

Output Zeile für Zeile

Schritt Python R Erklärung
ohne Einstellungen Form (3, 1), eine Spalte namens datum;typ;verbrauch ncol = 1 Ohne Semikolon als Trenner steht die ganze Zeile in einer Spalte. Kein Fehler, nur eine unbrauchbare Tabelle.
mit Einstellungen datum Text, typ Text, verbrauch float64 character, character, numeric Erst mit decimal="," beziehungsweise read_csv2 wird verbrauch eine Zahl.
Summe Verbrauch 36.300000000000004 36.3 Dieselbe Zahl. Python zeigt die Gleitkommadarstellung ungerundet, R rundet bei der Ausgabe auf sieben Stellen.
Datum umgewandelt datetime64[ns] Date to_datetime mit format beziehungsweise dmy().
Text sortiert ['01.03.2026', '02.02.2026'] dieselbe Reihenfolge Falsch: Der 1. März steht vor dem 2. Februar, weil zeichenweise verglichen wird.
Datum sortiert 01.03., 02.03., 03.03. 2026-02-02, 2026-03-01 Als Datum stimmt die Reihenfolge, und Differenzen lassen sich rechnen.

Interpretation und Ergebnissatz

Der Import ohne Einstellungen liefert hier eine Tabelle mit einer einzigen Spalte, das fällt sofort auf. Gefährlicher ist der halbe Erfolg: Trenner richtig, Dezimalzeichen falsch. Dann steht in verbrauch Text, jede Summe bricht ab oder ist falsch, und die Ursache liegt drei Schritte zurück.

Ohne Angabe des Trenners entsteht eine Tabelle mit einer einzigen Spalte; mit sep=";" und decimal="," wird verbrauch numerisch und summiert sich zu 36.3. Das Datum muss ausdrücklich umgewandelt werden, sonst steht der 1. März beim Sortieren vor dem 2. Februar.

Frage und Datenlage

Eine Artikelliste mit vier Spalten: Kennung, Postleitzahl 8001, 0815, 3011, Menge 1.234, 987 und ein fehlender Wert, Preis mit Dezimalkomma. Was macht der Import daraus, wenn man ihn raten lässt?

Rechnung

csv2 <- c("id;plz;menge;preis",
          "1;8001;1.234;12,50",
          "2;0815;987;9,90",
          "3;3011;;7,25")
pfad2 <- tempfile(fileext = ".csv")
writeLines(csv2, pfad2)

geraten <- read_csv2(pfad2, show_col_types = FALSE)
sapply(geraten, class)
         id         plz       menge       preis 
  "numeric" "character"   "numeric"   "numeric" 
geraten$plz
[1] "8001" "0815" "3011"
geraten$menge
[1] 1234  987   NA
ganzzahl <- read_csv2(pfad2, show_col_types = FALSE,
                      col_types = cols(plz = col_character(),
                                       menge = col_integer()))
ganzzahl$menge                       # col_integer() kennt kein Tausenderzeichen
[1]  NA 987  NA
genau <- read_csv2(pfad2, show_col_types = FALSE,
                   col_types = cols(plz = col_character(),
                                    menge = col_number()))
genau$plz
[1] "8001" "0815" "3011"
genau$menge
[1] 1234  987   NA
sum(genau$menge, na.rm = TRUE)
[1] 2221
csv2 = "id;plz;menge;preis\n1;8001;1.234;12,50\n2;0815;987;9,90\n3;3011;;7,25\n"
pfad2 = ordner / "artikel.csv"
_ = pfad2.write_text(csv2, encoding="utf-8")

geraten = pd.read_csv(pfad2, sep=";", decimal=",")
print(geraten.dtypes.astype(str).to_dict())
{'id': 'int64', 'plz': 'int64', 'menge': 'object', 'preis': 'float64'}
print(geraten["plz"].tolist(), geraten["menge"].tolist())
[8001, 815, 3011] ['1.234', '987', nan]
try:                                   # Tausenderzeichen und Int64 zusammen
    pd.read_csv(pfad2, sep=";", decimal=",", thousands=".",
                dtype={"plz": str, "menge": "Int64"})
except TypeError as fehler:
    print(type(fehler).__name__, str(fehler)[:60])
TypeError cannot safely cast non-equivalent float64 to int64
genau = pd.read_csv(pfad2, sep=";", decimal=",", thousands=".",
                    dtype={"plz": str})
genau["menge"] = genau["menge"].astype("Int64")
print(genau["plz"].tolist(), genau["menge"].tolist(), genau["menge"].sum())
['8001', '0815', '3011'] [1234, 987, <NA>] 2221

Output Zeile für Zeile

Spalte geraten in pandas geraten in R Erklärung
plz [8001, 815, 3011] 815 statt 0815 Beide machen aus der Kennung eine Zahl, und die führende Null ist weg. Eine Postleitzahl ist Text.
menge Text: ['1.234', '987', nan] 1234, 987, NA Hier trennen sich die Sprachen: read_csv2 kennt den Punkt als Tausenderzeichen, pandas braucht thousands="." und liest die Spalte sonst als Text.
preis float64 numeric Das Dezimalkomma war angegeben.
thousands mit dtype="Int64" TypeError pandas wandelt zuerst in Gleitkomma und kann dann nicht mehr verlustfrei in die ganzzahlige Spalte giessen. Erst lesen, dann astype("Int64").
mit Vorgaben ['8001', '0815', '3011'], Mengen [1234, 987, <NA>], Summe 2221 0815 bleibt; mit col_number() ebenfalls 1234, 987, NA und Summe 2221 Int64 mit grossem I trägt fehlende Werte, int64 nicht.
R mit col_integer() NA, 987, NA, Summe nur 987 Die ausdrückliche Ganzzahl-Vorgabe kennt das Tausenderzeichen nicht und wirft den Wert weg, den read_csv2 ohne Vorgabe richtig gelesen hätte. Für Zahlen mit Trennzeichen ist col_number() die richtige Angabe.

Interpretation und Ergebnissatz

Zwei Fehler entstehen hier stumm: die verlorene führende Null und die Mengenspalte, die in pandas Text bleibt. Die erste fällt später auf, wenn ein Join auf die Postleitzahl nichts findet, die zweite beim ersten Rechnen. read_csv2 nimmt einem in R mehr ab, dafür ist in pandas jede Annahme im Aufruf sichtbar.

Ohne Vorgaben wird aus der Kennung 0815 die Zahl 815, und die Menge 1.234 bleibt in pandas Text, während read_csv2 daraus 1234 macht. Mit dtype, thousands und Int64 stehen am Ende die Mengen 1234, 987 und ein fehlender Wert mit der Summe 2221.

Frage und Datenlage

Eine UTF-8-Datei mit Umlauten in Spaltenname und Wert (gerät, grösse, Fräse). Was passiert beim Lesen mit der falschen Kodierung?

Rechnung

pfad3 <- tempfile(fileext = ".csv")
writeLines(c("gerät;grösse", "Fräse;12,5"), pfad3, useBytes = TRUE)

utf <- read_csv2(pfad3, show_col_types = FALSE)
names(utf)
[1] "gerät"  "grösse"
utf[[1]][1]
[1] "Fräse"
lat <- read_csv2(pfad3, show_col_types = FALSE,
                 locale = locale(encoding = "latin1", decimal_mark = ","))
names(lat)
[1] "gerät"  "grösse"
lat[[1]][1]
[1] "Fräse"
pfad3 = ordner / "umlaute.csv"
_ = pfad3.write_bytes("gerät;grösse\nFräse;12,5\n".encode("utf-8"))

utf = pd.read_csv(pfad3, sep=";", decimal=",", encoding="utf-8")
print(list(utf.columns), utf.iloc[0, 0])
['gerät', 'grösse'] Fräse
lat = pd.read_csv(pfad3, sep=";", decimal=",", encoding="latin-1")
print(list(lat.columns), lat.iloc[0, 0])
['gerät', 'grösse'] Fräse
try:
    pd.read_csv(pfad3, sep=";", encoding="ascii")
except UnicodeDecodeError as fehler:
    print(type(fehler).__name__)
UnicodeDecodeError

Output Zeile für Zeile

Lesart Spaltennamen erster Wert Erklärung
UTF-8 gerät, grösse Fräse richtig
Latin-1 gerät, grösse Fräse Kein Fehler, sondern verdorbener Text. Betroffen sind auch die Spaltennamen, und ab da scheitert jeder Zugriff über den Namen.
ASCII UnicodeDecodeError ASCII bricht bei Bytes über 127 ab.

Das Erkennungszeichen bleibt dasselbe wie beim Dateizugriff: ä für ä, ö für ö. Wer es sieht, hat eine UTF-8-Datei als Latin-1 gelesen.

Interpretation und Ergebnissatz

Beim Import ist die falsche Kodierung schlimmer als beim blossen Lesen einer Textdatei, weil sie die Spaltennamen trifft. Ein df["gerät"] findet die Spalte dann nicht, und die Fehlermeldung nennt einen Schlüssel, der auf dem Bildschirm richtig aussieht. Deshalb gehört die Kodierung in jeden Importaufruf.

Mit Latin-1 gelesen heisst die Spalte gerät und der Wert Fräse, ohne jede Fehlermeldung; mit ASCII bricht der Import mit einem UnicodeDecodeError ab. Nur mit encoding="utf-8" stimmen Spaltennamen und Werte.

Frage und Datenlage

Die Antwort einer Schnittstelle: eine Anlage, darin eine Liste von Messungen, und jede Messung enthält wiederum ein Objekt sensor mit Typ und Seriennummer. Wie wird daraus eine Tabelle?

Rechnung

antwort <- list(anlage = "Werk Nord", messungen = list(
  list(id = 1, wert = 12.4, sensor = list(typ = "A", seriennummer = "S-1")),
  list(id = 2, wert = 9.8,  sensor = list(typ = "B", seriennummer = "S-2"))))

pfad4 <- tempfile(fileext = ".json")
jsonlite::write_json(antwort, pfad4, auto_unbox = TRUE)

roh <- jsonlite::fromJSON(pfad4)
names(roh)
[1] "anlage"    "messungen"
names(roh$messungen)
[1] "id"     "wert"   "sensor"
class(roh$messungen$sensor)
[1] "data.frame"
tabelle <- jsonlite::flatten(roh$messungen)
names(tabelle)
[1] "id"                  "wert"                "sensor.typ"         
[4] "sensor.seriennummer"
tabelle
  id wert sensor.typ sensor.seriennummer
1  1 12.4          A                 S-1
2  2  9.8          B                 S-2
antwort = {"anlage": "Werk Nord", "messungen": [
    {"id": 1, "wert": 12.4, "sensor": {"typ": "A", "seriennummer": "S-1"}},
    {"id": 2, "wert": 9.8, "sensor": {"typ": "B", "seriennummer": "S-2"}}]}
pfad4 = ordner / "antwort.json"
_ = pfad4.write_text(json.dumps(antwort, ensure_ascii=False), encoding="utf-8")

roh = pd.read_json(pfad4)
print(roh.shape, list(roh.columns))
(2, 2) ['anlage', 'messungen']
print(roh["messungen"].iloc[0])
{'id': 1, 'wert': 12.4, 'sensor': {'typ': 'A', 'seriennummer': 'S-1'}}
tabelle = pd.json_normalize(antwort["messungen"], sep="_")
print(list(tabelle.columns))
['id', 'wert', 'sensor_typ', 'sensor_seriennummer']
print(tabelle.to_string(index=False))
 id  wert sensor_typ sensor_seriennummer
  1  12.4          A                 S-1
  2   9.8          B                 S-2

Output Zeile für Zeile

Schritt Python R Erklärung
roh eingelesen Form (2, 2), Spalten anlage, messungen Liste mit anlage und messungen Die Spalte messungen enthält Dictionaries, keine Werte. Rechnen lässt sich damit nicht.
eine Zelle das ganze Dictionary mit sensor darin sensor ist ein eigener Data Frame Die Verschachtelung steckt in der Zelle.
flach gemacht Spalten id, wert, sensor_typ, sensor_seriennummer id, wert, sensor.typ, sensor.seriennummer json_normalize beziehungsweise jsonlite::flatten. Nur der Trenner unterscheidet sich, Unterstrich gegen Punkt.
Tabelle zwei Zeilen mit Werten 12.4 und 9.8 dieselben zwei Zeilen Jetzt ist es eine gewöhnliche Tabelle.

Interpretation und Ergebnissatz

JSON aus einer Schnittstelle ist fast nie flach. Wer die eingelesene Struktur ungeprüft weiterreicht, hat Spalten, die Objekte enthalten, und stolpert erst beim Gruppieren darüber. Das Flachmachen ist ein eigener Arbeitsschritt und gehört unmittelbar hinter den Import; die Namen wie sensor_typ dokumentieren dabei, woher ein Wert stammt.

Eingelesen enthält die Spalte messungen ganze Objekte; erst json_normalize beziehungsweise jsonlite::flatten macht daraus eine Tabelle mit den Spalten id, wert, sensor_typ und sensor_seriennummer.

Frage und Datenlage

Eine bereinigte Tabelle mit Datum, Kategorie und einer ganzzahligen Spalte mit einem fehlenden Wert wird als CSV geschrieben und wieder gelesen. Kommt dasselbe zurück?

Rechnung

df <- data.frame(datum = as.Date(c("2026-03-01", "2026-03-02")),
                 typ   = factor(c("A", "B")),
                 menge = c(5L, NA))
sapply(df, class)
    datum       typ     menge 
   "Date"  "factor" "integer" 
pfad5 <- tempfile(fileext = ".csv")
write_csv(df, pfad5)
readLines(pfad5)
[1] "datum,typ,menge" "2026-03-01,A,5"  "2026-03-02,B,NA"
zurueck <- read_csv(pfad5, show_col_types = FALSE)
sapply(zurueck, class)
      datum         typ       menge 
     "Date" "character"   "numeric" 
pfad6 <- tempfile(fileext = ".rds")
saveRDS(df, pfad6)
identical(readRDS(pfad6), df)
[1] TRUE
df = pd.DataFrame({"datum": pd.to_datetime(["2026-03-01", "2026-03-02"]),
                   "typ": pd.Categorical(["A", "B"]),
                   "menge": pd.array([5, None], dtype="Int64")})
print(df.dtypes.astype(str).to_dict())
{'datum': 'datetime64[ns]', 'typ': 'category', 'menge': 'Int64'}
pfad5 = ordner / "rund.csv"
df.to_csv(pfad5, index=False)
print(pfad5.read_text(encoding="utf-8").replace("\n", " | "))
datum,typ,menge | 2026-03-01,A,5 | 2026-03-02,B, | 
zurueck = pd.read_csv(pfad5)
print(zurueck.dtypes.astype(str).to_dict())
{'datum': 'object', 'typ': 'object', 'menge': 'float64'}
pfad6 = ordner / "rund.pkl"
df.to_pickle(pfad6)
print(pd.read_pickle(pfad6).equals(df))
True

Output Zeile für Zeile

Spalte vorher nach CSV in Python nach CSV in R Erklärung
datum datetime64[ns] object (Text) Date readr erkennt das ISO-Format beim Lesen wieder, pandas nicht ohne parse_dates.
typ category object character Die Kategorie mit ihren Stufen ist weg, in beiden Sprachen.
menge Int64 mit fehlendem Wert float64, der Wert wird zu NaN numeric Die CSV-Zeile lautet 2026-03-02,B,; ob dort eine leere Zahl, ein Text oder nichts stand, ist der Datei nicht anzusehen.
Datei drei Zeilen: Kopfzeile, 2026-03-01,A,5 und 2026-03-02,B, R schreibt dort 2026-03-02,B,NA Kein Typ in Sicht. pandas lässt die Zelle leer, write_csv schreibt das Wort NA; beim Einlesen erkennen beide ihre eigene Schreibweise wieder, fremde nicht ohne na-Angabe.
mit Pickle bzw. RDS True, alles identisch TRUE Das sprachgebundene Format bringt Datum, Kategorie und fehlenden Wert unverändert zurück.

Interpretation und Ergebnissatz

CSV ist ein Textformat, und Text hat keine Typen. Jede Bereinigung, die im Typ steckt (Datum, geordnete Kategorie, ganze Zahl mit fehlendem Wert), muss nach dem Einlesen wiederholt werden. Für den Austausch ist CSV trotzdem richtig, weil es jedes Programm liest; für den eigenen Zwischenstand nimmt man RDS, Pickle oder Parquet, und für regelmässige Importe schreibt man die Typvorgaben einmal auf und verwendet sie wieder.

Nach der Rundreise durch CSV ist aus dem Datum Text geworden, aus der Kategorie Text und aus der ganzzahligen Spalte eine Gleitkommaspalte; nur to_pickle beziehungsweise saveRDS liefert die Tabelle unverändert zurück.

Verständnisfragen

Eine Spalte kommt beim Import als Text an, obwohl nur Zahlen darin stehen. Das Dezimalzeichen ist richtig eingestellt. Was ist die nächstliegende Ursache?

Ein einzelner Sonderwert, etwa ein Strich für einen fehlenden Wert oder ein Tausenderpunkt
Richtig. Ein einziger solcher Eintrag genügt, weil eine Spalte nur einen Typ trägt. In Beispiel 2 macht 1.234 genau das.
Die Datei ist zu gross
Die Grösse beeinflusst den erkannten Typ nicht.
Zahlen lassen sich nur aus Excel-Dateien lesen
Der Import aus Text funktioniert, er muss nur richtig eingestellt sein.

Warum sollte das Datum direkt beim Import umgewandelt werden und nicht erst kurz vor der Grafik?

Weil die Umwandlung später länger dauert
Die Rechenzeit ist nicht das Problem.
Weil jeder Schritt dazwischen sonst mit Text arbeitet und falsch sortiert oder gruppiert
Richtig. Als Text steht der 1. März vor dem 2. Februar, und wer nach Monat gruppiert, gruppiert nach Zeichenketten.
Weil ggplot2 keine Datumsangaben darstellen kann
Kann es, sofern der Typ stimmt.

Nach einem Import steht in der Spalte plz die Zahl 815 statt 0815. Wie verhindert man das?

Die Spalte beim Import als Text vorgeben
Richtig, mit col_character() beziehungsweise dtype={"plz": str}. Nachträglich lässt sich die Null nur raten, denn 815 und 0815 sind als Zahl dasselbe.
Nach dem Import mit str_pad auffüllen
Das hilft nur, wenn alle Kennungen dieselbe Länge haben; sonst rät man.
Die Spalte in Excel formatieren
Die Formatierung ändert nichts am Inhalt der CSV.

Die Spaltennamen einer eingelesenen Tabelle lauten gerät und grösse. Was ist passiert, und was ist die Folge?

Eine UTF-8-Datei wurde als Latin-1 gelesen; jeder Zugriff über den Spaltennamen scheitert
Richtig. Der Fehler trifft nicht nur die Werte, sondern die Namen, und die Meldung nennt dann einen Schlüssel, der richtig aussieht.
Die Datei ist beschädigt
Die Bytes sind in Ordnung, nur ihre Deutung ist falsch.
Das Trennzeichen war falsch
Das ergäbe eine Spalte zu wenig, keine verdorbenen Umlaute.

Eine bereinigte Tabelle mit Datum und geordneter Kategorie wird als CSV abgelegt und am nächsten Tag wieder eingelesen. Was ist zu erwarten?

Datum und Kategorie sind Text, die Bereinigung muss wiederholt werden
Richtig, wie Beispiel 5 zeigt. Für Zwischenstände sind RDS, Pickle oder Parquet die richtige Wahl.
Alles kommt unverändert zurück, CSV speichert die Typen mit
CSV ist ein Textformat ohne Typinformation.
Die Datei lässt sich nicht mehr lesen
Lesen lässt sie sich immer, genau darin liegt ihr Wert.

Verlinkte Ressourcen