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
yearaindicator_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 filtraciyear == 2023engine 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
NULLv numerickém poli promění celou kolonu nafloat64, 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.
Č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:
- Neviditelné určování typů:
int64s NULL →float64,datetime64→string. - 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í:
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
Zatím žádné komentáře.