Pandas: оптимизация производительности
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
Pandas — де-факто стандарт для data analysis в Python. Но на 10M+ rows легко напороться на медленные операции, OOM. Знать, как оптимизировать — разница между 30-секундным и 10-минутным скриптом.
На собесах middle+ часто: «как бы вы ускорили этот pandas-код?».
Топ приёмов
1. Vectorize (избегать for loops)
Плохо:
result = []
for i in range(len(df)):
result.append(df['a'][i] + df['b'][i])Хорошо:
df['sum'] = df['a'] + df['b']Векторизованные операции — C-optimized, в 100x быстрее.
2. apply vs map vs vectorize
Skip lambdas для простых операций:
# Slow
df['upper'] = df['name'].apply(lambda x: x.upper())
# Fast (vectorized string methods)
df['upper'] = df['name'].str.upper()3. Categorical dtype
Для strings с low cardinality:
df['country'] = df['country'].astype('category')Memory ↓ 10x, operations быстрее.
4. Правильный dtype
# Default int64 → 8 bytes
# Часто достаточно int32 или int16
df['age'] = df['age'].astype('int16')5. Избегать chained indexing
# Плохо (warning, может не сработать)
df['col'][0] = 5
# Хорошо
df.loc[0, 'col'] = 56. iloc vs loc
iloc— positional (integer)loc— label-based
Для performance — iloc обычно быстрее.
7. Query vs boolean indexing
# Боlee читаемый + часто быстрее на big data
df.query('age > 30 and income > 50000')
# vs
df[(df['age'] > 30) & (df['income'] > 50000)]8. In-place operations
# Не in-place (создаёт copy)
df = df.fillna(0)
# In-place (быстрее, меньше memory)
df.fillna(0, inplace=True)Но осторожно: inplace deprecated, использование меняется.
9. Read только нужные columns
df = pd.read_csv('big.csv', usecols=['col1', 'col2'])Не загружать всё.
10. Chunking для huge files
for chunk in pd.read_csv('huge.csv', chunksize=100000):
process(chunk)Memory optimization
Check
df.memory_usage(deep=True).sum() / 1024**2 # MBDowncast
df = df.apply(pd.to_numeric, errors='ignore', downcast='integer')Auto-detect minimal dtype.
Object to category
for col in df.select_dtypes(include='object'):
if df[col].nunique() / len(df) < 0.5:
df[col] = df[col].astype('category')Альтернативы
polars
Rust-based, faster на large data:
import polars as pl
df = pl.read_csv('file.csv')
df = df.filter(pl.col('x') > 100).group_by('y').sum()10-100x быстрее pandas на many operations.
DuckDB
In-process SQL database:
import duckdb
result = duckdb.sql("SELECT * FROM 'file.parquet' WHERE x > 100").df()Fast, SQL syntax, lazy.
Dask
Pandas API, distributed:
import dask.dataframe as dd
df = dd.read_csv('*.csv')
df.groupby('col').sum().compute()Для datasets > memory.
Spark
Для huge data + cluster.
Profiling
%timeit
%timeit df['col'].sum()Measure single operation.
%prun
%prun slow_function()Function-level profiling.
Memory profiler
from memory_profiler import profile
@profile
def func():
...Specific tips
merge vs join
# Merge for complex
df.merge(other, on='key')
# Join for index-based
df.join(other) # on indexIndex matters
Set index для частых lookups:
df.set_index('user_id', inplace=True)
df.loc[user_id] # O(1)groupby + agg
# Slow
df.groupby('col').apply(custom_function)
# Fast
df.groupby('col').agg({'a': 'sum', 'b': 'mean'})Parallel
modin
Drop-in pandas replacement:
import modin.pandas as pd # вместо pandasAuto-parallelizes.
swifter
Auto-detect vectorization:
df['new'] = df['col'].swifter.apply(my_func)На собесе
«Pandas медленно. Как ускорить?»
- Vectorize (no loops)
- Правильный dtype (category, downcast)
- Read только нужные columns
- Profile для identifying bottleneck
«10M rows — pandas ok?»
В memory — да. Но polars / DuckDB обычно быстрее.
«100M+ rows?»
Уже не pandas. Spark, DuckDB, warehouse.
Частые ошибки
Loop over rows
Always slow. Use vectorization or .apply (better still — no).
Object dtype
String columns — explicit str or category.
Inplace mix
Use consistent style. Modern — assignment: df = df.method().
Связанные темы
- Как работать с большим датасетом в pandas
- Polars vs pandas
- Pandas memory optimization
- Pandas vs NumPy
FAQ
Pandas или polars для новых?
Polars faster, pandas standard. Learn both.
Parallel pandas — modin worth?
Для simple replacement yes. Для complex — Spark / polars.
Typing autodetect?
df.infer_objects() или downcast='integer'.
Тренируйте Python — откройте тренажёр с 1500+ вопросами для собесов.