Как работать с большим датасетом в pandas

Когда pandas не справляется

Pandas работает в памяти. Если датасет больше RAM (16–32 Gb обычно) — beda:

  • MemoryError.
  • Тормозит весь компьютер.
  • Простой query занимает 30 минут.

Решение: или уменьшить датасет, или использовать другой инструмент.

1. Используйте dtype

По умолчанию pandas грузит числа как int64 / float64 — 8 bytes на значение. Часто избыточно.

# До
df = pd.read_csv('big.csv')
df.memory_usage(deep=True).sum() / 1024**2  # 1500 MB

# После
dtypes = {
    'user_id': 'int32',          # 4B вместо 8B
    'amount': 'float32',          # 4B вместо 8B
    'status': 'category',         # 1B вместо ~50B на строку
    'is_active': 'bool',          # 1B
}
df = pd.read_csv('big.csv', dtype=dtypes)
df.memory_usage(deep=True).sum() / 1024**2  # 400 MB → 4x экономия

category — для столбцов с малым числом уникальных значений (страна, статус).

2. Прочитать только нужные колонки

# Все 50 столбцов
df = pd.read_csv('big.csv')

# Только 5 нужных
df = pd.read_csv('big.csv', usecols=['user_id', 'date', 'amount', 'status', 'category'])

10x экономия памяти на wide-таблицах.

3. Chunksize для обработки

Если данных больше памяти — читайте по частям:

chunk_iter = pd.read_csv('big.csv', chunksize=100_000)

results = []
for chunk in chunk_iter:
    # Обработка одного chunk
    result = chunk.groupby('user_id')['amount'].sum().reset_index()
    results.append(result)

# Объединить
final = pd.concat(results).groupby('user_id')['amount'].sum().reset_index()

Обрабатываем 10 GB при 1 GB RAM.

4. Parquet вместо CSV

# CSV: медленно, без типов
df.to_csv('data.csv')          # 5 GB
df = pd.read_csv('data.csv')    # 60 секунд

# Parquet: быстро, типы сохраняются
df.to_parquet('data.parquet')           # 2 GB (сжатие)
df = pd.read_parquet('data.parquet')    # 10 секунд

Дополнительно: parquet поддерживает read только нужных колонок.

Тренироваться на таких вопросах можно в Telegram-боте Карьерник — там 1500+ задач с реальных собесов с разборами.

5. Фильтр перед загрузкой (Parquet)

import pyarrow.parquet as pq

# Только строки, удовлетворяющие условию
table = pq.read_table('data.parquet', filters=[('country', '=', 'RU')])
df = table.to_pandas()

Не грузим лишнее в память.

6. Векторизация вместо apply

# Медленно
df['x2'] = df['x'].apply(lambda v: v ** 2)

# Быстрее в 100x
df['x2'] = df['x'] ** 2

# Или через NumPy
import numpy as np
df['log_x'] = np.log(df['x'])

apply с Python-функцией — последнее средство.

7. Бери SQL вместо pandas

Часто проще предагрегировать в БД, потом загрузить в pandas:

# ❌ Медленно
df = pd.read_sql('SELECT * FROM events', conn)  # 50M строк
agg = df.groupby('date')['user_id'].nunique()

# ✅ Быстрее
df = pd.read_sql("""
    SELECT date, COUNT(DISTINCT user_id) AS dau
    FROM events GROUP BY 1
""", conn)  # 365 строк

ClickHouse / BigQuery считают агрегаты в распределённой системе намного быстрее pandas.

8. Polars вместо pandas

Polars — современная альтернатива pandas, работает в 5–30x быстрее на больших данных.

import polars as pl

df = pl.read_csv('big.csv')
agg = df.group_by('user_id').agg(pl.col('amount').sum())

Синтаксис похож на pandas, переход — за пару дней.

Когда Polars: данные 1–100 GB, нужна скорость.

9. Dask для очень больших данных

Dask — pandas API + распределённые вычисления.

import dask.dataframe as dd

df = dd.read_csv('big_*.csv')  # читает много файлов
result = df.groupby('user_id')['amount'].sum().compute()

Когда: данные 100 GB+, нужно обрабатывать на кластере.

10. Используйте SQL DB в качестве storage

Если данные уже в БД — обрабатывайте там, не выгружайте в pandas:

# ❌ Выгрузить → обработать → загрузить обратно
df = pd.read_sql('SELECT * FROM events', conn)
df['hour'] = df['created_at'].dt.hour
df.to_sql('events_hourly', conn)

# ✅ Сделать всё в SQL
conn.execute("""
    CREATE TABLE events_hourly AS
    SELECT *, EXTRACT(HOUR FROM created_at) AS hour FROM events
""")

К слову, набить руку на таких кейсах удобно через тренажёр в Telegram — разбирайте по 10 вопросов в день, через 2 недели тема становится рефлексом.

Порядок действий при memory error

  1. Уменьшить колонкиusecols.
  2. Уменьшить типыdtypes.
  3. Chunksize для batch-обработки.
  4. Parquet вместо CSV.
  5. Pre-aggregate в SQL.
  6. Polars / Dask если ничего не помогло.

Бенчмарк (примерно)

Для 10 Gb CSV → загрузить и сделать groupby:

Инструмент Время RAM
pandas (без оптимизации) 5 мин или OOM 30 GB
pandas + dtypes 2 мин 12 GB
pandas + chunksize 3 мин 2 GB
Polars 30 сек 8 GB
Dask 1 мин 4 GB (multi-core)
ClickHouse 5 сек мало

Лайфхак: pandas memory inspector

df.info(memory_usage='deep')

# Какая колонка занимает много
df.memory_usage(deep=True).sort_values(ascending=False)

Читайте также

FAQ

Когда переключаться с pandas?

Когда данные >1–10 GB или операции >5 минут.

pandas или Polars?

Pandas — стандарт, больше material. Polars — современнее, быстрее. Учить оба.

Dask vs Spark?

Dask — Python-friendly, для дата-сайентистов. Spark — production-grade, для дата-инженеров.

Что делать, если данные 100 GB?

  1. Pre-aggregate в БД. 2) Если обработка нужна — Dask / Spark на кластере. 3) Polars если влезает на RAM сервера.