Каждый аналитик данных рано или поздно сталкивается с ситуацией, когда привычный Pandas начинает «есть» гигабайты памяти, а время выполнения groupby или merge измеряется минутами. Переход на Polars обещает ускорение, но требует переписывания кода и понимания новых API. LLM могут стать мостом между этими мирами — если знать, как правильно формулировать запросы.
В этой подборке — 12 промтов для практической работы с табличными данными. Мы разберём, как заставить модель писать корректные цепочки операций, переводить тяжёлый Pandas-код на Polars, объяснять утечки памяти и предлагать векторизацию вместо apply. Каждый промт сопровождается примером результата и комментарием о том, где LLM ошибается и что нужно перепроверять.
Базовые промты: очистка и подготовка данных
1. Генерация цепочки очистки с обработкой пропусков
Задача: быстро получить код для типовой очистки датасета с пропусками, выбросами и дубликатами.
Промт:
Ты — эксперт по Pandas. Напиши функцию clean_dataframe(df), которая:
1. Удаляет полные дубликаты.
2. Для числовых колонок заполняет пропуски медианой, для категориальных — модой.
3. Обрезает выбросы по правилу IQR (1.5 * IQR) для всех числовых колонок.
4. Возвращает очищенный DataFrame и словарь с количеством удалённых/заполненных значений.
Добавь комментарии, объясняющие каждый шаг.
Пример результата (сокращённо):
import pandas as pd
import numpy as np
def clean_dataframe(df):
report = {}
initial_len = len(df)
df = df.drop_duplicates()
report['duplicates_removed'] = initial_len - len(df)
for col in df.select_dtypes(include=np.number).columns:
median = df[col].median()
df[col] = df[col].fillna(median)
q1 = df[col].quantile(0.25)
q3 = df[col].quantile(0.75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
df[col] = df[col].clip(lower, upper)
for col in df.select_dtypes(include='object').columns:
mode = df[col].mode()[0]
df[col] = df[col].fillna(mode)
return df, report
LLM хорошо справляется с шаблонными операциями, но может пропустить обработку колонок с типом datetime или категориальным типом Pandas. Всегда проверяйте, что функция не ломается на пустых датафреймах.
fillna(method='ffill') — в новых версиях Pandas он вызывает предупреждение. Правильно: df.ffill().
2. Промт для merge с проверкой типов ключей
Задача: избежать ошибки слияния из-за разных типов ключевых колонок.
Промт:
Напиши код для merge двух DataFrame по колонке 'user_id'. Перед слиянием проверь, что типы колонок совпадают. Если нет — приведи оба к строковому типу и выведи предупреждение. После слияния выведи количество строк и проверь, что не появилось дубликатов ключей (если в правом датафрейме ключи уникальны).
Пример результата:
def safe_merge(left, right, on='user_id'):
if left[on].dtype != right[on].dtype:
print(f"Типы не совпадают: {left[on].dtype} vs {right[on].dtype}. Приводим к строке.")
left[on] = left[on].astype(str)
right[on] = right[on].astype(str)
merged = left.merge(right, on=on, how='left', validate='many_to_one')
print(f"Строк после merge: {len(merged)}")
return merged
Параметр validate помогает отловить неожиданное размножение строк. LLM часто забывает про него, поэтому указывайте явно.
Продвинутые промты: группировки, временные ряды и векторизация
3. Сложный groupby с несколькими агрегациями
Задача: построить отчёт с разными агрегатами для разных колонок.
Промт:
Дан DataFrame с колонками: region, product, sales, quantity, date. Напиши код, который группирует по region и product и считает:
- сумму sales,
- среднее quantity,
- количество уникальных дат.
Используй именованную агрегацию Pandas. Отсортируй результат по сумме sales по убыванию.
Пример результата:
result = (df.groupby(['region', 'product'])
.agg(total_sales=('sales', 'sum'),
avg_quantity=('quantity', 'mean'),
unique_dates=('date', 'nunique'))
.sort_values('total_sales', ascending=False)
.reset_index())
Именованная агрегация появилась в Pandas 0.25 и считается стандартом. LLM может предложить устаревший синтаксис с словарём — проверяйте.
4. Resample для временных рядов с пропущенными датами
Задача: корректно агрегировать данные по неделям, учитывая пропуски.
Промт:
Есть DataFrame с колонкой date (тип datetime) и value. Напиши код, который:
1. Устанавливает date как индекс.
2. Ресемплирует по неделям ('W') и считает сумму value.
3. Заполняет пропущенные недели нулями.
4. Возвращает результат с индексом в исходном формате дат.
Пример результата:
df = df.set_index('date')
weekly = df.resample('W')['value'].sum().fillna(0)
weekly.index = weekly.index.strftime('%Y-%m-%d')
LLM иногда путает 'W' и 'W-MON' — уточняйте, что нужна неделя, заканчивающаяся в воскресенье (по умолчанию).
5. Векторизация вместо apply
Задача: ускорить медленный apply, заменив его векторными операциями.
Промт:
У меня есть медленный код:
df['category'] = df['description'].apply(lambda x: 'A' if 'apple' in x.lower() else 'B')
Предложи векторизованную замену без apply. Объясни, почему это быстрее.
Пример результата:
df['category'] = np.where(df['description'].str.lower().str.contains('apple'), 'A', 'B')
Векторизация через строковые методы Pandas работает в разы быстрее, но требует, чтобы все значения были строками. Если есть пропуски, str.contains вернёт NaN — предусмотрите fillna(False).
6. Перевод Pandas-кода на Polars
Задача: автоматически конвертировать цепочку операций в Polars.
Промт:
Переведи следующий Pandas-код на Polars, сохранив логику:
result = df[df['value'] > 100].groupby('category')['value'].agg(['mean', 'count']).reset_index()
Пример результата:
import polars as pl
result = (df.filter(pl.col('value') > 100)
.group_by('category')
.agg([pl.col('value').mean().alias('mean'),
pl.col('value').count().alias('count')])
.sort('category'))
Polars не использует индексы, поэтому reset_index() не нужен. LLM может забыть про alias — проверяйте имена колонок.
Экспертные промты: отладка памяти и сложные сценарии
7. Диагностика утечки памяти
Задача: понять, почему скрипт потребляет много RAM.
Промт:
Мой Pandas-скрипт обрабатывает CSV на 2 ГБ и падает с MemoryError. Вот код:
[вставьте код]
Проанализируй, какие операции создают копии DataFrame, и предложи оптимизации: использование категориальных типов, чтение по частям (chunksize), удаление ненужных колонок.
Пример результата (фрагмент):
# Вместо полной загрузки:
df = pd.read_csv('big.csv', usecols=['col1', 'col2'], dtype={'col1': 'category'})
# Или чанками:
for chunk in pd.read_csv('big.csv', chunksize=100_000):
process(chunk)
LLM часто предлагает del df и gc.collect(), но это не решает проблему копий. Ищите в ответе конкретные методы: astype('category'), pd.read_csv(..., low_memory=False).
df.memory_usage(deep=True) без пояснения — это ключевой инструмент для поиска тяжёлых колонок.
8. Сложный merge с асинхронными данными
Задача: объединить датафреймы с разной частотой (например, ежедневные продажи и ежемесячные маркетинговые бюджеты).
Промт:
Есть два DataFrame: sales (daily) и marketing (monthly). Нужно для каждой продажи подтянуть маркетинговый бюджет за соответствующий месяц. Напиши код, который создаёт колонку 'month' в обоих датафреймах и выполняет merge по этой колонке. Учти, что в marketing может не быть некоторых месяцев — заполни пропуски нулями.
Пример результата:
sales['month'] = sales['date'].dt.to_period('M')
marketing['month'] = marketing['date'].dt.to_period('M')
merged = sales.merge(marketing[['month', 'budget']], on='month', how='left')
merged['budget'] = merged['budget'].fillna(0)
LLM может предложить resample вместо merge — это работает, но менее эффективно при больших объёмах. Выбирайте решение осознанно.
9. Оптимизация groupby с помощью категориальных типов
Задача: ускорить группировку по строковой колонке с малым числом уникальных значений.
Промт:
Код: df.groupby('city')['sales'].sum() выполняется 10 секунд. Колонка city содержит 50 уникальных значений. Как ускорить? Предложи преобразование типов и альтернативы.
Пример результата:
df['city'] = df['city'].astype('category')
# или использовать Polars:
import polars as pl
df_pl = pl.from_pandas(df)
result = df_pl.group_by('city').agg(pl.col('sales').sum())
Категориальный тип уменьшает память и ускоряет группировку за счёт целочисленных кодов. Но осторожно: при частых операциях добавления новых категорий это может замедлить работу.
10. Генерация синтетических данных для тестов
Задача: создать реалистичный датафрейм для проверки пайплайна.
Промт:
Сгенерируй Pandas DataFrame с 10 000 строк и колонками: user_id (int), age (int от 18 до 70), country (выбор из 5 стран), purchase_amount (float с логнормальным распределением), purchase_date (datetime за последний год). Добавь 5% пропусков в purchase_amount.
Пример результата:
import numpy as np
import pandas as pd
np.random.seed(42)
n = 10000
df = pd.DataFrame({
'user_id': np.arange(n),
'age': np.random.randint(18, 71, n),
'country': np.random.choice(['RU', 'US', 'DE', 'FR', 'CN'], n),
'purchase_amount': np.random.lognormal(mean=3, sigma=1, size=n),
'purchase_date': pd.date_range('2025-01-01', periods=n, freq='H')
})
df.loc[df.sample(frac=0.05).index, 'purchase_amount'] = np.nan
LLM хорошо справляется с такими задачами, но может перепутать параметры распределения. Проверяйте, что данные не содержат отрицательных сумм и даты в разумном диапазоне.
11. Перевод сложной агрегации на Polars
Задача: переписать многошаговую агрегацию с оконными функциями.
Промт:
Переведи на Polars:
df['rank'] = df.groupby('category')['value'].rank(pct=True)
df['rolling_mean'] = df.groupby('category')['value'].transform(lambda s: s.rolling(7).mean())
Пример результата:
df_pl = df_pl.with_columns([
pl.col('value').rank().over('category').alias('rank'),
pl.col('value').rolling_mean(7).over('category').alias('rolling_mean')
])
В Polars оконные функции требуют явного указания .over(). LLM может забыть про порядок сортировки для rolling — проверяйте, что данные отсортированы по времени.
12. Отладка ошибок типов при merge
Задача: разобраться, почему merge возвращает пустой результат.
Промт:
Код: df1.merge(df2, on='id') возвращает 0 строк, хотя в обоих датафреймах есть значения '123'. В df1 колонка id имеет тип object, в df2 — int64. Как исправить и почему это происходит?
Пример результата:
df1['id'] = df1['id'].astype(int) # или df2['id'].astype(str)
merged = df1.merge(df2, on='id')
Причина: Pandas сравнивает значения с учётом типа, и строка '123' не равна числу 123. LLM часто предлагает просто astype(str) для обоих, но это может привести к потере ведущих нулей. Лучше приводить к тому типу, который соответствует смыслу данных.
Когда LLM ускоряет работу, а когда требует проверки
LLM отлично справляются с генерацией шаблонного кода, переводом между библиотеками и объяснением ошибок. Они экономят время на рутине: написание clean_dataframe, конвертация Pandas → Polars, поиск узких мест по описанию.
Но есть области, где модель ошибается предсказуемо:
- Типы данных и пропуски. LLM может предложить операцию, которая упадёт на NaN или на неожиданном типе. Всегда проверяйте код на тестовом наборе с пропусками.
- Порядок операций. В Polars порядок
filterиwith_columnsможет влиять на результат. Модель может переставить шаги местами. - Устаревший синтаксис. Pandas быстро развивается, и LLM иногда выдаёт код, который работает, но с предупреждениями (например,
appendвместоconcat).
Практическое правило: используйте LLM для черновика, но финальную проверку делайте на реальных данных с граничными случаями. Особенно это касается merge и groupby — здесь ошибки типов приводят к неверным результатам, а не к исключениям.
Если вы хотите глубже разобраться в работе с данными и LLM, обратите внимание на смежные материалы: Юнит-тесты на Python с LLM: 12 промтов от pytest-фикстур до Hypothesis и Golang с нуля: горутины, каналы и реальная цена конкурентности в Go.
Промты — это инструмент, который усиливает ваши навыки, но не заменяет понимание данных. Чем лучше вы знаете Pandas и Polars, тем точнее формулируете запросы и тем быстрее замечаете, где модель ошиблась. Начните с малого: возьмите один промт из этой подборки и примените к своему текущему проекту. Уже через несколько итераций вы выработаете собственный стиль общения с LLM, который будет экономить часы работы.