Zpět na domů

Parquet v Pythonu: čtení, zápis a optimalizace

Průvodce praktickým použitím Parquet v Pythonu pro data inženýry. Ukázáno, jak efektivně číst s filtrováním, psát s explicitní schémou a dictionary encoding, nastavovat row group a compression, a také aplikovat partitionování pro velké datové sady.

Parquet v production: od pandas do pyarrow
Advertisement 728x90

Parquet v produkci: jak číst, zapisovat a optimalizovat data pro analytiku

Parquet není jen formát ukládání, ale nástroj architektonické optimalizace. Jeho kolonková struktura, vestavěné filtrování při čtení, slovníkové kódování a metadata řádkových skupin umožňují snížit I/O desítkykrát a objem dat až o 140× ve srovnání s CSV. Pro data-inženýry a analytiky to znamená nejen úsporu disku, ale i snížení latence při interaktivních dotazech, menší zátěž paměti a předvídatelnost výkonu. V tomto článku najdete praktické doporučení pro práci s Parquetem v Pythonu na úrovni middle/senior: od základního čtení pomocí pandas až po ruční řízení schématu, partitionování a kompresi.

Proč Parquet nahrazuje CSV v moderních data-pipelinech

CSV je textový formát bez semantiky: každý řádek se zpracovává jako jednotný blok, oddělovače se parsují za letu, typy se určují heuristicky. Parquet je naopak binární, striktně typizovaný, kolonkový formát s vestavěnou metainformací. Klíčové rozdíly jsou:

  • Kolonkové ukládání: každá kolona se ukládá samostatně, což umožňuje číst pouze potřebná pole (například year a indicator_value) a ignorovat ostatní – bez kompletního rozbory řádku.
  • Row groups: data jsou rozdělena do bloků (ve výchozím nastavení ~128 MB), každý z nich obsahuje statistiku (min, max, null_count). Při filtraci year == 2023 engine přeskočí celé row groups, aniž by je načetl do paměti.
  • Dictionary encoding: u řetězcových kolon s vysokou opakovatelností (regiony, kategorie) Parquet implicitně nebo explicitně ukládá slovník unikátních hodnot a odkazy na ně – místo „Moskevská oblast“ × 500 tis. záznamů je to kód 1 × 500 tis.
  • Typová bezpečnost: v CSV se NULL v numerickém poli promění celou kolonu na float64, zatímco v Parquetu jsou typy pevně stanoveny ve schématu a kontrolují se při zápisu.

Rozdíl v praxi je kolosální: dataset o objemu 576 MB s nemocností rakoviny se v CSV stáhne na 4 MB v Parquetu (soubor se zmenší o 144×), a čas načtení vyfiltrovaného podmnožiny se sníží z 12–18 sekund na <300 ms.

Google AdInline article slot

Čtení: od pandas až po fine-grained control s pyarrow

Základní čtení prostřednictvím pandas

Pro rychlý start stačí pd.read_parquet():

import pandas as pd

df = pd.read_parquet(
    "data_zis_109_v20260126.parquet",
    columns=["object_name", "year", "indicator_value"],
    filters=[("year", "=", 2023)]
)

Přímý přístup přes pyarrow: kontrola nad schématem a metadata

Když je potřeba přesné řízení, použijte pyarrow.parquet přímo:

import pyarrow.parquet as pq

# Bez načtení dat – pouze schéma a metadata
schema = pq.read_schema("data_zis_109_v20260126.parquet")
meta = pq.read_metadata("data_zis_109_v20260126.parquet")
print(f"Řádků: {meta.num_rows:,}, Kolon: {meta.num_columns}")

# Čtení s filtrem a výběrem kolon
table = pq.read_table(
    "data_zis_109_v20260126.parquet",
    columns=["object_name", "object_oktmo", "year", "indicator_value"],
    filters=[("year", "=", 2023)]
)

df = table.to_pandas()  # konverze do pandas, pokud je potřeba
pf = pq.ParquetFile("data_zis_109_v20260126.parquet")
for batch in pf.iter_batches(batch_size=100_000):
    df_batch = batch.to_pandas()
    # zpracování porce

Zápis: od jednoduchého to_parquet() až po production-grade serializaci

Problémy automatické serializace

df.to_parquet() funguje, ale skrývá rizika:

Google AdInline article slot
  • Neviditelné určování typů: int64 s NULL → float64, datetime64string.
  • Chybějící dictionary encoding u řetězcových kolon s vysokou kardinálností.
  • Nevhodný rozměr row group (ve výchozím nastavení 64 MB), což snižuje efektivitu filtrace.

Explicitní schéma a kontrolované kódování

import pyarrow as pa
import pyarrow.parquet as pq

schema = pa.schema([
    pa.field("object_name", pa.string()),
    pa.field("object_oktmo", pa.dictionary(pa.int32(), pa.string())),
    pa.field("year", pa.int32()),
    pa.field("indicator_value", pa.float64())
])

table = pa.Table.from_pandas(df, schema=schema, preserve_index=False)

pq.write_table(
    table,
    "data_zis_new_version.parquet",
    compression="zstd",
    row_group_size=500_000,
    version="2.6",
    write_statistics=True
)

Proč zstd? Kompresi o 30–50% lepší než snappy při srovnatelné rychlosti dekompresie. Pro analytické pracovní zátěže to je optimální balance.

Partitionování a práce se soubory

Pokud jsou data logicky rozdělena (podle let, regionů), použijte write_to_dataset():

pq.write_to_dataset(
    table,
    root_path="data_zis_partitioned/",
    partition_cols=["year"]
)

Čtení pak bude efektivní:

Google AdInline article slot
import pyarrow.dataset as ds
dataset = ds.dataset("data_zis_partitioned/", format="parquet", partitioning="hive")

table = dataset.to_table(filter=ds.field("year") == 2023)

Partitionování je vhodné při objemu >1 GB a kardinálnosti partitionovacího klíče <1000 hodnot. Vyhněte se partitionování podle high-cardinality polí (například indicator_value) – to vytváří tisíce malých souborů a ničí výkon.

Co je důležité

  • Parquet nenahrazuje databázi: je optimální pro append-only, read-heavy analytické scénáře, ale nepodporuje transakce, UPDATE ani indexy.
  • Dictionary encoding dává maximální výhodu při kardinálnosti <10⁴ a podílu opakování >30%. U kolon s unikátními hodnotami (UUID, timestamps) je zbytečný.
  • Velikost row group ovlivňuje přesnost filtrace: příliš malá – spousta servisních metadat; příliš velká – přeskočení nepotřebných dat je neefektivní. Optimální je 100–500 tis. řádků.
  • Statistika (write_statistics=True) je nutná pro produkci. Bez ní filtrace funguje jako full scan.
  • Vždy kontrolujte schéma před zápisem: pa.Table.from_pandas(df, schema=schema) vyhodí chybu při nesouladu typů – to chrání před tichou korupcí.

— Editorial Team

Advertisement 728x90

Číst dál