- BrainTools - https://www.braintools.ru -
Давайте начистоту. Если вы когда-нибудь пытались анализировать табличные данные с помощью стандартных списков и словарей Питона, вы знаете, какая это боль [1]. Циклы внутри циклов, куча проверок на пустоту, простыни кода ради простейшей группировки…
А если вы пробовали открыть CSV-файл на пару-тройку миллионов строк в обычном Excel — ну, вы наверняка помните этот зависший белый экран и звук взлетающего кулера.
Так вот, Pandas — это, по сути, Excel на максималках. Это швейцарский нож для любых табличных данных. Библиотека берет вашу таблицу, загружает ее в оперативную память [2] и позволяет вертеть ей как угодно: фильтровать, искать аномалии, считать статистику, заполнять пропуски и склеивать разные файлы воедино за доли секунды. И всё это — буквально парой строчек элегантного кода.
В этой статье я собрал самую выжимку — базовый пайплайн, которого хватит для старта. А если после прочтения вам захочется погрузиться в тему серьезнее, порешать задачки и набить руку на реальных датасетах, заходите на мой бесплатный курс «Pandas для анализа данных: Полный курс» на Stepik [3]. Там мы разбираем всё: от основ до продвинутых фишек.
Установка и импорт
Хватит лирики, давайте к делу. Если Pandas у вас еще не установлен (хотя в сборках вроде Anaconda он идет из коробки), открываем терминал и пишем классическое:
pip install pandas
Готово. Теперь открываем ваш любимый редактор или Jupyter Notebook.
В мире Python есть негласное, но абсолютно железобетонное правило: никто не пишет просто import pandas. Все разработчики в мире договорились использовать сокращение (алиас) pd. Считайте это невидимым контрактом, который вы подписываете при входе в анализ данных:
import pandas as pd
Вся магия 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): Обратите внимание [5] на цифры 0, 1, 2, 3 с левого края. Мы их не создавали, Pandas добавил их сам. Это номера строк — вертикальный адрес данных.
Почему я так акцентирую на этом внимание? Потому что вся навигация по таблице, фильтрация и поиск значений в Pandas строится на пересечении Колонок и Индексов. Это как игра в морской бой: чтобы найти нужную ячейку, вам нужно назвать ряд и колонку.
Вбивать словари руками — это, конечно, весело на этапе обучения [6], но в реальной жизни (или как говорят айтишники, «в бою») данные приходят к нам в виде файлов. Десятки, сотни мегабайт, а иногда и гигабайты текста. И здесь 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… В мире дата-сайенса это классический мем. Так что просто возьмите в привычку: сохраняете данные — отключайте экспорт индекса (если только он не несет в себе какой-то полезной информации вроде дат).
Итак, вы загрузили файл. Отлично.
Первое негласное правило любого аналитика или дата-сайентиста: сначала посмотри на данные глазами. Вдруг там съехали колонки? Вдруг вместо чисел пришел текст? А может, половина таблицы вообще пустая? Чтобы не получить мусор на выходе, в 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 руб» буквами). Математика [7] на этой колонке сломается, ее придется чистить.
Оценка масштаба катастрофы с пропусками: Обратите внимание на столбик 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), чтобы один Билл Гейтс не исказил статистику по доходам всего бара.
Всего три короткие строчки кода — а вы уже знаете о своих данных больше, чем человек, который собирал этот файл!
Итак, мы посмотрели на данные в целом. Но в реальности вам редко нужна вся таблица целиком. Обычно датасеты содержат десятки колонок, а для отчета вам нужны только три. Или вам нужно вытащить данные только по одному конкретному отделу.
В Pandas есть несколько способов «нарезать» таблицу. На первых порах синтаксис может показаться непривычным, но к нему быстро привыкаешь.
Выбор колонок: Одинарные или двойные скобки?
Чтобы достать из датафрейма одну колонку, мы обращаемся к ней по имени в квадратных скобках, прямо как по ключу в словаре:
names = df['Имя']
На выходе вы получите тот самый одномерный объект Series.
Но что, если нам нужны две колонки? Имя и Зарплата? И вот тут 99% новичков совершают свою первую ошибку [8], пытаясь написать 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...])?
Всё логично [9]:
Внутренняя часть df['Кофе_чашек_в_день'] > 3 пробегается по каждой строке и возвращает список ответов: [True, False, True, False].
Внешние скобки df[...] применяют этот список к таблице, оставляя только те строки, где выпало True.
Сложные условия
А если условий несколько? Мы склеиваем их с помощью логических операторов & (И) или | (ИЛИ).
Важнейший нюанс: в Pandas каждое условие обязательно нужно брать в круглые скобки, иначе Питон запутается в приоритетах операций и выдаст ошибку.
# Ищем богатых Питонистов
rich_pythons = df[
(df['Роль'] == 'Python Dev') &
(df['Зарплата_руб'] > 200000)
]
Вот так, без единого цикла for и десятков if/else, мы в одну строчку отфильтровали огромную таблицу по сложным критериям. Работает моментально даже на миллионах строк.
Реальные данные никогда не бывают идеальными. Они приходят с опечатками, пустыми ячейками, дубликатами и странными названиями колонок. Если вы скормите такой “грязный” датасет алгоритму машинного обучения или попытаетесь построить красивый отчет — получите мусор на выходе.
Поэтому 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()
Если бы мне сказали: «Оставь в 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 # Чем заполнить дыры, если аналитиков в Питере нет
)
На выходе мы получим шикарную двумерную таблицу, где на пересечении строк (Ролей) и колонок (Городов) будут стоять средние зарплаты.
В реальной жизни данные редко лежат в одном удобном файле. Обычно у вас есть таблица с заказами (где указаны только 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', чтобы просто подтянуть справочную информацию к основной таблице транзакций или логов.
Освоить базовые методы Pandas — это только половина дела. Вторая половина (и именно она отличает джуна от мидла) — это умение писать код так, чтобы он работал быстро и читался легко. Pandas позволяет решить одну и ту же задачу десятью разными способами, но девять из них заставят ваш ноутбук дымиться.
Вот три главных правила выживания.
Главный смертный грех: Никогда не используйте циклы for
Если вы пришли из обычного Питона, ваш мозг [10] запрограммирован решать задачи перебором. Нужно изменить зарплату всем сотрудникам? Рука сама тянется написать что-то вроде:
# УЖАСНО: Никогда так не делайте в 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)
)
Посмотрите на этот блок! Он читается сверху вниз, как рецепт пирога: «Взять данные -> удалить пустые -> отфильтровать взрослых -> сгруппировать по городу -> найти среднюю зарплату -> отсортировать по убыванию». Никаких лишних переменных, никакого мусора в памяти, идеальная читаемость. Ваши коллеги скажут вам спасибо.
Давайте будем честны: как только вы уверенно освоите Pandas, открывать Excel для чего-то сложнее списка покупок вам больше не захочется. Да, поначалу синтаксис со всеми этими квадратными скобками, loc и лямбда-функциями может казаться перегруженным. Но кривая обучения окупается сполна.
Мы пробежались по самым верхам, но поверьте: то, что мы разобрали в этой статье (чтение I/O, фильтрация, очистка от пропусков, groupby и слияния) — это та самая база, которая закрывает 90% повседневных задач по работе с таблицами. Будь вы дата-сайентистом, бэкендером или маркетологом, которому скинули «эксельку на 5 гигов и попросили быстро свести стату» — этого арсенала вам хватит с головой.
Анонсы новых статей, полезные материалы, а так же если в процессе у вас возникнут сложности, обсудить их или задать вопрос по этой статье можно в моём Telegram-сообществе [11]. Смело заходите, если что-то пойдет не так, — постараемся разобраться вместе.
Автор: enamored_poc
Источник [12]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/26398
URLs in this post:
[1] боль: http://www.braintools.ru/article/9901
[2] память: http://www.braintools.ru/article/4140
[3] бесплатный курс «Pandas для анализа данных: Полный курс» на Stepik: https://stepik.org/course/275620/promo
[4] Image: https://sourcecraft.dev/
[5] внимание: http://www.braintools.ru/article/7595
[6] обучения: http://www.braintools.ru/article/5125
[7] Математика: http://www.braintools.ru/article/7620
[8] ошибку: http://www.braintools.ru/article/4192
[9] логично: http://www.braintools.ru/article/7640
[10] мозг: http://www.braintools.ru/parts-of-the-brain
[11] моём Telegram-сообществе: https://t.me/+NlTdqmVuBkIzMDBi
[12] Источник: https://habr.com/ru/articles/1005114/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1005114
Нажмите здесь для печати.