fbpx
Big DataData SciencePoradniki

Warsztat Data Engineera – kiedy wybrać CSV, parquet, feather albo pickle?

Praca z danymi to codzienność w świecie Data. Niezależnie od tego, czy budujemy pipeline ETL, analizujemy dane w Jupyter notebooku, lub zapisujemy wyniki modelu ML – dane wyjściowe będą zapisane w jednym z kilku popularnych formatów. Chociaż zapis danych do pliku to tylko jedna linijka kodu, odpowiedni format wpływa na czytelność, rozmiar oraz czas zapisu i odczytu.

Żeby nie operować na abstrakcyjnych przykładach, załóżmy że pracujemy na konkretnym zbiorze danych – niech będzie nim DataFrame z 10 000 000 rekordów. Wygenerujemy go sami, aby symulował realistyczny plik. Warto zaznaczyć, że wszystkie czasy odczytu i zapisu podane w tym artykule mają charakter poglądowy – będą się one różnić w zależności od specyfikacji sprzętowej komputera.

import pandas as pd
import numpy as np

n = 10000000

df = pd.DataFrame({
    'id': np.arange(n),
    'wiek': np.random.randint(18, 80, size=n),
    'miasto': np.random.choice(['Warszawa', 'Kraków', 'Łódź', 'Siedlce'], size=n),
    'zarobki': np.random.normal(7000, 1500, size=n).round(2),
    'aktywny': np.random.choice([True, False], size=n)
})

Porównując wydajność zmierzymy czasy odczytu i zapisu, aby to zrobić wykorzystamy bibliotekę time. Dodamy linijkę będącą naszym startem mierzenia czasu, potem wykonamy operację odczytu/zapisu, a tuż po niej dodamy linijkę odczytującą czas po raz drugi. Dostaniemy wtedy informację w sekundach, np. dla zapisu pliku w CSV będzie to wyglądać tak:

import time

start = time.time()
df.to_csv('dane.csv', index=False)
print('CSV zapis: ', time.time() - start)

CSV – prostota kosztem wydajności

  • Czas zapisu: 9,2073 s
  • Czas odczytu: 2,5402 s
  • Rozmiar pliku na dysku: ~327 MB
# Odczyt
start = time.time()
df_csv = pd.read_csv('dane.csv')
print('CSV odczyt: ', time.time() - start)

Pliki CSV to prawdopodobnie pierwsze pliki z jakimi mamy do czynienia pracując z Pythonem i paczką Pandas. Jest to najprostszy format pliku, a jego główną zaletą jest możliwość otwarcia w każdym pakiecie biurowym jak MS Office czy OpenOffice. Dzięki temu dane zapisane w ten sposób mogą trafić bezpośrednio do mniej technicznych osób. Minusem jest wydajność – każdy z 3 wylistowanych powyżej parametrów wygląda lepiej u „konkurencji”.

Parquet – złoty standard Data Engineera

  • Czas zapisu: 1,7193 s
  • Czas odczytu: 0,6834 s
  • Rozmiar pliku na dysku: ~108 MB

Python

# Zapis
start = time.time()
df.to_parquet('dane.parquet', index=False)
print('Parquet zapis: ', time.time() - start)

# Odczyt
start = time.time()
df_parquet = pd.read_parquet('dane.parquet')
print('Parquet odczyt: ', time.time() - start)

Parquet to bardzo popularny wybór w projektach Data Engineeringowych i wszędzie tam, gdzie pracujemy na większych wolumenach danych. Jego ogromną zaletą jest wydajność zarówno przy zapisie, jak i przy odczycie. Nasz przykład to jeden całkiem spory DataFrame, ale przy pracy z wieloma plikami kilkukrotny uzysk na czasie potrafi zaoszczędzić godziny. Dodatkowo pliki parquet zajmują zdecydowanie mniej miejsca na dysku niż CSV ponieważ korzystają z kompresji i przechowują dane w formacie kolumnowym. Z kolei to przydaje się przy analizie danych, np. jeśli chcemy wykorzystać jedynie część kolumn. Minusem jest mniejsza przystępność, formatu parquet nie otworzy się korzystając z pakietu biurowego MS Office albo OpenOffice.

Feather – szybkość przede wszystkim

  • Czas zapisu: 0,7579 s
  • Czas odczytu: 0,3905 s
  • Rozmiar pliku na dysku: ~171 MB
# Zapis
start = time.time()
df.to_feather('dane.feather')
print('Feather zapis: ', time.time() - start)

# Odczyt
start = time.time()
df_feather = pd.read_feather('dane.feather')
print('Feather odczyt: ', time.time() - start)

Feather to format, który świetnie się sprawdza gdy zależy nam przede wszystkim na szybkości – przede wszystkim odczytu. Swój niski czas odczytu zawdzięcza temu, że jest oparty o Apache Arrow, czyli o kolumnowy format przechowywania danych zaprojektowany z myślą o bardzo szybkim dostępie do pamięci. W praktyce oznacza to, że dane są zapisane w sposób bardzo uporządkowany i bliski temu jak program i tak chce z nich skorzystać. W przeciwieństwie do np. CSV nie trzeba sparsować tekstu, rozpoznawać separatorów i odgadywać typów danych – plik można praktycznie od razu załadować do DataFrame’a. Minusem formatu Feather jest trochę większy rozmiar pliku. Często przegrywa przez to z parquetem, gdyż często czasy odczytu i zapisu są już w nim wystarczająco niskie. Niemniej, warto znać i ten format – na pewno przyda się w sytuacjach, gdy to szybkość ma największe znaczenie.

Pickle – wygoda kosztem uniwersalności

  • Czas zapisu: 2,3001 s
  • Czas odczytu: 1,0439 s
  • Rozmiar pliku na dysku: ~305 MB
# Zapis
start = time.time()
df.to_pickle('dane.pkl')
print('Pickle zapis: ', time.time() - start)

# Odczyt
start = time.time()
df_pickle = pd.read_pickle('dane.pkl')
print('Pickle odczyt: ', time.time() - start)

Pickle to format specyficzny dla Pythona – dzięki temu w prosty sposób można zapisać i odczytać dane korzystając z tego języka. W przypadku DataFrame’ów działa to całkiem sprawnie, co widać też w naszym porównaniu. Jednak Pickle działa tylko w Pythonie, a jeśli uwzględnić też duży rozmiar pliku .pkl, to jest wybierany bardzo rzadko. Mimo to, może się przydać w sytuacjach, gdy zapis DataFrame’a do formatów Parquet czy Feather wymaga czasochłonnej transformacji danych. Listy i słowniki w DataFrame’ach nie są zwykłymi typami tabelarycznymi i trzeba je najpierw ujednolicić. To może być problematyczne, wymaga przetestowania pod kątem znikających danych itp. – Pickle zapisze taki „trudny” DataFrame bez problemu.


Ostatecznie nie istnieje jeden „najlepszy” format zapisu danych. Jak widać, każdy z wymienionych ma swoje plusy i minusy. Sztuką jest zidentyfikować, czy w naszym projekcie na danym etapie potrzebna jest interakcja z nietechnicznym użytkownikiem, mały rozmiar pliku, a może szybki jego odczyt i zapis? CSV wygrywa uniwersalnością, Parquet i Feather – rozmiarem i szybkością. A przy pracy z bardziej skomplikowanymi obiektami Pythona warto pamiętać o Pickle.

Jeśli interesują cię zagadnienia związane z Pythonem, chciałbyś zagłębić temat wykorzystania różnych formatów plików i poznać różne technologie związane z pracą Data Engineera, koniecznie sprawdź nasz kurs Data Engineera na infoShare Academy!

Artur Dzięcioł [in]

Data Engineer @ CBRE | Python | PySpark | AWS | PostgreSQL | Terraform | CI/CD | Docker | Tableau

Jestem Data Engineerem z ponad 7-letnim doświadczeniem. Przeszedłem ścieżkę od analityka i BI Developera (Excel, Tableau) do obecnej roli opartej na Pythonie, SQL i AWS. Doświadczenie w pracy z biznesem pozwala mi patrzeć na dane kompleksowo. Obecnie skupiam się na backendzie, a pasję do nauki realizuję na bootcampach, gdzie wyjaśniam skomplikowane technologie prostymi słowami.

Back to top button