1. Введение и быстрый старт: Excel на максималках
Давайте начистоту. Если вы когда-нибудь пытались анализировать табличные данные с помощью стандартных списков и словарей Питона, вы знаете, какая это боль. Циклы внутри циклов, куча проверок на пустоту, простыни кода ради простейшей группировки…
А если вы пробовали открыть CSV-файл на пару-тройку миллионов строк в обычном Excel — ну, вы наверняка помните этот зависший белый экран и звук взлетающего кулера.
Так вот, Pandas — это, по сути, Excel на максималках. Это швейцарский нож для любых табличных данных. Библиотека берет вашу таблицу, загружает ее в оперативную память и позволяет вертеть ей как угодно: фильтровать, искать аномалии, считать статистику, заполнять пропуски и склеивать разные файлы воедино за доли секунды. И всё это — буквально парой строчек элегантного кода.
В этой статье я собрал самую выжимку — базовый пайплайн, которого хватит для старта. А если после прочтения вам захочется погрузиться в тему серьезнее, порешать задачки и набить руку на реальных датасетах, заходите на мой бесплатный курс «Pandas для анализа данных: Полный курс» на Stepik. Там мы разбираем всё: от основ до продвинутых фишек.
Установка и импорт
Хватит лирики, давайте к делу. Если Pandas у вас еще не установлен (хотя в сборках вроде Anaconda он идет из коробки), открываем терминал и пишем классическое:
pip install pandas
Готово. Теперь открываем ваш любимый редактор или Jupyter Notebook.
В мире Python есть негласное, но абсолютно железобетонное правило: никто не пишет просто import pandas. Все разработчики в мире договорились использовать сокращение (алиас) pd. Считайте это невидимым контрактом, который вы подписываете при входе в анализ данных:
import pandas as pd
2. Основы: Series и DataFrame (Из чего состоят таблицы)
Вся магия Pandas строится на двух базовых «детальках Лего». Если вы поймете, как они работают, всё остальное пойдет как по маслу.
Series (Серия): колонка
Представьте себе обычный питоновский список: [10, 20, 30]. А теперь представьте, что к каждому элементу этого списка приклеили этикетку с адресом. Это и есть Series — одномерный массив данных. По сути, это просто одна колонка из таблицы.
Главное отличие Series от обычного списка — наличие индекса (тех самых этикеток). По умолчанию это просто порядковые номера (0, 1, 2…), но вы можете сделать индексами хоть даты, хоть имена, хоть буквы алфавита.
DataFrame (Датафрейм): царь-таблица
А вот теперь берем несколько Series одинаковой длины, ставим их рядом, плечом к плечу, и даем каждому имя. Поздравляю, вы получили DataFrame.
DataFrame — это двумерная таблица, с которой вы и будете работать 99% времени. Это ваш рабочий лист Excel, таблица из SQL-базы или загруженный CSV-файл.
Чтобы не быть голословными, давайте создадим DataFrame прямо сейчас. В реальной жизни вы редко будете набивать данные руками (для этого есть чтение файлов, о котором в следующем пункте), но для тестов и понимания сути проще всего собрать датафрейм из обычного словаря (dict).
Давайте опишем небольшую IT-команду:
import pandas as pd
# Обычный питоновский словарь со списками внутри
team_data = {
'Имя': ['Алексей', 'Мария', 'Иван', 'Елена'],
'Роль': ['Python Dev', 'Data Analyst', 'DevOps', 'Product Manager'],
'Зарплата_руб': [250000, 180000, 220000, 300000],
'Кофе_чашек_в_день': [4, 2, 5, 1]
}
# Превращаем тыкву (словарь) в карету (DataFrame)
df = pd.DataFrame(team_data)
# Посмотрим, что получилось
print(df)
Вывод на экран будет выглядеть так:
Имя Роль Зарплата_руб Кофе_чашек_в_день
0 Алексей Python Dev 250000 4
1 Мария Data Analyst 180000 2
2 Иван DevOps 220000 5
3 Елена Product Manager 300000 1
Разбираем архитектуру
Посмотрите на вывод выше. В датафрейме есть три важнейшие сущности, которые нужно знать в лицо:
-
Данные (Data): Сама таблицы — имена, цифры, роли.
-
Колонки (Columns): Заголовки сверху. В нашем случае это
'Имя', 'Роль', .... Колонки — это горизонтальный адрес данных. -
Индекс (Index): Обратите внимание на цифры
0, 1, 2, 3с левого края. Мы их не создавали, Pandas добавил их сам. Это номера строк — вертикальный адрес данных.
Почему я так акцентирую на этом внимание? Потому что вся навигация по таблице, фильтрация и поиск значений в Pandas строится на пересечении Колонок и Индексов. Это как игра в морской бой: чтобы найти нужную ячейку, вам нужно назвать ряд и колонку.
3. Загрузка и выгрузка данных (I/O)
Вбивать словари руками — это, конечно, весело на этапе обучения, но в реальной жизни (или как говорят айтишники, «в бою») данные приходят к нам в виде файлов. Десятки, сотни мегабайт, а иногда и гигабайты текста. И здесь Pandas раскрывается во всей красе.
Чтение данных: как втянуть файл в память
Самый популярный формат обмена данными — это старый добрый CSV (Comma-Separated Values). Чтобы загрузить его в Pandas, нужна ровно одна строчка кода. Эту команду в мире, наверное, запускают миллион раз в секунду:
df = pd.read_csv('my_data.csv')
Всё! Датафрейм готов. Но есть нюанс. В идеальном мире все CSV-файлы разделены запятыми. В реальном мире кто-то выгрузил файл из 1С, кто-то из русской версии Excel, и вместо запятой там точка с запятой (;), а то и вообще табуляция. Если вы просто сделаете read_csv, Pandas свалит всё в одну нечитаемую колонку.
Поэтому нужно знать два главных параметра:
-
sep(от слова separator). Явно указываем разделитель. Если у вас русская экселька, пишем:pd.read_csv('data.csv', sep=';'). -
index_col. Часто бывает, что в первой колонке вашего файла уже лежат какие-нибудь айдишники (например,user_id). Если файл загрузить как есть, Pandas создаст свой индекс (0, 1, 2…), аuser_idсделает обычной колонкой. Чтобы сказать библиотеке: «Эй, используй первую колонку как индекс!», пишем:pd.read_csv('data.csv', index_col=0).
А как же Excel?
Если вам прислали файл .xlsx, не беда. Метод называется ожидаемо:
df = pd.read_excel('report.xlsx', sheet_name='Sheet1')
Маленький совет: чтобы read_excel работал, у вас должна быть установлена библиотека openpyxl (ставится так же через pip). Параметр sheet_name позволяет указать имя конкретной вкладки, если их в файле несколько.
Сохранение результатов: Золотое правило index=False
Допустим, вы загрузили данные, почистили их, посчитали всё, что нужно, и теперь хотите отправить результат коллеге. Нам нужно выгрузить датафрейм обратно в файл. Делается это методами to_csv() или to_excel().
df.to_csv('clean_data.csv', index=False)
Стоп. Почему index=False?
Запомните этот параметр, он спасет вам кучу нервов. Помните те самые номера строк (0, 1, 2, 3), которые Pandas автоматически приклеивает слева? Если вы не напишете index=False, Pandas сохранит эти цифры в файл как новую безымянную колонку.
Когда ваш коллега завтра прочитает этот файл, Pandas добавит еще один индекс. У него появится колонка Unnamed: 0. Если он сохранит файл и пришлет вам, у вас будет Unnamed: 0.1… В мире дата-сайенса это классический мем. Так что просто возьмите в привычку: сохраняете данные — отключайте экспорт индекса (если только он не несет в себе какой-то полезной информации вроде дат).
4. Знакомство с данными (Первичный осмотр)
Итак, вы загрузили файл. Отлично.
Первое негласное правило любого аналитика или дата-сайентиста: сначала посмотри на данные глазами. Вдруг там съехали колонки? Вдруг вместо чисел пришел текст? А может, половина таблицы вообще пустая? Чтобы не получить мусор на выходе, в Pandas есть три метода для быстрой и элегантной разведки (так называемый EDA — Exploratory Data Analysis).
Методы .head() и .tail(): Посмотреть глазками
Как мы привыкли делать в Excel? Открываем файл и начинаем скроллить. В Pandas выводить на экран датафрейм из миллиона строк бессмысленно (он всё равно покажет только начало и конец, скрыв середину за многоточием).
Чтобы быстро провести sanity check (проверку на адекватность), мы просим Pandas показать нам “голову” (начало) или “хвост” (конец) таблицы:
# Смотрим первые 5 строк (по умолчанию)
df.head()
# Смотрим последние 3 строки (можно передать любое число)
df.tail(3)
Этого достаточно, чтобы понять: ага, разделители сработали правильно, заголовки на месте, данные похожи на правду.
Метод .info(): Рентген вашего датафрейма
Если .head() — это беглый визуальный осмотр пациента, то .info() — это глубокий рентген. Это абсолютно незаменимый метод, который нужно вызывать каждый раз, когда вы видите датасет впервые.
df.info()
Вывод этого метода не очень красивый, это просто текст, но в нем кроется тонна ответов:
-
Размер таблицы: сколько всего строк (Index) и колонок (Columns).
-
Типы данных (Dtype): Pandas автоматически пытается угадать тип данных в колонке.
int64— целые числа,float64— числа с точкой. Если вы видитеobject— обычно это текст. Сразу лайфхак: если вы ждали колонку с ценой в числах, а.info()говорит, что тамobject— значит, в данные затесался мусор (например, кто-то написал «1000 руб» буквами). Математика на этой колонке сломается, ее придется чистить. -
Оценка масштаба катастрофы с пропусками: Обратите внимание на столбик
Non-Null Count. Если всего в таблице 1000 строк, а в колонке “Возраст” стоит850 non-null— значит, у 150 человек возраст не указан (там лежат значения NaN). Морально готовьтесь с этим разбираться. -
Потребление памяти (Memory usage): Сколько мегабайт ваш датафрейм отъедает в оперативке.
Метод .describe(): Статистика для ленивых
Допустим, с типами данных всё окей, числа распознались как числа. Теперь хочется понять масштаб значений. Какая средняя зарплата в датасете? Кто самый молодой? Какой разброс цен?
Вместо того чтобы писать руками формулы для каждой колонки, мы используем “волшебную кнопку”:
df.describe()
Pandas сам пробежится по таблице, найдет все числовые колонки (текстовые он проигнорирует) и выдаст по ним готовую статистическую сводку:
-
count— количество заполненных (непустых) ячеек. -
mean— среднее арифметическое. -
std— стандартное отклонение (показывает, насколько сильно данные “размазаны” вокруг среднего). -
minиmax— минимумы и максимумы (сразу видно аномалии: например, еслиmaxвозраст равен 999 — кто-то ошибся при вводе). -
25%, 50%, 75%— квартили. Кстати, 50% — это медиана. Опытные аналитики всегда смотрят на нее, а не на среднее (mean), чтобы один Билл Гейтс не исказил статистику по доходам всего бара.
Всего три короткие строчки кода — а вы уже знаете о своих данных больше, чем человек, который собирал этот файл!
5. Индексация и фильтрация (Как достать то, что нужно)
Итак, мы посмотрели на данные в целом. Но в реальности вам редко нужна вся таблица целиком. Обычно датасеты содержат десятки колонок, а для отчета вам нужны только три. Или вам нужно вытащить данные только по одному конкретному отделу.
В Pandas есть несколько способов «нарезать» таблицу. На первых порах синтаксис может показаться непривычным, но к нему быстро привыкаешь.
Выбор колонок: Одинарные или двойные скобки?
Чтобы достать из датафрейма одну колонку, мы обращаемся к ней по имени в квадратных скобках, прямо как по ключу в словаре:
names = df['Имя']
На выходе вы получите тот самый одномерный объект Series.
Но что, если нам нужны две колонки? Имя и Зарплата? И вот тут 99% новичков совершают свою первую ошибку, пытаясь написать df['Имя', 'Зарплата']. Pandas выдаст злющую ошибку.
Правильно делать так: вы передаете внутрь квадратных скобок список нужных колонок. То есть появляются двойные скобки:
# Обратите внимание на [[ ]]
subset = df[['Имя', 'Зарплата_руб']]
Вуаля! На выходе вы получили новый, аккуратненький DataFrame только с двумя колонками. Запомните: одинарные скобки — для одной колонки (вернут Series), двойные скобки — для нескольких (вернут DataFrame).
По строкам: Вечная путаница loc и iloc
Если с колонками всё просто, то выбор конкретных строк — это главная драма для тех, кто только изучает Pandas. Есть два метода, которые делают вроде бы одно и то же, но по-разному. Давайте раз и навсегда закроем этот вопрос.
-
iloc(integer location) — поиск по порядковому номеру.
Представьте, что это слепой робот. Ему всё равно, как называются ваши строки (какой у них индекс). Он понимает только координаты: «дай мне первую строку» или «дай мне строки с 10-й по 20-ю».# Получить самую первую строку в таблице (индекс 0) df.iloc[0] # Получить первые 5 строк (работают обычные питоновские срезы!) df.iloc[0:5] -
loc(location) — поиск по имени (метке).
А это уже умный курьер, который ищет по адресу. Он смотрит на ваш Индекс (те самые названия строк слева). Если ваш индекс — это даты, вы ищете по датам. Если имена — по именам.# Допустим, мы сделали колонку 'Имя' индексом таблицы df.set_index('Имя', inplace=True) # Теперь мы можем достать данные конкретного человека по метке! df.loc['Алексей']
Краткое правило: iloc — это всегда математическая позиция (0, 1, 2…). loc — это то, что реально написано в Индексе.
Булева фильтрация: Магия условий
Доставать строки по номерам — это скучно. Самая частая задача звучит так: «А выведи-ка мне всех разработчиков старше 18 лет, у которых зарплата больше 200 тысяч». В SQL для этого есть WHERE. В Excel — фильтры по колонкам. В Pandas есть Boolean Indexing (булева фильтрация).
Выглядит эта магия так:
# Хотим найти тех, кто пьет больше 3 чашек кофе
hard_workers = df[df['Кофе_чашек_в_день'] > 3]
Почему конструкция выглядит как масло масляное (df[df...])?
Всё логично:
-
Внутренняя часть
df['Кофе_чашек_в_день'] > 3пробегается по каждой строке и возвращает список ответов:[True, False, True, False]. -
Внешние скобки
df[...]применяют этот список к таблице, оставляя только те строки, где выпалоTrue.
Сложные условия
А если условий несколько? Мы склеиваем их с помощью логических операторов & (И) или | (ИЛИ).
Важнейший нюанс: в Pandas каждое условие обязательно нужно брать в круглые скобки, иначе Питон запутается в приоритетах операций и выдаст ошибку.
# Ищем богатых Питонистов
rich_pythons = df[
(df['Роль'] == 'Python Dev') &
(df['Зарплата_руб'] > 200000)
]
Вот так, без единого цикла for и десятков if/else, мы в одну строчку отфильтровали огромную таблицу по сложным критериям. Работает моментально даже на миллионах строк.
6. Трансформация и очистка данных (Уборка перед анализом)
Реальные данные никогда не бывают идеальными. Они приходят с опечатками, пустыми ячейками, дубликатами и странными названиями колонок. Если вы скормите такой “грязный” датасет алгоритму машинного обучения или попытаетесь построить красивый отчет — получите мусор на выходе.
Поэтому 80% времени аналитика занимает именно очистка данных (Data Cleaning). В Pandas для этого есть свой арсенал швабр и пылесосов.
Пропуски (NaN): Найти и обезвредить
Самая частая боль — пустые ячейки. В базе данных это NULL, а в Pandas — загадочный NaN (Not a Number). Любая математическая операция с NaN (например, попытка сложить зарплату и NaN) вернет NaN. Это как черная дыра, которая съедает ваши расчеты.
Что с ними делать?
-
Шаг 1: Найти.
Сначала нужно понять масштаб бедствия. Методisna()возвращает таблицу такого же размера, где вместо значений стоятTrue(если пусто) илиFalse. Но смотреть на огромную таблицу изTrue/Falseбессмысленно. Поэтому мы сразу просим посчитать сумму пропусков в каждой колонке:# Выдаст количество NaN в каждой колонке df.isna().sum() -
Шаг 2: Принять решение (Удалить или Заполнить?).
Если пропусков мало (например, 5 строк из 10 000), проще всего их удалить. Методdropna()по умолчанию удалит любую строку, в которой есть хотя бы одинNaN.# Жестко удаляем все строки с пропусками clean_df = df.dropna()Но если у вас пустая колонка “Второй телефон” у половины базы, удалять эти строки нельзя — вы потеряете кучу ценных клиентов! В этом случае пропуски лучше заполнить чем-то нейтральным: нулями, словом “Не указан” или, например, средней зарплатой по отделу.
# Заполняем пропуски в колонке 'Зарплата' нулями df['Зарплата_руб'] = df['Зарплата_руб'].fillna(0) # Или словом 'Unknown' для текстовых колонок df['Роль'] = df['Роль'].fillna('Unknown')
Создание новых колонок: Векторизация в действии
Часто нам нужно посчитать что-то новое на основе того, что уже есть. Например, перевести зарплату из рублей в доллары или посчитать общую выручку (цена × количество).
Если вы пришли из обычного Питона, ваша рука инстинктивно потянется написать цикл for, который побежит по строкам. Бейте себя по рукам! В Pandas циклы работают возмутительно медленно.
Вместо этого мы используем магию векторизации — применяем математику сразу ко всей колонке разом:
# Допустим, курс доллара 90.
# Pandas сам разделит КАЖДОЕ значение в колонке на 90 за долю секунды.
df['Зарплата_USD'] = df['Зарплата_руб'] / 90
# Или перемножим две колонки между собой (строка за строкой)
df['total_price'] = df['price'] * df['qty']
Синтаксис до смешного прост: вы просто объявляете имя новой колонки в квадратных скобках (как новый ключ в словаре) и присваиваете ей результат вычислений.
Наведение красоты: Переименование и Дубликаты
Иногда колонки называются так, что без слез не взглянешь: usr_nm_1, sal_rub_gross. Работать с этим неудобно. Переименовать колонки можно через словарь, где ключ — старое имя, а значение — новое.
# Метод rename принимает словарь с заменами
df = df.rename(columns={
'Имя': 'name',
'Зарплата_руб': 'salary'
})
И, наконец, классика жанра — кто-то случайно нажал кнопку “Отправить” дважды, и в базе появились одинаковые строки. Чтобы не задваивать статистику, сносим дубликаты одним мощным ударом:
# Оставляет только первое вхождение, остальные удаляет
df = df.drop_duplicates()
7. Группировка и агрегация (The Power of GroupBy)
Если бы мне сказали: «Оставь в Pandas только одну функцию, а остальные мы удалим», я бы, не задумываясь, выбрал .groupby(). Это альфа и омега любого анализа данных.
С помощью группировки сырые, бессмысленные строчки логов превращаются в бизнес-метрики. Ответить на вопросы вроде «Какой отдел тратит больше всего денег?», «В каком месяце у нас просели продажи?» или «Сколько в среднем живут пользователи из разных стран?» без нее просто невозможно.
Метод .groupby(): Принцип Split-Apply-Combine
Под капотом группировка работает по элегантному принципу «Раздели – Примени – Соедини».
-
Split (Разделение): Pandas берет вашу огромную таблицу и разбивает ее на маленькие кучки по уникальным значениям в выбранной колонке. Например, кучка записей “Отдел Продаж”, кучка “IT”, кучка “HR”.
-
Apply (Применение): К каждой из этих кучек применяется какая-то математическая функция (сумма, среднее, количество).
-
Combine (Склейка): Результаты собираются обратно в новую, компактную табличку.
Звучит сложно, пишется в одну строку. Давайте посчитаем среднюю зарплату по ролям:
# 1. Группируем по колонке 'Роль'
# 2. Выбираем колонку, которую хотим посчитать ('Зарплата_руб')
# 3. Применяем функцию среднего (mean)
average_salary = df.groupby('Роль')['Зарплата_руб'].mean()
print(average_salary)
Вывод будет выглядеть примерно так (это объект Series, где индексом стали названия ролей):
Роль
Data Analyst 180000.0
DevOps 220000.0
Product Manager 300000.0
Python Dev 250000.0
А что, если мы хотим посчитать не только среднее, но и сумму, и количество сотрудников в каждом отделе? Писать три разных groupby? Ни в коем случае! Используем мощнейший метод .agg() (от слова aggregation):
# Передаем список нужных функций
stats = df.groupby('Роль')['Зарплата_руб'].agg(['mean', 'sum', 'count'])
На выходе вы получите красивый датафрейм, где по строкам будут роли, а по колонкам — среднее, сумма и количество. Это уже готовый кусок отчета для руководства.
Сводные таблицы: pd.pivot_table() (Любимая игрушка аналитиков)
Если вы пришли из Excel, то при слове “Сводная таблица” (Pivot Table) у вас наверняка теплеет на душе. Это когда вы перетаскиваете поля в строчки и колонки, и магия случается сама собой.
В Pandas есть полный аналог этой функции — метод pivot_table. По сути, это тот же groupby, но с возможностью разложить данные не только по вертикали (строкам), но и по горизонтали (колонкам). Это нужно, когда вы хотите посмотреть на данные в разрезе двух и более параметров одновременно.
Допустим, у нас в таблице есть не только Роли, но и Города (Москва, Питер). Мы хотим посмотреть среднюю зарплату аналитиков в Москве и Питере в виде матрицы:
# Строим матрицу: Роль vs Город
pivot = pd.pivot_table(
data=df, # Наша таблица
values='Зарплата_руб', # Что считаем
index='Роль', # Что пойдет в строки
columns='Город', # Что пойдет в столбцы
aggfunc='mean', # Как считаем (по умолчанию и так mean)
fill_value=0 # Чем заполнить дыры, если аналитиков в Питере нет
)
На выходе мы получим шикарную двумерную таблицу, где на пересечении строк (Ролей) и колонок (Городов) будут стоять средние зарплаты.
8. Объединение датафреймов (Франкенштейн из таблиц)
В реальной жизни данные редко лежат в одном удобном файле. Обычно у вас есть таблица с заказами (где указаны только ID товаров) и отдельная таблица со справочником товаров (где лежат их названия и цены). Или вам каждый месяц присылают новый файл с продажами, и к концу года у вас их ровно 12.
Чтобы собрать из этого зоопарка единую картину, в Pandas есть два главных инструмента склейки. Один работает “вниз” (по вертикали), другой — “вбок” (по горизонтали).
Вертикальное склеивание: pd.concat() (Пришить новые строки снизу)
Представьте, что у вас есть два датафрейма с абсолютно одинаковой структурой (одни и те же колонки). Например, df_jan (продажи за январь) и df_feb (продажи за февраль). Вам нужно просто дописать февральские строки в конец январских.
Для этого используется функция pd.concat() (от слова concatenation — сцепление). Она принимает список датафреймов, которые нужно склеить:
# У нас есть две таблички: df_jan и df_feb
# Склеиваем их в одну большую df_all
df_all = pd.concat([df_jan, df_feb])
Важный нюанс: Помните, мы говорили про индексы (номера строк 0, 1, 2…)? При обычном concat() Pandas тупо приклеит вторую таблицу снизу вместе с её старыми индексами. В итоге у вас получится нумерация строк вида: 0, 1, 2, 0, 1, 2. Это чревато проблемами при поиске по индексу (loc).
Чтобы Pandas пересчитал индексы заново (0, 1, 2, 3, 4, 5), всегда добавляйте магический параметр ignore_index=True:
# Идеальное вертикальное склеивание
df_all = pd.concat([df_jan, df_feb], ignore_index=True)
Горизонтальное слияние: pd.merge() (Знакомьтесь, это SQL JOIN в Python)
А теперь более сложный и частый кейс. У вас есть таблица df_orders (Заказы), где есть колонка client_id (например, число 105). И есть таблица df_clients (Клиенты), где напротив client_id = 105 написано имя “Иван Иванов” и его email.
Вам нужно подтянуть имя и email клиента в таблицу с заказами. В Excel для этого пишут километровую формулу ВПР (VLOOKUP). В базах данных используют JOIN. В Pandas есть гениальная функция pd.merge().
# Приклеиваем справочник клиентов (df_clients) к заказам (df_orders)
df_full = pd.merge(
df_orders, # Левая таблица (основная)
df_clients, # Правая таблица (справочник, откуда тянем данные)
on='client_id', # По какой общей колонке ищем совпадения (ключ)
how='left' # Как именно склеивать (тип джойна)
)
Разбираемся с параметром how (Как склеивать?)
Параметр how определяет логику слияния, если в одной таблице есть ID, которого нет в другой. Это классическая теория множеств из SQL. Запомните 4 базовых сценария:
-
how='left'(Left Join — самый частый!). Берем все строки из левой таблицы (Заказы) и ищем для них совпадения в правой (Клиенты). Если клиент с таким ID не найден в справочнике — заказ всё равно останется в итоговой таблице, просто вместо имени и email там будет пустота (NaN). Это самый безопасный способ обогатить данные, ничего не потеряв. -
how='inner'(Inner Join — пересечение). Оставляет только те строки, которые одновременно есть и в левой, и в правой таблице. Если клиент сделал заказ, но его почему-то нет в справочнике — этот заказ безжалостно удалится из итоговой таблицы. Используйте с осторожностью, чтобы не потерять данные! -
how='right'(Right Join). То же самое, что и Left, только наоборот. Берем всех клиентов из справочника и приклеиваем к ним их заказы. Если клиент ничего не заказывал — он всё равно будет в таблице, но с пустыми колонками заказа. Применяется редко (проще поменять таблицы местами и сделатьleft). -
how='outer'(Full Outer Join — объединение всего). Берет вообще всё из обеих таблиц. Останутся и заказы без клиентов, и клиенты без заказов (с кучейNaNвезде, где нет совпадений).
Чаще всего (в 90% случаев) вы будете использовать именно how='left', чтобы просто подтянуть справочную информацию к основной таблице транзакций или логов.
9. Best Practices: Как писать код, за который не стыдно (и который не тормозит)
Освоить базовые методы Pandas — это только половина дела. Вторая половина (и именно она отличает джуна от мидла) — это умение писать код так, чтобы он работал быстро и читался легко. Pandas позволяет решить одну и ту же задачу десятью разными способами, но девять из них заставят ваш ноутбук дымиться.
Вот три главных правила выживания.
Главный смертный грех: Никогда не используйте циклы for
Если вы пришли из обычного Питона, ваш мозг запрограммирован решать задачи перебором. Нужно изменить зарплату всем сотрудникам? Рука сама тянется написать что-то вроде:
# УЖАСНО: Никогда так не делайте в Pandas!
for index, row in df.iterrows():
if row['role'] == 'Developer':
df.loc[index, 'salary'] = row['salary'] * 1.2
Знаете, что произойдет? На таблице в пару миллионов строк этот код будет работать минут пятнадцать. Pandas под капотом написан на C (через библиотеку NumPy), и когда вы используете питоновский цикл for, вы заставляете быстрый C-движок на каждой строчке спотыкаться и возвращать управление медленному Питону. Это как копать траншею чайной ложкой, имея за спиной экскаватор.
Решение — векторизация. Мы отдаем команду экскаватору сделать всё за один проход:
# ИДЕАЛЬНО: Отработает за миллисекунды
df.loc[df['role'] == 'Developer', 'salary'] *= 1.2
Бейте себя по рукам каждый раз, когда хотите написать for для обхода датафрейма. В 99% случаев для вашей задачи уже есть встроенный векторизованный метод.
Метод .apply(): Спасательный круг для хитрой логики
Но что делать в том самом 1% случаев? Допустим, у вас есть колонка с текстом отзыва клиента, и вам нужно прогнать каждый отзыв через какую-то вашу самописную функцию (например, сложный парсер или API-запрос к нейросети), которую встроенными методами не опишешь.
Для этого существует метод .apply(). Он берет вашу функцию и аккуратно применяет ее к каждой ячейке колонки.
# Наша хитрая самописная функция
def extract_domain(email):
if '@' in email:
return email.split('@')[1]
return 'unknown'
# Применяем функцию ко всей колонке
df['domain'] = df['email'].apply(extract_domain)
# Или то же самое через лямбда-функцию (для любителей однострочников)
df['domain'] = df['email'].apply(lambda x: x.split('@')[1] if '@' in x else 'unknown')
Важная оговорка: .apply() — это, по сути, замаскированный цикл for. Он работает быстрее, чем ручной перебор, но всё равно медленнее чистой векторизации. Используйте его только тогда, когда встроенных инструментов Pandas действительно не хватает.
Chain formatting (Цепочки методов): Пишем как поэты
Как обычно выглядит код новичка, который чистит данные? Это бесконечное создание промежуточных переменных:
# Грязный код (плохо читается, жрет память)
df_clean = df.dropna()
df_filtered = df_clean[df_clean['age'] > 18]
df_grouped = df_filtered.groupby('city')['salary'].mean()
df_final = df_grouped.sort_values(ascending=False)
В Pandas почти все методы возвращают новый датафрейм. А значит, мы можем прицеплять методы друг за другом, как вагоны поезда! Чтобы код не уехал за горизонт экрана, мы оборачиваем всё выражение в круглые скобки. Это позволяет Питону игнорировать переносы строк, и мы можем красиво писать каждый метод с новой строки:
# Чистый код (Method Chaining)
df_final = (
df.dropna()
.query('age > 18') # Альтернатива булевой фильтрации, очень удобна в цепочках
.groupby('city')['salary']
.mean()
.sort_values(ascending=False)
)
Посмотрите на этот блок! Он читается сверху вниз, как рецепт пирога: «Взять данные -> удалить пустые -> отфильтровать взрослых -> сгруппировать по городу -> найти среднюю зарплату -> отсортировать по убыванию». Никаких лишних переменных, никакого мусора в памяти, идеальная читаемость. Ваши коллеги скажут вам спасибо.
10. Заключение: Обратного пути нет
Давайте будем честны: как только вы уверенно освоите Pandas, открывать Excel для чего-то сложнее списка покупок вам больше не захочется. Да, поначалу синтаксис со всеми этими квадратными скобками, loc и лямбда-функциями может казаться перегруженным. Но кривая обучения окупается сполна.
Мы пробежались по самым верхам, но поверьте: то, что мы разобрали в этой статье (чтение I/O, фильтрация, очистка от пропусков, groupby и слияния) — это та самая база, которая закрывает 90% повседневных задач по работе с таблицами. Будь вы дата-сайентистом, бэкендером или маркетологом, которому скинули «эксельку на 5 гигов и попросили быстро свести стату» — этого арсенала вам хватит с головой.
Анонсы новых статей, полезные материалы, а так же если в процессе у вас возникнут сложности, обсудить их или задать вопрос по этой статье можно в моём Telegram-сообществе. Смело заходите, если что-то пойдет не так, — постараемся разобраться вместе.
Автор: enamored_poc


