Programma dat CSV, Excel, JSON, vaste-breedte tekst, pickle en Python-snippets onderling converteert via een Pandas DataFrame. Demonstreert dtype-afleiding, datetime-parsing met tijdzonebeheer (UTC-normalisatie) en het omzetten naar een lijst-van-dicts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Joppe — Pandas Bestandsformaat-Converter
Een klein demo-programma dat een databestand inleest in een Pandas DataFrame en het terug wegschrijft in een ander formaat. Gemaakt om een Python-ontwikkelaar te tonen hoe Pandas formaatconversie omvormt tot een tweetraps-pijplijn:
bestand op schijf ──[lezer]──▶ DataFrame ──[schrijver]──▶ bestand op schijf
Ondersteunde formaten (automatisch gekozen op basis van de bestandsextensie):
| Extensie | Formaat | Pandas-lezer | Pandas-schrijver |
|---|---|---|---|
.txt |
Tekst met vaste breedte | pd.read_fwf |
zelf gemaakt |
.csv |
Kommagescheiden waarden | pd.read_csv |
df.to_csv |
.xlsx |
Excel-werkmap | pd.read_excel |
df.to_excel |
.json |
JSON (records) | pd.read_json |
df.to_json |
.pkl |
Python pickle | pickle.loads |
pickle.dumps |
.py |
Python-snippet (alleen uitvoer) | — | zelf gemaakt |
uv — Pakketbeheer voor Python
uv is een moderne, razendsnel pakketbeheerder
voor Python. Het vervangt de combinatie van pip, venv en pip-tools met
één enkel gereedschap.
uv installeren
Linux / macOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
Windows (PowerShell):
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Via pip (als je al een werkende Python hebt):
pip install uv
Controleer de installatie:
uv --version
Nieuw project aanmaken
uv init mijn-project
cd mijn-project
uv init maakt automatisch aan:
| Bestand | Inhoud |
|---|---|
pyproject.toml |
Projectnaam, Python-versie, dependencies |
.python-version |
Gewenste Python-versie (bv. 3.14) |
main.py |
Leeg startbestand |
README.md |
Leeg documentatiebestand |
Libraries toevoegen
uv add pandas openpyxl
uv add doet drie dingen tegelijk:
- Voegt de library toe aan
dependenciesinpyproject.toml. - Bepaalt compatibele versies en schrijft ze vast in
uv.lock. - Installeert alles in de virtuele omgeving van het project (
.venv/).
Een library verwijderen gaat met:
uv remove openpyxl
Een specifieke versie pinnen:
uv add "pandas>=2.0,<4"
pyproject.toml van dit project
[project]
name = "joppe"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
"openpyxl>=3.1.5",
"pandas>=3.0.2",
]
requires-python garandeert dat uv een compatibele Python-versie kiest.
dependencies bevat de minimale versievereisten; de exacte vergrendelde
versies staan in uv.lock.
Dit project installeren
Clone de repository en voer daarna uit:
uv sync
uv sync leest uv.lock, installeert de exacte versies en maakt .venv/
aan als die nog niet bestaat. Alle collega's krijgen zo exact dezelfde
omgeving.
Scripts uitvoeren
uv run python main.py sample.txt sample.csv
uv run zorgt automatisch dat de virtuele omgeving van het project actief
is. Je hoeft .venv/bin/activate nooit handmatig te roepen.
Wil je een gewone Python-REPL in de projectomgeving?
uv run python
Gebruik
uv run python main.py <invoerbestand> <uitvoerbestand>
Het programma kijkt naar de invoer-extensie om de lezer te kiezen, en naar de uitvoer-extensie om de schrijver te kiezen. Elke combinatie werkt.
Voorbeelden
Een voorbeeldbestand met vaste breedte, sample.txt, is meegeleverd om
alle formaten te testen. Het bevat 5 rijen en 6 kolommen die een mix van
datatypes dekken: integer (id, quantity), string (name), float
(price), ISO-datetime met tijdzone (created_at) en datum in
dd/mm/yyyy-formaat (delivery_date).
1. Vaste breedte → CSV
uv run python main.py sample.txt sample.csv
id,name,price,quantity,created_at,delivery_date
1,Widget,9.99,10,2026-05-08T12:30:00+0000,2026-05-15T00:00:00+0000
2,Gadget,19.95,5,2026-04-12T07:00:00+0000,2026-05-20T00:00:00+0000
3,Sprocket,0.5,250,2026-03-22T07:15:00+0000,2026-06-10T00:00:00+0000
Merk op dat created_at van +02:00 (lokale tijd Brussel) naar
+0000 (UTC) is omgezet: de tijd schuift van 14:30 naar 12:30, maar
het is hetzelfde absolute moment. Dat is de gouden regel — opslaan
in UTC.
2. CSV → JSON
uv run python main.py sample.csv sample.json
[
{
"id": 1,
"name": "Widget",
"price": 9.99,
"quantity": 10,
"created_at": "2026-05-08T12:30:00.000Z",
"delivery_date": "2026-05-15T00:00:00.000Z"
}
]
date_format="iso" is meegegeven aan to_json zodat datums leesbaar
blijven in plaats van als Unix-milliseconden te verschijnen.
3. JSON → Excel
uv run python main.py sample.json sample.xlsx
Excel-cellen kunnen geen tijdzone bevatten, dus het programma laat de tz-info vallen vlak voor het wegschrijven (de UTC-tijd zelf blijft behouden in de Excel-cel).
4. Excel → vaste breedte
uv run python main.py sample.xlsx roundtrip.txt
In de uitgevoerde .txt staat created_at weer in ISO 8601 met offset
en delivery_date weer in dd/mm/yyyy — elk in zijn eigen kolomstijl.
5. Naar pickle (.pkl)
uv run python main.py sample.txt sample.pkl
# of vanuit CSV, JSON, Excel — werkt allemaal
uv run python main.py sample.csv sample.pkl
Het pickle-bestand bevat een lijst van gewone Python-dicts met standaard
types (int, float, datetime). Daarna uitlezen:
import pickle
with open("sample.pkl", "rb") as f:
records = pickle.load(f)
print(records[0])
# {'id': 1, 'name': 'Widget', 'price': 9.99, 'quantity': 10,
# 'created_at': datetime(2026, 5, 8, 12, 30, 0, tzinfo=timezone.utc), ...}
print(type(records[0]["id"])) # <class 'int'>
print(type(records[0]["created_at"])) # <class 'datetime.datetime'>
Een pickle opnieuw inlezen als DataFrame:
uv run python main.py sample.pkl sample.csv
6. Naar Python-snippet (.py)
uv run python main.py sample.txt sample.py
# of vanuit CSV, Excel, JSON, pickle — allemaal ondersteund
uv run python main.py sample.csv sample.py
Dit genereert een kant-en-klaar Python-bestand:
# Auto-gegenereerd door main.py
from datetime import datetime, timezone
DATA = [
{
'id': 1,
'name': 'Widget',
'price': 9.99,
'quantity': 10,
'created_at': datetime(2026, 5, 8, 12, 30, 0, tzinfo=timezone.utc),
'delivery_date': datetime(2026, 5, 15, 0, 0, 0, tzinfo=timezone.utc),
},
...
]
Je kunt dit bestand kopiëren in een project of direct importeren:
from sample import DATA
for row in DATA:
print(row["name"], row["created_at"])
Datetimes zijn echte datetime-objecten (niet strings), zodat je ze
meteen kunt gebruiken voor berekeningen of vergelijkingen.
7. Volledige rondreis
Je kan de conversies aaneenschakelen om te controleren dat er niets verloren gaat:
uv run python main.py sample.txt stap1.csv
uv run python main.py stap1.csv stap2.json
uv run python main.py stap2.json stap3.xlsx
uv run python main.py stap3.xlsx stap4.pkl
uv run python main.py stap4.pkl eind.py
Van DataFrame naar lijst-van-dicts
De spil van de .pkl- en .py-uitvoer is df.to_dict(orient="records").
Dit zet elke rij van het DataFrame om naar één dict:
records = df.to_dict(orient="records")
# [{"id": 1, "name": "Widget", ...}, {"id": 2, ...}, ...]
En terug de andere kant op:
df = pd.DataFrame(records)
De orient-parameter
| orient | Resultaat | Gebruik |
|---|---|---|
"records" |
[{"col": val, ...}, ...] |
REST-API, fixtures |
"dict" |
{"col": {0: val, 1: val, ...}} |
kolom-opzoektabellen |
"list" |
{"col": [val, val, ...]} |
kolom-georiënteerde verwerking |
"index" |
{0: {"col": val}, ...} |
rij-index als sleutel |
"split" |
{"columns": [...], "data": [[...], ...]} |
compacte overdracht |
Voor de meeste situaties is "records" de juiste keuze.
Opgelet: NumPy/Pandas-types in dicts
to_dict() levert soms numpy.int64 of pd.Timestamp terug — die zijn
niet JSON-serialiseerbaar en gedragen zich subtiel anders dan ingebouwde
Python-types. Converteer ze expliciet:
import numpy as np
def to_plain_python(row: dict) -> dict:
result = {}
for k, v in row.items():
if isinstance(v, pd.Timestamp):
result[k] = v.to_pydatetime() # pd.Timestamp -> datetime
elif isinstance(v, np.integer):
result[k] = int(v) # numpy.int64 -> int
elif isinstance(v, np.floating):
result[k] = float(v) # numpy.float64 -> float
else:
result[k] = v
return result
records = [to_plain_python(r) for r in df.to_dict(orient="records")]
Dit programma doet dit automatisch in df_to_records() — zodat zowel
pickle als het Python-snippet correcte standaard-types bevatten.
Datums en tijdzones
Het demo-bestand mengt twee veelvoorkomende datumformaten in dezelfde rijen:
created_atin ISO 8601 met tijdzone-offset, bv.2026-05-08T14:30:00+02:00(lokale Brusselse tijd).delivery_datein dag-eerst notatie, bv.15/05/2026(geen tijd, geen tijdzone).
Bij het inlezen normaliseert parse_datetimes() beide naar UTC met
pd.to_datetime(..., format="mixed", utc=True). De truc:
format="mixed"laat Pandas per rij raden welk formaat van toepassing is — handig wanneer dezelfde kolom meerdere stijlen bevat.utc=Truezorgt dat alles tijdzone-bewust is in UTC, zodat naïeve en bewuste timestamps niet door elkaar lopen.
Voor de dd/mm/yyyy-kolom wordt extra dayfirst=True aangezet wanneer
de waarden schuine strepen bevatten — anders zou Pandas "01/02/2026"
als 2 januari lezen in plaats van 1 februari.
De drie nuttige tijdzone-operaties
# 1. Parse + normaliseer naar UTC in één stap (altijd veilig).
df["created_at"] = pd.to_datetime(df["created_at"], utc=True)
# 2. Plak een tijdzone op een naïeve timestamp (je weet welke zone bedoeld is).
df["created_at"] = df["created_at"].dt.tz_localize("Europe/Brussels")
# 3. Reken om naar een andere tijdzone (zelfde moment, andere weergave).
df["created_at"] = df["created_at"].dt.tz_convert("Europe/Brussels")
De gouden regel: opslaan in UTC, weergeven in lokale tijd. Roep operatie 3 alleen aan vlak voor het tonen of wegschrijven.
Wat te bestuderen in main.py
read_any/write_any— de keuze op basis van de bestandsextensie. Dat is het volledige idee van "formaatconversie" in 10 regels.- Argumenten van
pd.read_fwf—colspecs,names, en vooraldtype. Vaste-breedte bestanden bevatten geen type-informatie, dus je moet Pandas vertellen wat elke kolom is. parse_datetimes— laat zien hoe je gemengde datumformaten (ISO én dd/mm/yyyy) in één pass omzet naar UTC-Timestamps.df_to_records+_to_plain_python— het omzetten van DataFrame naar lijst-van-dicts met schone Python-types.write_fwf— een uitgewerkt voorbeeld van uitvoer bouwen vanuit een DataFrame door over kolommen en rijen te itereren. Handig wanneer er geen ingebouwde schrijver bestaat voor je doelformaat.
Bovenaan main.py staat een lange docstring die elke gebruikte lezer en
schrijver doorloopt; lees die naast de code.