Перейти к основному содержимому

Parquet и ORC

Разработчику Аналитику Архитектору Инженеру

Parquet и ORC

Колоночное хранение

Обычный CSV и строка в SQL хранят данные по строкам — сначала вся первая запись, потом вторая. Колоночный формат хранит значения по столбцам — все значения колонки region подряд, потом все revenue.

Это ускоряет типичную аналитику, когда из таблицы с тремястами столбцов нужны три:

  • чтение с диска берёт только нужные колонки
  • однотипные значения лучше сжимаются
  • агрегации (SUM, GROUP BY) работают быстрее на больших объёмах

Apache Parquet и Apache ORC (Optimized Row Columnar) — бинарные колоночные форматы. Файл нельзя прочитать в блокноте; нужны Spark, Pandas с pyarrow, DuckDB, Hive и т.п.

Внутри файла лежит схема — имена и типы столбцов. Отдельный файл-словарь для импорта не нужен, как иногда бывает с CSV.

Применение:

  • Data Lake и DWH (хранилища больших данных)
  • пайплайны ETL/ELT
  • Spark, Hive, ClickHouse, DuckDB, BigQuery

Обзор масштаба — Big Data. Компактный обмен в API — MessagePack, Protobuf.


Строки и столбцы на диске

Представьте таблицу из трёх строк и четырёх столбцов:

idregionrevenuedate
1Москва10002025-06-01
2Казань8002025-06-02
3Москва12002025-06-03

Row-oriented (строковое хранение, как CSV) записывает на диск целые строки подряд:

1,Москва,1000,2025-06-01 | 2,Казань,800,2025-06-02 | 3,Москва,1200,2025-06-03

Запрос SELECT SUM(revenue) GROUP BY region всё равно читает все столбцы каждой строки — лишние id и date попадают в память.

Column-oriented (колоночное хранение, Parquet/ORC) группирует значения по столбцам:

id: [1, 2, 3]
region: [Москва, Казань, Москва]
revenue: [1000, 800, 1200]
date: [2025-06-01, 2025-06-02, 2025-06-03]

Движок читает только region и revenue — остальное не трогает.

На практике внутри Parquet данные ещё разбиты на row groups (горизонтальные полоски строк), но внутри каждой группы значения одного столбца лежат рядом — отсюда выигрыш по сжатию и по I/O.


Parquet

Parquet — самый частый выбор в облачной аналитике и в стеке Python (Pandas, Polars, PyArrow).

Устройство файла (упрощённо):

  • файл разбит на row groups — горизонтальные блоки строк (обычно 128–256 МБ несжатых данных)
  • внутри row group — column chunks со сжатием Snappy, ZSTD или GZIP
  • метаданные и схема — в конце файла (footer)
  • можно читать только выбранные столбцы, не весь файл

Запись и чтение в Python:

import pandas as pd

df = pd.read_csv("sales.csv", encoding="utf-8-sig", sep=";")
df.to_parquet("sales.parquet", engine="pyarrow", compression="snappy")

df2 = pd.read_parquet("sales.parquet", columns=["region", "revenue"])

DuckDB выполняет SQL прямо по файлу на диске:

SELECT region, SUM(revenue) AS total
FROM 'sales.parquet'
GROUP BY region;

Сравнение с pandas и SQL — напоминалка Pandas / Polars / SQL.


Parquet-файл устроен так, что схема и статистика лежат в конце (footer), а не в начале, как в CSV с заголовком.

[ Row Group 0: column chunks ... ]
[ Row Group 1: column chunks ... ]
[ Row Group N: column chunks ... ]
[ Footer: schema, row group offsets, column statistics ]
[ 4 bytes: длина footer ]
[ 4 bytes: magic "PAR1" ]

Footer в конце файла. При записи потоком движок не знает заранее, сколько row groups получится. Он пишет данные, потом дописывает метаданные и длину footer. При чтении клиент сначала читает хвост файла (последние 8 байт), узнаёт длину footer, загружает схему и список row groups — и только потом обращается к нужным кускам.

В footer для каждого column chunk часто хранятся:

  • минимум и максимум значений (для чисел и дат)
  • количество NULL
  • кодировка (plain, dictionary, RLE)

Эта статистика питает predicate pushdown — см. ниже.

Просмотреть метаданные без Python:

parquet-tools meta sales.parquet
parquet-tools schema sales.parquet

Утилита parquet-tools (Java) или parquet-cli — стандартный способ заглянуть внутрь бинарного файла. В экосистеме Arrow есть также parquet-dump-arrow и встроенные команды в DuckDB (DESCRIBE SELECT * FROM 'file.parquet').


Кодеки сжатия

Сжатие в Parquet работает на уровне column chunk, а не всего файла целиком. Один и т же файл может теоретически использовать разные кодеки для разных столбцов, хотя на практике чаще один кодек на весь файл.

КодекСкорость записиСкорость чтенияСтепень сжатияТипичное применение
Snappyочень быстробыстросредняядефолт в Spark и многих пайплайнах
ZSTDбыстробыстровысокаяcold storage, архивы, когда диск дороже CPU
GZIPмедленнеемедленнеевысокаясовместимость со старыми системами
LZ4очень быстроочень быстрониже Snappyrealtime ingest, когда latency важнее места
Brotliмедленносреднеочень высокаяредко в Parquet; чаще в веб
uncompressedнетотладка, уже сжатые payload (JPEG внутри binary)

Выбор кодека — компромисс место на диске ↔ CPU при чтении/записи:

# Pandas + PyArrow
df.to_parquet("sales_snappy.parquet", compression="snappy")
df.to_parquet("sales_zstd.parquet", compression="zstd")
df.to_parquet("sales_gzip.parquet", compression="gzip")
# PySpark при записи
df.write.parquet("output/", compression="snappy")
df.write.option("compression", "zstd").parquet("output_zstd/")

Snappy почти не сжимает текстовые столбцы с уникальными UUID, но отлично работает на повторяющихся категориях (region, status). ZSTD часто даёт на 20–40% меньший файл при сопоставимой скорости на современных CPU — его всё чаще ставят дефолтом в новых lakehouse-платформах.

Parquet также применяет кодирование значений до сжатия:

  • Dictionary encoding — если в столбце мало уникальных строк, хранят словарь и индексы
  • RLE / bit-packing — для повторяющихся чисел и boolean

Именно сочетание однотипных значений подряд, dictionary encoding и Snappy/ZSTD даёт файлы в 5–20 раз меньше CSV на реальных датасетах.


Apache Arrow и Parquet

Apache Arrow — не формат файла на диске, а in-memory представление табличных данных (columnar format в RAM). Parquet — формат на диске. Они дополняют друг друга:

CSV / JSON --read--> Arrow Table (RAM) --write--> Parquet
Parquet --read--> Arrow Table (RAM) --compute--> результат

PyArrow, Polars, DuckDB, Pandas 2.x (через PyArrow backend) используют Arrow внутри:

import pyarrow.parquet as pq

table = pq.read_table("sales.parquet", columns=["region", "revenue"])
# table — pyarrow.Table, колонки в памяти как Arrow arrays

df = table.to_pandas() # zero-copy или почти zero-copy
import polars as pl

lf = pl.scan_parquet("sales/*.parquet") # lazy, читает только нужное
result = lf.filter(pl.col("year") == 2026).group_by("region").agg(pl.col("revenue").sum())

Arrow задаёт единые типы (int64, utf8, timestamp[us]) между Python, R, Spark и Rust. Parquet хранит схему, совместимую с Arrow — поэтому конвертация Parquet → Arrow быстрая.

Flight и Arrow IPC — протоколы передачи Arrow-таблиц по сети между процессами; Parquet остаётся форматом долговременного хранения в S3/HDFS.

Подробнее о типах в памяти — структуры данных. Оценка объёма — пакетная работа.


Schema evolution (эволюция схемы)

В живом lake датасет меняется со временем: добавили столбец campaign_id, переименовали revrevenue, поменяли int32 на int64.

Parquet поддерживает эволюцию схемы на уровне чтения, если соблюдать правила:

ИзменениеОбычно безопасноРиск
Добавить новый столбецда — старые файлы без столбца дают NULLнужна единая логика в ETL
Удалить столбецда при чтении — столбец игнорируетсястарые отчёты сломаются
Переименовать столбецнет — для Parquet это удаление + добавлениенужна миграция или view в SQL
Сузить тип (int64int32)нетошибка или потеря данных
Расширить тип (int32int64)часто да через promotionпроверьте движок

В Spark явная схема при записи предотвращает плавающие типы:

from pyspark.sql.types import StructType, StructField, StringType, DoubleType, IntegerType

schema = StructType([
StructField("region", StringType(), False),
StructField("revenue", DoubleType(), True),
StructField("year", IntegerType(), False),
])

df.write.schema(schema).mode("append").parquet("sales/")

При append новых файлов в ту же папку Spark/Hive/Delta проверяют совместимость схем. Для строгого контроля используют Delta Lake, Iceberg или Hudi — они добавляют transaction log поверх Parquet; базовая идея эволюции та же.

mergeSchema в Spark (осторожно в проде):

df.write.option("mergeSchema", "true").mode("append").parquet("sales/")

Практика для bronze/silver слоёв — не переименовывать столбцы в сырых файлах; переименование делать в SQL-трансформации silver.


Predicate pushdown

Predicate pushdown (проталкивание фильтра) — оптимизация, когда условие WHERE revenue > 1000 не читает row groups и column chunks, где по статистике footer точно нет подходящих значений.

-- DuckDB / Spark SQL
SELECT region, SUM(revenue)
FROM 'sales.parquet'
WHERE year = 2026 AND revenue > 500
GROUP BY region;

Цепочка:

  1. Читатель открывает footer, видит min/max year и revenue по каждому row group
  2. Row group, где max(year) = 2025, пропускается целиком
  3. Внутри оставшихся groups читаются только столбцы region, revenue, year
  4. Фильтр применяется к декодированным значениям

Pushdown не заменяет partition pruning по папкам — они работают вместе. Pushdown отсекает внутри файла, pruning — целые файлы по пути year=2026/.

Ограничения:

  • если статистика устарела или отключена при записи, pushdown слабее
  • фильтр по LIKE '%ма' не использует min/max
  • вложенные типы (struct) поддерживаются не во всех движках одинаково
# PyArrow — filters при чтении
import pyarrow.parquet as pq

table = pq.read_table(
"sales.parquet",
columns=["region", "revenue", "year"],
filters=[("year", "=", 2026), ("revenue", ">", 500)],
)

Spark — чтение и запись

Apache Spark — типичный движок для batch-обработки Parquet в кластере.

Чтение:

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("parquet-demo").getOrCreate()

sales = spark.read.parquet("s3://datalake/sales/")
# или локально
sales = spark.read.parquet("/data/sales/year=2026/")

sales.printSchema()
sales.filter("revenue > 1000").groupBy("region").sum("revenue").show()

Запись:

(
sales
.repartition(4) # 4 выходных файла (осторожно с мелкими файлами)
.write
.mode("overwrite")
.partitionBy("year", "month")
.parquet("/data/sales_curated/")
)

Параметры, которые стоит знать:

ПараметрСмысл
spark.sql.parquet.compression.codecsnappy, gzip, zstd
spark.sql.parquet.mergeSchemaобъединять схемы при чтении нескольких путей
maxRecordsPerFileограничить строк в одном файле
coalesce(n) / repartition(n)число файлов на выходе

Мелкие файлы (тысячи файлов по 100 КБ) убивают производительность listing в S3 и планирование задач. Целевой размер одного Parquet-файла — 128 МБ – 1 ГБ несжатых или эквивалент после сжатия.

# compact после ingest
spark.read.parquet("bronze/events/").coalesce(50).write.mode("overwrite").parquet("bronze/events_compact/")

Spark SQL напрямую:

CREATE TABLE sales USING PARQUET LOCATION 's3://datalake/sales/';
SELECT region, SUM(revenue) FROM sales WHERE year = 2026 GROUP BY region;

Связь с ETL/ELT и оркестрацией — Airflow, Dagster, dbt поверх тех же путей.


Партиции каталога и partition pruning

В Data Lake один логический датасет — папка с множеством файлов:

sales/
year=2025/month=06/part-00001.parquet
year=2025/month=06/part-00002.parquet
year=2026/month=01/part-00001.parquet
year=2026/month=02/part-00001.parquet

Hive-style partitioning — ключи year=2026 в имени папки, не обязательно дублируются как столбцы в каждой строке (хотя Spark часто добавляет их как virtual columns).

Partition pruning — движок смотрит на путь и не открывает файлы из year=2025/, если в запросе WHERE year = 2026.

-- DuckDB
SELECT SUM(revenue) FROM read_parquet('sales/year=2026/*/*.parquet');

-- Spark
SELECT SUM(revenue) FROM sales WHERE year = 2026 AND month = 2;

Глубокий разбор:

1. Pruning по пути vs фильтр по столбцу

Если year есть только в пути, а в файле столбца year нет — pruning всё равно работает в Spark/Hive. Если year продублирован в данных, фильтр по столбцу + pruning дают двойную защиту.

2. Слишком много мелких партиций

Партиция day=2026-06-14 с одним файлом 50 КБ × 365 дней = сотни мелких объектов в S3. Лучше партиционировать по month или year, а день фильтровать pushdown внутри файла.

3. Partition columns в SELECT

# Spark — не забыть partition columns в схеме таблицы
spark.sql("""
CREATE TABLE sales (
region STRING,
revenue DOUBLE
)
USING PARQUET
PARTITIONED BY (year INT, month INT)
LOCATION '/data/sales/'
""")

4. Dynamic partition overwrite

spark.conf.set("spark.sql.sources.partitionOverwriteMode", "dynamic")
df.write.mode("overwrite").insertInto("sales") # перезапишет только затронутые year/month

5. DuckDB hive partitioning

import duckdb
duckdb.sql("""
SELECT year, month, SUM(revenue)
FROM read_parquet('sales/*/*/*.parquet', hive_partitioning=true)
WHERE year >= 2026
GROUP BY 1, 2
""")

Слои bronze / silver / gold — см. следующий раздел и Data Warehouse, Lake, Mesh.


Bronze, silver, gold в lake

Медальонная архитектура (medallion) — соглашение об уровнях качества данных в одном lake:

СлойСодержимоеФорматПартиции
Bronzeсырые события, как пришли из API/Kafka/CSVParquet, иногда JSON linesпо дате ingest
Silverочистка типов, дедуп, join справочниковParquetпо business date
Goldвитрины для BI, агрегатыParquet или таблицы DWHпо домену
s3://company-lake/
bronze/events/dt=2026-06-14/part-00001.parquet
silver/orders/date=2026-06-14/part-00001.parquet
gold/daily_revenue_by_region/snapshot=2026-06-14/part-00001.parquet

Bronze — append-only, схема может плыть; mergeSchema или отдельные папки по версии API.

Silver — единая схема, NULL обработаны, ключи уникальны; здесь очистка в Pandas или Spark.

Gold — то, что читает Tableau / Metabase / внутренний дашборд; часто денормализованные широкие таблицы.

Parquet на всех трёх слоях — стандарт de facto: одни и те же инструменты (Spark, DuckDB, Trino), одно сжатие, pruning по дате.


ORC

ORC — родственный колоночный формат. Исторически сильнее в экосистеме Apache Hive и части on-prem Hadoop-кластеров. Тоже сжимает данные и хранит схему внутри.

Структура ORC (упрощённо):

[ Stripe 0: index + data streams по столбцам ]
[ Stripe 1: ... ]
[ Footer: schema, statistics, stripe locations ]
[ Postscript + footer length ]

Stripe в ORC ≈ row group в Parquet. ORC изначально проектировали под Hive ACID-транзакции и vectorized чтение в Tez/MR.

Расширенное сравнение:

КритерийParquetORC
Типичный стекSpark, Arrow, облако, DatabricksHive, Hadoop on-prem, часть Cloudera
Поддержка в облакеS3, GCS, ADLS — nativeесть, но реже default
Вложенные типыstruct, list, map — нативно в Arrowstruct, list, map — типы Hive
ACID upsertчерез Delta/Iceberg/Hudi (Parquet base)Hive ACID ORC (merging)
Сжатие по умолчаниюSnappy / ZstdZlib / Snappy / Zstd
Predicate pushdownда, column statisticsда, bloom filters + stats
Bloom filtersопционально в Parquet 2.xвстроены для строковых столбцов
Скорость в Hiveхорошая через Tezhistorically лучше на Hive
Скорость в Sparkде facto standardподдерживается, но реже выбирают
Python pandaspyarrow, first-classчерез pyarrow/orc module, реже
Новый проект с нулячаще Parquetесли команда уже на Hive/ORC
ClickHouse, DuckDBnative read/writeограниченная или read-only
# ORC в PySpark
df.write.format("orc").save("/data/hive_table/")
spark.read.orc("/data/hive_table/")

Для первого знакомства важнее понять идею колоночного файла, чем выбирать между Parquet и ORC. Оба выигрывают у CSV на гигабайтах аналитики.

Когда ORC оправдан

  • существующий Hive-warehouse с сотнями ORC-таблиц
  • политика безопасности запрещает менять формат
  • команда эксплуатации знает ORC tuning (stripe size, bloom filters)

Когда мигрировать на Parquet

  • новый Spark-only pipeline без Hive metastore
  • кросс-языковый стек (Rust, Go, Arrow)
  • облачный lake без legacy Hadoop

Инструменты

parquet-tools / parquet-cli

# Java parquet-tools
parquet-tools head -n 20 sales.parquet
parquet-tools schema sales.parquet
parquet-tools rowcount sales.parquet
parquet-tools dump sales.parquet --column revenue

Полезно при расследовании, почему Spark видит другой тип — schema в footer — источник правды.

DuckDB

Встраиваемая аналитическая SQL-СУБД. Читает Parquet локально и в S3 без кластера:

INSTALL httpfs;
LOAD httpfs;

SELECT *
FROM read_parquet('sales/**/*.parquet', hive_partitioning = true)
WHERE year = 2026
LIMIT 100;

COPY (
SELECT region, SUM(revenue) AS total
FROM 'sales.parquet'
GROUP BY region
) TO 'summary.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);

DuckDB — отличный способ прототипировать SQL-трансформации перед переносом в Spark. См. также SQLite для маленьких объёмов.

Polars

DataFrame-библиотека на Rust с lazy API:

import polars as pl

q = (
pl.scan_parquet("sales/year=2026/*.parquet")
.filter(pl.col("revenue") > 1000)
.group_by("region")
.agg(pl.col("revenue").sum().alias("total"))
)
print(q.explain()) # план с pushdown
df = q.collect()

Polars агрессивно использует predicate и projection pushdown при scan Parquet.

Pandas + PyArrow

import pandas as pd

pd.read_parquet("sales.parquet", engine="pyarrow", columns=["region"])

Для файлов больше RAM — pyarrow.dataset с batch iteration:

import pyarrow.dataset as ds

dataset = ds.dataset("sales/", format="parquet", partitioning="hive")
for batch in dataset.to_batches(columns=["revenue"], filter=ds.field("year") == 2026):
process(batch)

Когда Parquet не подходит

Parquet — не универсальная замена всем форматам.

СценарийЛучше альтернативаПочему
Обмен с бухгалтерией, ExcelCSVчеловек открывает в таблице
REST API, конфиг приложенияJSONтекст, schema-on-read
Одна маленькая таблица на ноутбукеCSV или SQLiteoverhead метаданных Parquet
Потоковая запись событий по одной строкеKafka, JSON linesParquet batch-oriented
Частые одиночные UPDATE строкPostgreSQL, Delta/IcebergParquet immutable files
Нужен full-text search по полюElasticsearch, OpenSearchcolumnar не для inverted index
Картинки, PDF, бинарные blobobject storage as-isParquet для tabular
Latency < 10 ms point lookup по PKOLTP БДParquet scan-oriented
5 строк для unit-тестаinline JSON в тестене тащить parquet-tools в CI

Immutable files — ключевая идея. Parquet-файл после записи не патчат на месте; обновление = перезапись файла или новая версия в Delta/Iceberg.

Много мелких случайных записей — Parquet неудобен; накапливайте batch в буфере, flush раз в N минут или M мегабайт.


Когда какой формат

ЗадачаФормат
Отчёт в Excel, обмен с бухгалтериейCSV
REST API, конфиг приложенияJSON
Датасет от ~100 МБ, много агрегаций и MLParquet
Архив сырых событий в lakeParquet или ORC + разбиение по дате
Одна маленькая таблица на ноутбукеCSV или SQLite достаточно
Feature store для ML offlineParquet
Логи приложения в реальном времениJSON lines → bronze Parquet batch

Жёсткого порога "pandas уже не тянет" нет — смотрите на объём RAM, время read_csv и сколько раз вы проходите по файлу. Подсказки — пакетная работа.


Практический пайплайн CSV → Parquet

import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

# 1. Загрузка с явными типами
df = pd.read_csv(
"raw/sales.csv",
encoding="utf-8-sig",
sep=";",
dtype={"order_id": "Int64", "region": "string"},
parse_dates=["order_date"],
)

# 2. Очистка
df = df.dropna(subset=["order_id", "revenue"])
df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce")

# 3. Партиционированная запись через PyArrow
table = pa.Table.from_pandas(df)
pq.write_to_dataset(
table,
root_path="lake/silver/sales",
partition_cols=["year", "month"],
compression="zstd",
existing_data_behavior="overwrite_or_ignore",
)

Проверка:

import duckdb
duckdb.sql("SELECT COUNT(*) FROM 'lake/silver/sales/**/*.parquet'").show()

Типичные ошибки

ОшибкаЧто сделать
Файл не открывается в блокнотеParquet бинарный — DuckDB, Arrow, Spark
Типы столбцов разъехались после склейкиЕдиная схема при записи; явный schema в Spark
CSV сконвертировали без очисткиСначала типы и NULL — очистка в Pandas
Один гигантский файлРазбить по партициям; row group порядка 128–256 МБ
Тысячи файлов по 50 КБcoalesce / compact job
Переименовали столбец в новых файлахVIEW с alias или миграция silver
int/float путаются между batchЯвный schema, не полагаться на inference
Snappy плохо сжимает UUID-столбецОжидаемо; вынести в отдельный файл или нормализовать
Pruning не работаетПроверьте hive paths и hive_partitioning=true
ORC не читается в PolarsКонвертируйте через Spark или pyarrow

ClickHouse и другие движки

ClickHouse читает Parquet из S3/HDFS natively:

SELECT region, sum(revenue)
FROM s3('https://bucket/sales/*.parquet', 'Parquet')
WHERE year = 2026
GROUP BY region;

Trino/Presto — federated SQL по каталогу:

SELECT * FROM hive.default.sales WHERE dt = '2026-06-14';

BigQuery — external tables over Parquet in GCS.

Pandas 2.x — optional PyArrow backend для zero-copy handoff в ML (scikit-learn, PyTorch dataloaders через to_numpy() на Arrow arrays).


Безопасность и governance

Parquet-файл в публичном bucket раскрывает схему любому, кто скачает footer — не храните секреты в именах столбцов.

Column-level encryption — Parquet modular encryption (редко в small teams); чаще шифрование bucket (SSE-S3) + IAM.

Lineage — OpenLineage/Marquez отслеживают путь CSV → bronze Parquet → gold.

PII — маскируйте в silver; bronze может содержать сырые персональные данные с ограниченным ACL.


Упражнения

1. Row vs column

Скачайте sample CSV ~50 МБ (например, публичный датасет продаж). Засеките:

import time
import pandas as pd

t0 = time.perf_counter()
df = pd.read_csv("big.csv", usecols=["region", "amount"])
print("CSV partial:", time.perf_counter() - t0)

df.to_parquet("big.parquet", compression="snappy")

t0 = time.perf_counter()
df2 = pd.read_parquet("big.parquet", columns=["region", "amount"])
print("Parquet partial:", time.perf_counter() - t0)

Сравните размер файлов на диске.

2. Кодеки

Запишите один DataFrame в три файла (snappy, zstd, gzip). Таблица: размер MB, время записи, время чтения.

3. Footer

Установите parquet-tools или используйте DuckDB DESCRIBE. Найдите min/max для числового столбца в metadata.

4. Partition pruning

Создайте структуру папок demo/year=2025/ и demo/year=2026/ с Parquet внутри. Запрос в DuckDB с hive_partitioning — убедитесь через EXPLAIN, что читается одна ветка.

5. Spark local

spark.read.parquet("demo/").filter("year = 2026").explain()

Найдите в плане PartitionFilters или PushedFilters.

6. Schema evolution

Запишите два файла — во втором добавьте столбец notes. Прочитайте оба одним read_parquet('demo/*.parquet') в Pandas. Что в notes для строк из первого файла?

7. Bronze → silver

Возьмите грязный CSV с пропусками и дубликатами. Очистите в pandas, запишите в silver/ с партицией по month. Документируйте схему в комментарии SQL для DuckDB.

8. Когда не Parquet

Для каждого сценария (чат-приложение, Excel-отчёт, ML training 10 GB, конфиг nginx) выберите формат и опишите одним предложением.


Связанные материалы