Een CSV inlezen, en controleren wat er is binnengekomen
Een bestand inlezen met read_csv en het direct inspecteren met shape, head, info en describe — omdat een verkeerd ingelezen bestand verkeerde antwoorden geeft zonder ooit een foutmelding te geven.
Hoe wil je leren?
Het probleem dat we oplossen
Tot nu toe is elke waarde in de tabel met de hand in de code getypt. Voor drie rijen werkt dat.
Echte data bevindt zich niet in code. Die zit in een bestand — gemaild door iemand, geëxporteerd uit een systeem, of opgeslagen vanuit een spreadsheet. En dat bestand heeft twaalfduizend rijen, geen twaalf.
Zo'n bestand ziet er meestal zo uit: platte tekst, de kolomnamen op de eerste regel, en vervolgens één rij per regel, gescheiden door komma's:
product,quantity,price
pen,12,15.0
notebook,5,60.0Dit is een CSV — comma-separated values. Dit omzetten in een tabel kost één regel pandas, en het is meteen de meest gebruikte regel in pandas.
Maar er is een gewoonte die je direct na die regel moet opbouwen, en dat is het eigenlijke onderwerp van dit hoofdstuk: zodra een bestand is ingelezen, bekijk je het. Hoeveel rijen er zijn binnengekomen, of de kolomnamen overeenkomen met wat je verwachtte, of de numerieke kolommen wel echt als getallen zijn binnengekomen. Sla die dertig seconden over en de fout duikt pas veel later op, wanneer een antwoord verkeerd is en er nergens een foutmelding is opgetreden.
Aan het einde van dit hoofdstuk kun je
- Een bestand inlezen met
pd.read_csv()en het padprobleem aanpakken dat daarop volgt - Een bestand snel controleren met
head(),tail(),shapeencolumns info()aflezen om rij-aantallen, lege cellen en het type van elke kolom in één keer te zien- De numerieke kolommen samenvatten met
describe() - Een
FileNotFoundErroren een verkeerd scheidingsteken herkennen en oplossen
Voorkennis: Series en DataFrame — de twee bouwstenen van pandas.
Maak eerst het bestand
Maak in je projectmap een bestand aan genaamd sales.csv met precies deze zes regels:
product,quantity,price
pen,12,15.0
notebook,5,60.0
bag,2,850.0
bottle,7,120.0
eraser,30,8.0De eerste regel is geen data, het zijn de kolomnamen — de header. De overige vijf regels zijn vijf rijen.
Het inlezen, in één regel
import pandas as pd
sales = pd.read_csv("sales.csv")
print(sales)product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
3 bottle 7 120.0
4 eraser 30 8.0Die ene regel deed drie dingen, en alle drie geruisloos:
Ten eerste heeft het de eerste regel als header gebruikt — waardoor product, quantity en price kolomnamen werden in plaats van een rij.
Ten tweede heeft het een index gebouwd, de 0 tot en met 4 aan de linkerkant. Die stond niet in het bestand.
Ten derde, en dit is wat er echt toe doet: het heeft het type van elke kolom geraden. Alles in het bestand is tekst — 12 bestaat eigenlijk uit twee tekens. Pandas bekeek elke kolom en besliste of het getallen of tekst waren.
Die gok klopt meestal. Maar wanneer het misgaat, treedt er geen fout op — en dat is precies waarom je controleert.
De vier dingen om te controleren na het inlezen van een bestand
shape — hoeveel er is binnengekomen
print(sales.shape)(5, 3)Vijf rijen, drie kolommen. Het bestand had zes regels en dit heeft vijf rijen, omdat de header geen rij is. Als het getal ver onder je verwachting ligt, of precies één te hoog is, is dat het eerste teken dat er iets verkeerd is ingelezen.
head() en tail() — het begin en het einde
print(sales.head(3))product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0Alle twaalfduizend rijen van een echt bestand printen vult de terminal en vertelt je niets. head() toont de eerste paar — vijf als je niets opgeeft. tail() doet hetzelfde aan het einde, en naar het einde kijken is op zichzelf al nuttig, omdat geëxporteerde bestanden daar onderaan vaak een verdwaalde lege regel of een totalenrij bevatten.
info() — alles in één oogopslag
sales.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 product 5 non-null object
1 quantity 5 non-null int64
2 price 5 non-null float64
dtypes: float64(1), int64(1), object(1)
memory usage: 252.0+ bytesMerk op dat er sales.info() staat en niet print(sales.info()) — deze methode print uit zichzelf en geeft niets terug, dus als je er print omheen zet, verschijnt er een verdwaalde None aan het einde.
Dit is het nuttigste om uit te voeren na het inlezen van een bestand, omdat het twee belangrijke feiten tegelijk toont:
Non-Null Count — hoeveel cellen in elke kolom daadwerkelijk gevuld zijn. Alle drie geven hier 5 non-null aan, dus niets is leeg. Een 4 non-null ergens betekent dat één cel in die kolom leeg is, en dat verandert je berekeningen.
Dtype — waar de gok op is uitgekomen. quantity kwam binnen als int64, price als float64, product als object. Dat is precies wat we wilden.
(Het getal bij memory usage op de laatste regel kan op jouw computer afwijken en maakt verder niet uit.)
describe() — een samenvatting van de numerieke kolommen
print(sales.describe())quantity price
count 5.00000 5.000000
mean 11.20000 210.600000
std 11.12205 360.217712
min 2.00000 8.000000
25% 5.00000 15.000000
50% 7.00000 60.000000
75% 12.00000 120.000000
max 30.00000 850.000000Het pikt de numerieke kolommen eruit en voert op elk acht berekeningen uit. product ontbreekt, omdat het tekst is.
Eén ding zou hier moeten opvallen: het gemiddelde (mean) van price is 210.6, terwijl de 50% — de mediaan — slechts 60 is. Een gat dat zo groot is betekent dat één waarde ver boven de rest uitsteekt, en max wijst hem aan: 850. Dat is hoe je describe() leest — wanneer het gemiddelde en de mediaan uit elkaar lopen, kijk dan dáár.
Een compleet voorbeeld
report.py:
import pandas as pd
sales = pd.read_csv("sales.csv")
print("Rows, columns:", sales.shape)
print()
print(sales.head(3))
print()
print("Columns :", list(sales.columns))
print("Total qty :", sales["quantity"].sum())
print("Avg price :", round(sales["price"].mean(), 2))Rows, columns: (5, 3)
product quantity price
0 pen 12 15.0
1 notebook 5 60.0
2 bag 2 850.0
Columns : ['product', 'quantity', 'price']
Total qty : 56
Avg price : 210.6Een klein programma, maar de structuur ervan is het opmerken waard — inlezen, dan controleren, dan berekenen. shape en head() zijn geen berekeningen, het zijn controlevragen: heb ik het juiste bestand ingelezen? Staan er zoveel rijen in als er zouden moeten zijn?
En er is een reden om list(sales.columns) te printen. Als lijst komt elke naam terug tussen aanhalingstekens, zodat een naam als 'quantity ' met een spatie aan het einde meteen zichtbaar is. Op elke andere manier geprint blijft die spatie onzichtbaar en levert later een KeyError op.
Wanneer het misgaat
FileNotFoundError: [Errno 2] No such file or directory: 'sales.csv' Python kijkt in de huidige map en heeft het bestand daar niet gevonden. Dit is hetzelfde probleem als in hoofdstuk twee van de Python-cursus: de terminal bevindt zich op de ene plek en het bestand op een andere. Voer pwd uit om te zien waar je bent, dir om te controleren of het bestand er staat, en gebruik vervolgens cd. Op Windows kan de echte naam sales.csv.txt zijn — schakel in Verkenner bestandsnaamextensies in.
Het hele bestand is binnengekomen als één enkele kolom Het scheidingsteken is geen komma. Bestanden die in grote delen van Europa zijn opgeslagen gebruiken vaak een puntkomma, omdat de komma daar het decimaalteken is. Schrijf pd.read_csv("sales.csv", sep=";"). Voor bestanden met tabs als scheidingsteken gebruik je sep="\t".
Een numerieke kolom is binnengekomen als object Er is ergens tekst in de kolom terechtgekomen — een N/A, een -, een lege cel of een scheidingsteken voor duizendtallen (1,200). Voer df["col"].unique() uit om te zien wat er daadwerkelijk in staat; de afwijkende waarde valt meestal meteen op. De oplossing is pd.to_numeric(df["col"], errors="coerce"), wat alles wat niet-numeriek is omzet in een ontbrekende waarde — uitgebreid behandeld in het hoofdstuk over ontbrekende data.
UnicodeDecodeError Het bestand is niet opgeslagen als UTF-8, wat regelmatig gebeurt bij bestanden die uit een spreadsheet komen. Probeer pd.read_csv("sales.csv", encoding="utf-8-sig"), en als dat niet werkt encoding="latin-1".
De eerste rij met data werd de kolomnamen, terwijl het bestand geen header had Schrijf pd.read_csv("sales.csv", header=None) en de kolommen worden 0, 1, 2. Om ze een naam te geven, voeg je names=["product", "quantity", "price"] toe.
Er wordt een verdwaalde None geprint na sales.info() Het was geschreven als print(sales.info()). info() print uit zichzelf en geeft niets terug, dus print print de None die het teruggaf. Schrijf gewoon sales.info().
Stap 4 van 6 — Voorspellen
Test je begrip
Het bestand heeft zes regels. Wat geeft shape aan?
from io import StringIO
import pandas as pd
# Stands in for sales.csv, so this snippet runs on its own.
RAW = """product,quantity,price
pen,12,15.0
notebook,5,60.0
bag,2,850.0
bottle,7,120.0
eraser,30,8.0
"""
sales = pd.read_csv(StringIO(RAW))
print(sales.shape)- A(5, 3)
- B(5, 4)
- C(3, 5)
- D(6, 3)
Wat vertelt de kolom Non-Null Count van info() je?
- AHoeveel cellen in elke kolom daadwerkelijk gevuld zijn — een getal onder het aantal rijen betekent dat er lege plekken zijn
- BHoeveel cellen in elke kolom leeg zijn
- CHoeveel ruimte elke kolom inneemt
- DHoeveel unieke waarden elke kolom heeft
Het bestand is gescheiden door puntkomma's, maar er is geen sep opgegeven. Wat wordt er geprint?
from io import StringIO
import pandas as pd
# Stands in for sales.csv, so this snippet runs on its own.
RAW = """product;quantity
pen;12
notebook;5
"""
df = pd.read_csv(StringIO(RAW))
print(df.shape)
print(list(df.columns))- A(3, 1) ['product;quantity']
- B(2, 1) ['product;quantity']
- C(2, 2) ['product', 'quantity']
- DEen `ParserError`
Voor antwoorden heb je een account nodig
Log in om je antwoorden te controleren
De vragen staan hierboven, en ze in je hoofd uitwerken is waar het om draait. Log in om de antwoorden, de uitleg en de hints in drie niveaus te zien.
Jouw beurt
Maak een bestand genaamd students.csv met daarin ten minste acht personen, verdeeld over drie kolommen — een naam, een leeftijd en een cijfer.
Schrijf vervolgens report.py, dat:
- Het bestand inleest
shapeenlist(columns)printhead(3)entail(2)printinfo()uitvoert en aangeeft of alle drie de kolomtypes overeenkomen met wat je verwachttedescribe()print, en afzonderlijk het gemiddelde en het maximum van de cijferkolom
Laat het daarna met opzet misgaan — drie keer, en let op wat er telkens gebeurt:
- Zet
N/Ain één cel van de cijferkolom. Wat zegtDtypeininfo()nu? Wat is er met het gemiddelde gebeurd? - Vervang elke komma in het bestand door een puntkomma. Wat zegt
shapenu, en waarom? - Verwijder de eerste regel, de header. Hoeveel rijen zijn er binnengekomen, en wat zijn nu de kolomnamen?
Alle drie komen regelmatig voor in de praktijk, en elk heeft één duidelijk symptoom. Door ze vandaag met opzet te veroorzaken, herken je ze de volgende keer direct.
Stap 6 van 6
Uitdaging — de hoofdstukquiz
Tien vragen van makkelijk naar moeilijk. De laatste zijn met opzet lastig.
Log in om de quiz te maken