Шпаргалка по pandas
Последнее обновление
Создание DataFrame
Небольшие DataFrame, собранные вручную: для примеров, тестов и справочных таблиц.
| Задача | Код |
|---|---|
| Импорт pandas и NumPy | import pandas as pd
import numpy as np |
| Из словаря столбцов | df = pd.DataFrame({"name": ["Ada", "Ben"], "age": [36, 28]}) |
| Из списка строк | pd.DataFrame([{"name": "Ada", "age": 36}, {"name": "Ben", "age": 28}]) |
| Из массива NumPy | pd.DataFrame(np.arange(6).reshape(3, 2), columns=["x", "y"]) |
| Series с метками | s = pd.Series([3, 5, 2], index=["a", "b", "c"]) |
| Диапазон дат | pd.date_range("2026-01-01", periods=7, freq="D") |
Чтение и запись файлов
Один вызов загружает файл в DataFrame, другой записывает его обратно.
| Задача | Код |
|---|---|
| Прочитать CSV | df = pd.read_csv("sales.csv") |
| Только нужные столбцы, даты как даты | pd.read_csv("sales.csv", usecols=["date", "city", "sales"], parse_dates=["date"]) |
| Столбец в качестве индекса | pd.read_csv("sales.csv", index_col="id") |
| Табуляция или другой разделитель | pd.read_csv("sales.tsv", sep="\t") |
| Дополнительные метки пропусков | pd.read_csv("sales.csv", na_values=["n/a", "-"]) |
| Прочитать только первые строки | pd.read_csv("sales.csv", nrows=1000) |
| Прочитать лист Excel (нужен openpyxl) | pd.read_excel("report.xlsx", sheet_name="2026") |
| Прочитать записи JSON | pd.read_json("records.json") |
| Прочитать из SQL-базы | pd.read_sql("SELECT * FROM orders", conn) |
| Записать CSV без индекса | df.to_csv("clean.csv", index=False) |
| Записать в Excel или Parquet | df.to_excel("clean.xlsx", index=False), df.to_parquet("clean.parquet") |
Первый взгляд на данные
С этих вызовов стоит начинать знакомство с любым новым датасетом.
| Задача | Код |
|---|---|
| Первые и последние строки | df.head(), df.tail(3) |
| Число строк и столбцов | df.shape # (6, 7) |
| Типы столбцов и число непустых значений | df.info() |
| Статистика по числовым столбцам | df.describe() |
| Сводка по всем столбцам | df.describe(include="all") |
| Имена и типы столбцов | df.columns, df.dtypes |
| Частота каждого значения | df["city"].value_counts() |
| Доля каждого значения | df["city"].value_counts(normalize=True) |
| Уникальные значения и их число | df["city"].unique(), df["city"].nunique() |
| Пропуски по столбцам | df.isna().sum() |
| Случайная выборка строк | df.sample(3, random_state=0) |
| Корреляция числовых столбцов | df.corr(numeric_only=True) |
Выбор столбцов и строк
loc выбирает по меткам и включает конец среза. iloc выбирает по позициям и конец не включает.
| Задача | Код |
|---|---|
| Один столбец как Series | df["age"] |
| Несколько столбцов как DataFrame | df[["name", "age"]] |
| Строки и столбцы по меткам | df.loc[0:2, ["name", "age"]] |
| Строки и столбцы по позициям | df.iloc[0:2, 0:3] |
| Одно значение | df.at[0, "age"], df.iat[0, 3] |
| Последняя строка | df.iloc[-1] |
| Столбцы одного типа | df.select_dtypes(include="number") |
| Текстовые столбцы (pandas 3) | df.select_dtypes(include="str") |
| Столбцы по шаблону имени | df.filter(like="sal"), df.filter(regex="^(age|salary)$") |
| Поиск строк по столбцу | df.set_index("name").loc["Ada", "salary"] |
| Вернуть индекс 0, 1, 2 | df.reset_index(drop=True) |
| Удалить столбцы или строки | df.drop(columns=["email"]), df.drop(index=[0, 1]) |
Фильтрация строк
Бери каждое условие в скобки и объединяй их через & (и), | (или) и ~ (не).
| Задача | Код |
|---|---|
| Строки по условию | df[df["age"] > 30] |
| Оба условия | df[(df["age"] > 30) & (df["city"] == "Oslo")] |
| Любое из условий | df[(df["city"] == "Oslo") | (df["salary"] > 6000)] |
| Значение из списка | df[df["city"].isin(["Oslo", "Rome"])] |
| Значение не из списка | df[~df["city"].isin(["Oslo", "Rome"])] |
| Между двумя значениями, включая границы | df[df["age"].between(30, 40)] |
| Фильтр строкой запроса | df.query("age > 30 and city == 'Oslo'") |
| Переменная в запросе | min_age = 30
df.query("age > @min_age") |
| Текст содержит | df[df["name"].str.contains("e", case=False)] |
| Пропущенные или заполненные значения | df[df["age"].isna()], df[df["age"].notna()] |
| Отфильтровать строки и выбрать столбцы | df.loc[df["age"] > 30, ["name", "salary"]] |
| Первые или последние n строк | df.nlargest(3, "salary"), df.nsmallest(2, "age") |
| Строки с повторяющимся значением | df[df.duplicated(subset=["city"], keep=False)] |
Сортировка и ранжирование
| Задача | Код |
|---|---|
| Сортировка по столбцу | df.sort_values("salary") |
| По убыванию | df.sort_values("salary", ascending=False) |
| Сортировка по нескольким столбцам | df.sort_values(["city", "salary"], ascending=[True, False]) |
| Пропуски в начале | df.sort_values("age", na_position="first") |
| Сортировка по индексу | df.sort_index() |
| Ранги, 1 у наибольшего | df["salary"].rank(ascending=False) |
Добавление и изменение столбцов
Операции над целым столбцом работают быстро. Берись за apply, только если нет подходящего встроенного метода.
| Задача | Код |
|---|---|
| Новый столбец из других | df["bonus"] = df["salary"] * 0.1 |
| Добавить столбец в цепочке | df.assign(bonus=lambda d: d["salary"] * 0.1) |
| Значение по условию | df["level"] = np.where(df["age"] >= 40, "senior", "junior") |
| Значение по нескольким условиям | df["band"] = np.select([df["age"] < 30, df["age"] < 45], ["young", "mid"], default="senior") |
| Изменить значения там, где выполнено условие | df.loc[df["city"] == "Lima", "salary"] = 4000 |
| Замена значений по словарю | df["country"] = df["city"].map({"Oslo": "NO", "Rome": "IT", "Lima": "PE"}) |
| Применить функцию к столбцу | df["salary"].apply(lambda x: round(x, -3)) |
| Применить функцию к каждой ячейке | df[["age", "salary"]].map(lambda x: x / 1000) |
| Переименовать столбцы | df.rename(columns={"salary": "pay"}) |
| Изменить тип столбца | df["salary"].astype("float64") |
| Текст в числа, ошибки в NaN | pd.to_numeric(s, errors="coerce") |
| Числа в интервалы с метками | pd.cut(df["age"], bins=[0, 30, 45, 120], labels=["young", "mid", "senior"]) |
| Числа в группы равного размера | pd.qcut(df["salary"], q=4, labels=False) |
| Цикл по строкам (медленно, крайний случай) | for row in df.itertuples():
print(row.name, row.salary) |
Пропуски и очистка данных
Пропущенные числа выглядят как NaN, пропущенные даты как NaT. Каждый из этих вызовов возвращает новый объект, так что присваивай результат.
| Задача | Код |
|---|---|
| Доля пропусков по столбцам | df.isna().mean() |
| Удалить строки с любым пропуском | df.dropna() |
| Удалить строки без ключевого столбца | df.dropna(subset=["age"]) |
| Заполнить значением для каждого столбца | df.fillna({"age": df["age"].median()}) |
| Заполнить из предыдущей или следующей строки | df["age"].ffill(), df["age"].bfill() |
| Заполнить пропуски интерполяцией | df["age"].interpolate() |
| Превратить метки в пропуски | df.replace(["n/a", "-", ""], np.nan) |
| Удалить дубликаты строк | df.drop_duplicates() |
| Оставить последнюю строку по ключу | df.drop_duplicates(subset=["city"], keep="last") |
| Привести в порядок имена столбцов | df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_") |
| Ограничить выбросы | df["salary"].clip(lower=4000, upper=6000) |
| Тип category для повторяющегося текста | df["city"] = df["city"].astype("category") |
Текстовые столбцы (.str)
В pandas 3 текстовые столбцы по умолчанию получают dtype str. Методы .str работают сразу со всем столбцом, а пропуски так и остаются пропусками.
| Задача | Код |
|---|---|
| Верхний, нижний регистр, с заглавной | df["name"].str.upper(), df["name"].str.lower(), df["name"].str.title() |
| Убрать пробелы по краям | df["name"].str.strip() |
| Заменить текст | df["email"].str.replace("mail.com", "example.com") |
| Замена по регулярному выражению | df["email"].str.replace(r"@.*", "", regex=True) |
| Содержит, начинается с | df["name"].str.contains("a"), df["name"].str.startswith("E") |
| Длина каждого значения | df["name"].str.len() |
| Первые символы | df["name"].str[:2] |
| Разбить на столбцы | df["email"].str.split("@", expand=True) |
| Извлечь группу регулярного выражения | df["email"].str.extract(r"@(\w+)") |
| Склеить текстовые столбцы | df["name"] + " (" + df["city"] + ")" |
| Дополнить числа нулями | df["salary"].astype("str").str.zfill(6) |
| Новый текстовый dtype | df["name"].dtype # str |
Даты и время
Аксессор .dt работает со столбцами datetime. Отсортированный индекс дат (ts ниже) позволяет делать срезы по строкам с датами и ресемплинг.
| Задача | Код |
|---|---|
| Преобразовать столбец в datetime | df["date"] = pd.to_datetime(df["date"]) |
| Разбор по точному формату | pd.to_datetime(["03/01/2026", "15/01/2026"], format="%d/%m/%Y") |
| Год, месяц, название дня недели | df["date"].dt.year, df["date"].dt.month, df["date"].dt.day_name() |
| Сгруппировать даты по месяцам | df["date"].dt.to_period("M") |
| Дней между двумя датами | (pd.Timestamp("2026-12-31") - df["date"]).dt.days |
| Прибавить дни или месяцы | df["date"] + pd.Timedelta(days=30), df["date"] + pd.DateOffset(months=1) |
| Строки в диапазоне дат | df[df["date"].between("2021-01-01", "2022-12-31")] |
| Индекс дат, срез по строке | ts = df.set_index("date").sort_index()
ts.loc["2021":"2022"] |
| Сумма по месяцам | ts["salary"].resample("ME").sum() |
Среднее по неделям; "QE" по кварталам, "YE" по годам | ts["salary"].resample("W").mean() |
| Скользящее среднее | ts["salary"].rolling(3).mean() |
| Изменение относительно предыдущей строки | ts["salary"].diff(), ts["salary"].pct_change() |
| Сдвинуть значения на строку вниз | ts["salary"].shift(1) |
| Нарастающий итог | ts["salary"].cumsum() |
| Задать и сменить часовой пояс | ts.index.tz_localize("UTC").tz_convert("Europe/Oslo") |
Группировка и агрегация
groupby разбивает строки по ключу, применяет функцию к каждой группе и собирает результаты вместе.
| Задача | Код |
|---|---|
| Среднее по группам | df.groupby("city")["salary"].mean() |
| Несколько статистик сразу | df.groupby("city")["salary"].agg(["mean", "max", "count"]) |
| Свои имена итоговых столбцов | df.groupby("city").agg(avg_pay=("salary", "mean"), people=("name", "count")) |
| Группировка по двум столбцам | df.groupby(["city", "team"])["salary"].sum() |
| Оставить ключи столбцами | df.groupby("city", as_index=False)["salary"].mean() |
| Число строк в группе | df.groupby("city").size() |
| Результат группы в каждой строке | df["city_avg"] = df.groupby("city")["salary"].transform("mean") |
| Оставить только часть групп | df.groupby("city").filter(lambda g: len(g) >= 2) |
| Строка с максимумом в группе | df.loc[df.groupby("city")["salary"].idxmax()] |
| Первые n строк в группе | df.sort_values("salary", ascending=False).groupby("city").head(2) |
| Пронумеровать строки в группе | df.groupby("city").cumcount() |
| Цикл по группам | for city, group in df.groupby("city"):
print(city, len(group)) |
| Фильтр, группировка и сортировка одной цепочкой | (df.query("age > 30")
.groupby("city", as_index=False)
.agg(avg_pay=("salary", "mean"))
.sort_values("avg_pay", ascending=False)) |
Сводные таблицы и изменение формы
В длинном формате одна строка на одно наблюдение. В широком одна переменная разложена по столбцам.
| Задача | Код |
|---|---|
| Сводная таблица | df.pivot_table(index="city", columns="team", values="salary", aggfunc="mean", fill_value=0) |
| Сводная таблица с итогами | df.pivot_table(index="city", values="salary", aggfunc="sum", margins=True) |
| Подсчёт сочетаний | pd.crosstab(df["city"], df["team"]) |
| Доли по строкам | pd.crosstab(df["city"], df["team"], normalize="index") |
| Из широкого формата в длинный | long = df.melt(id_vars="name", value_vars=["age", "salary"], var_name="field", value_name="value") |
| Из длинного формата в широкий | long.pivot(index="name", columns="field", values="value") |
| Внутренний уровень индекса в столбцы | df.groupby(["city", "team"])["salary"].sum().unstack(fill_value=0) |
| Строка на каждый элемент списка | pd.DataFrame({"id": [1, 2], "tags": [["a", "b"], ["c"]]}).explode("tags") |
| One-hot кодирование столбца | pd.get_dummies(df["team"], prefix="team") |
| Поменять строки и столбцы местами | df.T |
Объединение DataFrame
concat складывает таблицы вместе, а merge соединяет их по ключевым столбцам, как JOIN в SQL.
| Задача | Код |
|---|---|
| Склеить строки | pd.concat([df_2025, df_2026], ignore_index=True) |
| Поставить рядом | pd.concat([left, right], axis=1) |
| Соединить по ключевому столбцу | orders.merge(customers, on="customer_id", how="left") |
| Типы соединений | how="inner", "left", "right", "outer" |
| Ключи с разными именами | orders.merge(users, left_on="customer_id", right_on="id") |
| Проверить соединение и пометить источник строк | orders.merge(customers, on="customer_id", how="left", validate="many_to_one", indicator=True) |
| Соединить по индексу | left.join(right, how="inner") |
| Строки без пары в другой таблице | orders[~orders["customer_id"].isin(customers["customer_id"])] |
Графики в pandas
pandas рисует через Matplotlib. Каждый вызов графика возвращает Matplotlib ax, так что оформление можно продолжить.
| Задача | Код |
|---|---|
| Линейный график Series | ts["salary"].plot() |
| Линейный график по двум столбцам | df.plot(x="date", y="salary") |
| Столбчатая диаграмма по сводке | df.groupby("city")["salary"].mean().plot.bar() |
| Отсортированные горизонтальные столбцы | df["city"].value_counts().sort_values().plot.barh() |
| Гистограмма | df["salary"].plot.hist(bins=20) |
| Диаграмма рассеяния | df.plot.scatter(x="age", y="salary") |
| Ящик с усами по группам | df.boxplot(column="salary", by="city") |
| Заголовок и подпись оси | ax = df.plot(x="date", y="salary", title="Salary")
ax.set_ylabel("EUR") |
| Показать или сохранить график | plt.show(), ax.figure.savefig("salary.png", dpi=150) |
pandas 3: старые формы и что писать теперь
Код из старых туториалов в pandas 3 часто падает или выдаёт предупреждения. Пиши вместо него то, что указано в столбце pandas 3.
| Задача | pandas 3 | Старая форма |
|---|---|---|
| Функция для каждой ячейки | df.map(f) | df.applymap(f) |
| Заполнить из предыдущей строки | df.ffill() | df.fillna(method="ffill") |
| Добавить строки | pd.concat([df, new_rows]) | df.append(new_rows) |
| Ресемплинг на конец месяца | ts["salary"].resample("ME").sum() | resample("M") |
| Даты с шагом в час | pd.date_range("2026-01-01", periods=24, freq="h") | freq="H" |
| Задать одно значение | df.loc[0, "age"] = 37 | df["age"][0] = 37 |
| Изменить отфильтрованную часть | high = df[df["salary"] > 5000]
high["salary"] = 0 | high = df[df["salary"] > 5000].copy() |
| Это текстовый столбец? | df["name"].dtype == "str" | df["name"].dtype == object |
Код pandas, который аналитик данных пишет каждый день, разбитый по задачам: создать и загрузить данные, изучить их, выбрать и отфильтровать строки, добавить столбцы, разобраться с пропусками, поработать с текстом и датами, сгруппировать и агрегировать, изменить форму таблицы, объединить таблицы и построить график. В каждой строке короткий фрагмент кода, который можно сразу вставить, и одна строчка о том, что он делает.
Всё здесь написано для pandas 3, той версии, что работает на Coddy: текстовые столбцы получают новый dtype str, Copy-on-Write включён всегда, а удалённые API вроде applymap и fillna(method=...) остались только в таблице старых форм. В примерах используется DataFrame df со столбцами name, city, team, age, salary, date и email. Только начинаешь с Python? Загляни в шпаргалку по Python и песочницу Python, а рядом с этой шпаргалкой держи шпаргалки по NumPy, Matplotlib и scikit-learn.
Частые вопросы о шпаргалке по pandas
Эта шпаргалка по pandas бесплатная?
Какую версию pandas охватывает шпаргалка?
str, Copy-on-Write включён всегда, а вызовы, удалённые в pandas 3 (applymap, fillna(method=...), append, псевдонимы частоты M и H), есть только в последней таблице, рядом с тем, что писать вместо них. Почти все примеры работают и в pandas 2.2, кроме строк, которые опираются на dtype str или на Copy-on-Write.Чем loc отличается от iloc?
loc выбирает по меткам, то есть по значениям индекса и именам столбцов, и срез вроде df.loc[0:2] включает свой конец. iloc выбирает по целочисленной позиции, как список в Python, поэтому df.iloc[0:2] возвращает первые две строки и конец не включает. С помощью loc с условием можно за один шаг отфильтровать строки и выбрать столбцы: df.loc[df["age"] > 30, ["name"]].Как отфильтровать DataFrame по нескольким условиям?
& для «и», | для «или» и ~ для «не»: df[(df["age"] > 30) & (df["city"] == "Oslo")]. Питоновские and и or с целыми столбцами не работают. Длинные фильтры удобнее читать в виде df.query("age > 30 and city == 'Oslo'").Чем отличаются merge, join и concat?
merge объединяет два DataFrame по ключевым столбцам, как JOIN в SQL, с параметром how="inner", "left", "right" или "outer". join это сокращение для объединения по индексу. concat вообще не сопоставляет ключи: он ставит таблицы одну под другой или, с axis=1, рядом друг с другом.Почему присваивание не меняет DataFrame?
df["age"][0] = 37 меняет временный объект, а df остаётся прежним. Пиши это одним вызовом loc: df.loc[0, "age"] = 37. Методы вроде dropna и fillna тоже возвращают новый объект, так что присваивай результат.