werken met groot csv bestand in python voorbeeld tutorial

Hoe werk ik met een groot CSV-bestand in Python?

Wanneer je data uit een systeem exporteert kan het voorkomen dat je daarbij geen mogelijkheden hebt om filters op te geven, waardoor je alle data uit het systeem exporteert. En dit kan leiden tot een erg groot bestand. Het kan lastig zijn hiermee te werken.

Denk bijvoorbeeld aan de situatie dat je als data analist een export maakt van medische gegevens uit een ziekenhuissysteem, naar een CSV-bestand. Je verkrijgt daarbij alle historische data, waardoor het CSV-bestand erg groot is. Hierdoor duurt het erg lang om het te openen. Wat lastig kan zijn als je analyses uit moet voeren.

In deze blog bekijken welke verschillende mogelijkheden er zijn om alsnog met een groot CSV-bestand te kunnen werken, vanuit programmeertaal Python, met package pandas.

Deze onderwerpen zullen we behandelen:

  1. Bekijk eerst of het toch niet anders kan
  2. Werk met actuele versies van Python en packages
  3. Voorbeeld van groot CSV-bestand in Python (tutorial)
  4. Om te onthouden

1. Bekijk eerst of het toch niet anders kan

Probeer altijd de situatie te voorkomen dat je met een dataset gaat werken die onnodig groot is, en gegevens bevat die je niet gaat gebruiken. Denk bijvoorbeeld aan een export van medische gegevens uit een ziekenhuissysteem, waarbij je een export krijgt met data van alle ooit geregistreerde patiënten, terwijl je voor je analyses alleen data van patiënten nodig hebt die het afgelopen jaar behandeld zijn. Probeer in zo'n geval te onderzoeken of je niet alsnog een dataset kunt verkrijgen die alleen de data bevat die je echt nodig hebt. Bijvoorbeeld via een gerichte SQL-query op de onderliggende database, in plaats van het handmatig maken van een export vanuit de applicatie. Hierdoor kun je werken met een compactere dataset die sneller uit te lezen zal zijn.

2. Werk met actuele versies van Python en packages

Mocht er geen alternatief zijn om aan een compactere dataset te kunnen komen, zorg er dan sowieso voor dat je met een actuele versie van Python en de packages die je gebruikt werkt. Nieuwere versies van Python en bijbehorende packages zijn veelal verder geoptimaliseerd op uitvoersnelheid. Een voorbeeld hiervan is dat bij het werken met datasets package pandas versie 2.+ door diverse verbeteringen datasets sneller kan uitlezen.

3. Voorbeeld van groot CSV-bestand

In deze blog gaan we met een voorbeeld CSV-bestand aan de slag met hierin dummy data, in 40.000 rijen en meer dan 2.000 kolommen: ongeveer 650 MB aan data. Achtereenvolgens bekijken we verschillende manieren om de dataset te kunnen openen, om een zo snel mogelijk en goed werkbare manier te vinden.

In deze blog gebruiken we de volgende modules en packages:

  • pandas: voor het werken met data in tabelvorm.
  • os: voor het bepalen van de grootte van een bestand.
  • time en timeit: voor het meten van de utivoertijd van code.

3.1. Importeren van packages en definiëren van constanten

Allereerst importeren we in Python de te gebruiken packages:

import sys
import os
import time
import timeit
import pandas as pd

print("Python:", sys.version)
print("pandas:", pd.__version__)

Output:

Python: 3.8.9 (tags/v3.8.9:a743f81, Apr  2 2021, 11:10:41) [MSC v.1928 64 bit (AMD64)]
pandas: 1.3.3

Vervolgens stellen we de referentie in naar het bestandspad van de CSV waarmee we gaan werken:

FILEPATH_CSV = "data/source_large_csv.csv"
FILEPATH_PARQUET = "data/source_large_csv.parquet"

Dit hergebruiken we later. Dan volgt ook meer informatie over het .parquet formaat.

3.2. Uitlezen van het CSV-bestand

Met onderstaande code bekijken we nogmaals eerst de bestandsgrootte van de CSV:

file_size_in_bytes = os.path.getsize(FILEPATH_CSV)

print(f"{file_size_in_bytes / (1024 * 1024):.2f} MB")

Output:

642.83 MB

Het bestand is dus inderdaad ongeveer 650 MB groot.

We kunnen het met package pandas uitlezen, door gebruik te maken van de read_csv() functie:

df = pd.read_csv(FILEPATH_CSV, sep=";")

print(df.shape)
df

Output:

(40000, 2106)
datecountnamecompany_nameamountdetailsdate_1count_1name_1company_name_1...
017/12/202248CelineBluepine Analytics797Open issues21/09/201658EmmaBluepine Analytics...
107/11/2021599BramBluepine Analytics1369901/09/2005160CelineBluepine Analytics...
202/09/2008213CelineZentico Labs308TBD29/12/2013429DavidVeltrix Innovations...
308/08/2019830BramBluepine Analytics399919/12/2016992BramVeltrix Innovations...
416/01/2019672DavidBluepine Analytics786Fixed price20/03/2010752AnnaNexora Solutions...
5.................................

We hebben de dataset uitgelezen naar een pandas DataFrame, en zien dat het bestaat uit 40.000 rijen en 2.106 kolommen.

Afhankelijk van onder andere hoeveel geheugen je gebruikt en wat voor processor je hebt kan de tijd die het kost variëren.

3.2. Meten van de uitleestijd

Om te kunnen zien wat het effect gaat zijn van mogelijke verbeteringen (om de dataset sneller te kunnen openen) gaan we meten hoe lang het duurt om het CSV-bestand uit te lezen.

3.2.1. Uitleestijd meten met time

Met module time kunnen we vanuit code de huidige tijd verkrijgen. Als we dit voor en na het uitlezen van de dataset doen, kunnen we de tussenliggende tijd berekenen. Dit doen we met onderstaande code.

start = time.time()

df = pd.read_csv(FILEPATH_CSV, sep=";")

end = time.time()
print(f"Duration: {end - start:.4f} seconds")

Output:

Duration: 35.8615 seconds

We zien hiermee hoe lang het duurde om het CSV bestand uit te lezen naar een pandas DataFrame.

3.2.2. Uitleestijd meten met timeit

Afhankelijk van wat er op je machine (bijvoorbeeld laptop) gebeurt, kan het zijn dat er meer of minder geheugen en/of rekenkracht beschikbaar is. Daardoor kan er van moment tot moment verschil in zitten hoe lang het duurt om dezelfde code uit te voeren.

Om betrouwbaarder te weten hoe lang de uitvoertijd werkelijk duurt, kun je je code herhaaldelijk uitvoeren, en daarvoor de totale tijd berekenen. Dit kan gemakkelijk met package timeit. Daarbij moet je de de uit te voeren code omschrijven naar een functie. Dit doen we met onderstaande code. Daarin voeren we de code 5 keer uit, en berekenen de totale tijd.

def read_csv() -> pd.DataFrame:
    df = pd.read_csv(FILEPATH_CSV, sep=";")
    return df

NUMBER_OF_REPETITIONS = 5
duration = timeit.timeit(read_csv, number=NUMBER_OF_REPETITIONS)
print(f"Duration: {duration:.6f} seconds for {NUMBER_OF_REPETITIONS} repetitions")

Output:

Duration: 159.599420 seconds for 5 repetitions

3.3. Uitleestijd verkorten

We gaan diverse voorbeelden bekijken om de uitvoertijd te verkorten.

3.3.1. Gebruik van een ander engine

Wanneer je de read_csv() functie gebruikt kun je argument engine opgeven. Hierbij heb je, afhankelijk van de pandas versie waarmee je werkt, verschillende opties waaronder:

  • c
  • python
  • pyarrow (let op: niet beschikbaar in oudere versies)

Je past dit bijvoorbeeld als volgt toe:

df = pd.read_csv(FILEPATH_CSV, sep=";", engine="pyarrow")

3.3.2. Stapsgewijs uitlezen en vooraf bepalen van datatypes

Bij het uitlezen van een CSV naar een pandas DataFrame worden er datatypes voor de kolommen bepaald. Dit kan langer duren bij een dataset met veel rijen en kolommen.

Een mogelijke oplossing hiervoor is om achtereenvolgens:

  1. Eerst slechts een beperkt aantal rijen uitlezen
  2. Van de kolommen hieruit de datatypes bepalen
  3. De gehele dataset uit te lezen, waarbij je de datatypes van kolommen al opgeeft

Dit doen we met onderstaande code, waarbij we eerst met nrows het aantal uit te lezen rijen limiteren. Vervolgens halen we met df.dtypes.to_dict() de datatypes van de kolommen op, en geven dit bij het uitlezen van de gehele dataset op met argument dtypes.

def read_csv() -> pd.DataFrame:
    df = pd.read_csv(FILEPATH_CSV, sep=";", nrows=100)
    column_date_types = df.dtypes.to_dict()

    df = pd.read_csv(FILEPATH_CSV, sep=";", dtype=column_date_types)
    return df

NUMBER_OF_REPETITIONS = 5
duration = timeit.timeit(read_csv, number=NUMBER_OF_REPETITIONS)
print(f"Duration: {duration:.6f} seconds for {NUMBER_OF_REPETITIONS} repetitions")

Output:

Duration: 137.533957 seconds for 5 repetitions

Dit laat een kleine verbetering in uitvoertijd zien.

3.3.3. Vooraf opslaan van dataset in Parquet-formaat

Er zijn bestandsformaten die compacter zijn en sneller leesbaar zijn dan het CSV-formaat. Een voorbeeld daarvan is het Parquet-formaat. We kunnen hier als volgt gebruik van maken:

  1. Eenmalig uitlezen van de CSV naar pandas DataFrame met functie read_csv()
  2. Omzetten van het DataFrame naar Parquet-formaat met de to_parquet() methode
  3. Vervolgens voor hergebruik de het bestand in Parquet-formaat uitlezen met de read_parquet() functie

Dit gaan we stap voor stap doen. Het is vooral handig en tijdbesparend wanneer je verwacht de dataset herhaaldelijk te zullen openen. Met onderstaande code lezen we de CSV wederom uit, en slaan het op in Parquet-formaat:

df = pd.read_csv(FILEPATH_CSV, sep=";")
df.to_parquet(FILEPATH_PARQUET)

Als we nu de bestandsgrootte bekijken:

file_size_in_bytes = os.path.getsize(FILEPATH_PARQUET)

print(f"{file_size_in_bytes / (1024 * 1024):.2f} MB")

Output:

4.80 MB

Dan zien we dat dit meer dan 100 keer kleiner is dan de oorspronkelijke 650 mb. Dit laat direct zien dat het Parquet-formaat tot veel kleinere bestandsgroottes kan leiden. Een nadeel bij het gebruik van Parquet kan zijn dat het niet gemakkelijk met bijvoorbeeld Excel te openen is.

Wanneer we nu ook de uitvoersnelheid voor het uitlezen van de dataset in Parquet-formaat bekijken:

def read_parquet() -> pd.DataFrame:
    df = pd.read_parquet(FILEPATH_PARQUET)
    return df

NUMBER_OF_REPETITIONS = 5
duration = timeit.timeit(read_parquet, number=NUMBER_OF_REPETITIONS)
print(f"Duration: {duration:.6f} seconds for {NUMBER_OF_REPETITIONS} repetitions")

Output:

Duration: 29.498657 seconds for 5 repetitions

Zien we dat dit aanzienlijk sneller is dan de initiële uitleestijd, meer dan 5 keer zo snel.

4. Om te onthouden

Het kan voorkomen dat je met een grote dataset vanuit bijvoorbeeld een CSV-bestand moet werken. Kijk dan altijd of je alle data uit de dataset voor jouw toepassing wel nodig hebt, en of je geen compactere dataset kunt verkrijgen waarmee je makkelijker kunt werken. Dit zodat de uitleestijd korter gaat zijn. Zorg er ook voor dat je recente versies van Python en packages gebruikt. Deze zijn verder geoptimaliseerd voor uitvoersnelheid.

Als je niet aan een compactere dataset kunt komen, zijn er opties om met package pandas alsnog de uitvoersnelheid te verkorten. Experimenteer bijvoorbeeld met een andere engine, lees de dataset in stappen uit, en/of maak gebruik van een ander bestandsformaat, zoals Parquet. In dit voorbeeld hebben we gezien dat de uitleessnelheid daarmee met een factor 5 verkort kon worden.

Wil je op professioneel niveau met Python leren werken?

Schrijf je dan in voor onze Python cursus voor data science, Python advanced training, onze machine learning cursus, of voor onze data science opleiding en leer met vertrouwen te programmeren en visualiseren in Python. Nadat je een van onze trainingen hebt gevolgd kun je zelfstandig verder aan de slag. Je kunt ook altijd even contact opnemen als je een vraag hebt.

Download één van onze opleidingsbrochures voor meer informatie

by: