[Перевод] Моя шпаргалка по pandas

Один преподаватель как-то сказал мне, что если поискать аналог программиста в мире книг, то окажется, что программисты похожи не на учебники, а на оглавления учебников: они не помнят всего, но знают, как быстро найти то, что им нужно.

Возможность быстро находить описания функций позволяет программистам продуктивно работать, не теряя состояния потока. Поэтому я и создал представленную здесь шпаргалку по pandas и включил в неё то, чем пользуюсь каждый день, создавая веб-приложения и модели машинного обучения.

geybiuurbhpsffn2pu04xhax_rq.jpeg

Нельзя сказать, что это — исчерпывающий список возможностей pandas, но сюда входят функции, которыми я пользуюсь чаще всего, примеры и мои пояснения по поводу ситуаций, в которых эти функции особенно полезны.

1. Подготовка к работе


Если вы хотите самостоятельно опробовать то, о чём тут пойдёт речь, загрузите набор данных Anime Recommendations Database с Kaggle. Распакуйте его и поместите в ту же папку, где находится ваш Jupyter Notebook (далее — блокнот).

Теперь выполните следующие команды.

import pandas as pd
import numpy as np
anime = pd.read_csv('anime-recommendations-database/anime.csv')
rating = pd.read_csv('anime-recommendations-database/rating.csv')
anime_modified = anime.set_index('name')


После этого у вас должна появиться возможность воспроизвести то, что я покажу в следующих разделах этого материала.

2. Импорт данных


▍Загрузка CSV-данных


Здесь я хочу рассказать о преобразовании CSV-данных непосредственно в датафреймы (в объекты Dataframe). Иногда при загрузке данных формата CSV нужно указывать их кодировку (например, это может выглядеть как encoding='ISO-8859–1'). Это — первое, что стоит попробовать сделать в том случае, если оказывается, что после загрузки данных датафрейм содержит нечитаемые символы.

anime = pd.read_csv('anime-recommendations-database/anime.csv')


d5b59f543d887c33b912e66642640d9e.png


Загруженные CSV-данные

Существует похожая функция для загрузки данных из Excel-файлов — pd.read_excel.

▍Создание датафрейма из данных, введённых вручную


Это может пригодиться тогда, когда нужно вручную ввести в программу простые данные. Например — если нужно оценить изменения, претерпеваемые данными, проходящими через конвейер обработки данных.

df = pd.DataFrame([[1,'Bob', 'Builder'],
                  [2,'Sally', 'Baker'],
                  [3,'Scott', 'Candle Stick Maker']], 
columns=['id','name', 'occupation'])


402bcb22ee99170d544f90fd64104289.png


Данные, введённые вручную

▍Копирование датафрейма


Копирование датафреймов может пригодиться в ситуациях, когда требуется внести в данные изменения, но при этом надо и сохранить оригинал. Если датафреймы нужно копировать, то рекомендуется делать это сразу после их загрузки.

anime_copy = anime.copy(deep=True)


edd7c8e25b0d16a143659ca1462a186e.png


Копия датафрейма

3. Экспорт данных


▍Экспорт в формат CSV


При экспорте данных они сохраняются в той же папке, где находится блокнот. Ниже показан пример сохранения первых 10 строк датафрейма, но то, что именно сохранять, зависит от конкретной задачи.

rating[:10].to_csv('saved_ratings.csv', index=False)


Экспортировать данные в виде Excel-файлов можно с помощью функции df.to_excel.

4. Просмотр и исследование данных


▍Получение n записей из начала или конца датафрейма


Сначала поговорим о выводе первых n элементов датафрейма. Я часто вывожу некоторое количество элементов из начала датафрейма где-нибудь в блокноте. Это позволяет мне удобно обращаться к этим данным в том случае, если я забуду о том, что именно находится в датафрейме. Похожую роль играет и вывод нескольких последних элементов.

anime.head(3)
rating.tail(1)


0f60619bb1a49bde2df4f171613dd193.png


Данные из начала датафрейма

85d2ad3933d300b6afbe8f3b3140f40d.png


Данные из конца датафрейма

▍Подсчёт количества строк в датафрейме


Функция len(), которую я тут покажу, не входит в состав pandas. Но она хорошо подходит для подсчёта количества строк датафреймов. Результаты её работы можно сохранить в переменной и воспользоваться ими там, где они нужны.

len(df)
#=> 3


▍Подсчёт количества уникальных значений в столбце


Для подсчёта количества уникальных значений в столбце можно воспользоваться такой конструкцией:

len(ratings['user_id'].unique())


▍Получение сведений о датафрейме


В сведения о датафрейме входит общая информация о нём вроде заголовка, количества значений, типов данных столбцов.

anime.info()


2f7ab56f1f7d431ef3e5f6ac7922c9a9.png


Сведения о датафрейме

Есть ещё одна функция, похожая на df.info — df.dtypes. Она лишь выводит сведения о типах данных столбцов.

▍Вывод статистических сведений о датафрейме


Знание статистических сведений о датафрейме весьма полезно в ситуациях, когда он содержит множество числовых значений. Например, знание среднего, минимального и максимального значений столбца rating даёт нам некоторое понимание того, как, в целом, выглядит датафрейм. Вот соответствующая команда:

anime.describe()


bf52c0d4f50d357a40871a6999f2335f.png


Статистические сведения о датафрейме

▍Подсчёт количества значений


Для того чтобы подсчитать количество значений в конкретном столбце, можно воспользоваться следующей конструкцией:

anime.type.value_counts()


3d251e47afdd01962810b0a1a8aa8859.png


Подсчёт количества элементов в столбце

5. Извлечение информации из датафреймов


▍Создание списка или объекта Series на основе значений столбца


Это может пригодиться в тех случаях, когда требуется извлекать значения столбцов в переменные x и y для обучения модели. Здесь применимы следующие команды:

anime['genre'].tolist()
anime['genre']


c199fbd4cf50623d5fd75ded8f1614cc.png


Результаты работы команды anime['genre'].tolist ()

e1f008ddfdb79459dad6f50421736c7b.png


Результаты работы команды anime['genre']

▍Получение списка значений из индекса


Поговорим о получении списков значений из индекса. Обратите внимание на то, что я здесь использовал датафрейм anime_modified, так как его индексные значения выглядят интереснее.

anime_modified.index.tolist()


db377e121b77d871acdf9d1bc737069b.png


Результаты выполнения команды

▍Получение списка значений столбцов


Вот команда, которая позволяет получить список значений столбцов:

anime.columns.tolist()


685c1860f536fba854754c956107e3a7.png


Результаты выполнения команды

6. Добавление данных в датафрейм и удаление их из него


▍Присоединение к датафрейму нового столбца с заданным значением


Иногда мне приходится добавлять в датафреймы новые столбцы. Например — в случаях, когда у меня есть тестовый и обучающий наборы в двух разных датафреймах, и мне, прежде чем их скомбинировать, нужно пометить их так, чтобы потом их можно было бы различить. Для этого используется такая конструкция:

anime['train set'] = True


▍Создание нового датафрейма из подмножества столбцов


Это может пригодиться в том случае, если требуется сохранить в новом датафрейме несколько столбцов огромного датафрейма, но при этом не хочется выписывать имена столбцов, которые нужно удалить.

anime[['name','episodes']]


8fe93102df79a85d02b6ef286049def5.png


Результат выполнения команды

▍Удаление заданных столбцов


Этот приём может оказаться полезным в том случае, если из датафрейма нужно удалить лишь несколько столбцов. Если удалять нужно много столбцов, то эта задача может оказаться довольно-таки утомительной, поэтому тут я предпочитаю пользоваться возможностью, описанной в предыдущем разделе.

anime.drop(['anime_id', 'genre', 'members'], axis=1).head()


c7fbd8d5dff65dce484d873b497f31dc.png


Результаты выполнения команды

▍Добавление в датафрейм строки с суммой значений из других строк


Для демонстрации этого примера самостоятельно создадим небольшой датафрейм, с которым удобно работать. Самое интересное здесь — это конструкция df.sum(axis=0), которая позволяет получать суммы значений из различных строк. 

df = pd.DataFrame([[1,'Bob', 8000],
                  [2,'Sally', 9000],
                  [3,'Scott', 20]], columns=['id','name', 'power level'])
df.append(df.sum(axis=0), ignore_index=True)


c7be91e10b1e31ee525dc26e44d2e9ea.png


Результат выполнения команды

Команда вида df.sum(axis=1) позволяет суммировать значения в столбцах.

Похожий механизм применим и для расчёта средних значений. Например — df.mean(axis=0).

7. Комбинирование датафреймов


▍Конкатенация двух датафреймов


Эта методика применима в ситуациях, когда имеются два датафрейма с одинаковыми столбцами, которые нужно скомбинировать.

В данном примере мы сначала разделяем датафрейм на две части, а потом снова объединяем эти части:

df1 = anime[0:2]
df2 = anime[2:4]
pd.concat([df1, df2], ignore_index=True)


9b934f17234b988ded660c608413ab45.png


Датафрейм df1

3c65bb666ff3c7389c2b35cd2d4b3029.png


Датафрейм df2

68ba5d883c0b79be8a35a82296ba9dd9.png


Датафрейм, объединяющий df1 и df2

▍Слияние датафреймов


Функция df.merge, которую мы тут рассмотрим, похожа на левое соединение SQL. Она применяется тогда, когда два датафрейма нужно объединить по некоему столбцу.

rating.merge(anime, left_on=’anime_id’, right_on=’anime_id’, suffixes=(‘_left’, ‘_right’))


f84b07bf1b7f835b21aebda6004a84e4.png


Результаты выполнения команды

8. Фильтрация


▍Получение строк с нужными индексными значениями


Индексными значениями датафрейма anime_modified являются названия аниме. Обратите внимание на то, как мы используем эти названия для выбора конкретных столбцов.

anime_modified.loc[['Haikyuu!! Second Season','Gintama']]


77426631ebae515bef71c75fde3bd77b.png


Результаты выполнения команды

▍Получение строк по числовым индексам


Эта методика отличается от той, которая описана в предыдущем разделе. При использовании функции df.iloc первой строке назначается индекс 0, второй — индекс 1, и так далее. Такие индексы назначаются строкам даже в том случае, если датафрейм был модифицирован и в его индексном столбце используются строковые значения.

Следующая конструкция позволяет выбрать три первых строки датафрейма:

anime_modified.iloc[0:3]


c775045fcf404c6e973a3bfd2aa32192.png


Результаты выполнения команды

▍Получение строк по заданным значениям столбцов


Для получения строк датафрейма в ситуации, когда имеется список значений столбцов, можно воспользоваться следующей командой:

anime[anime['type'].isin(['TV', 'Movie'])]


5d52f052b52c7054bd81c38bb2fe1128.png


Результаты выполнения команды

Если нас интересует единственное значение — можно воспользоваться такой конструкцией:

anime[anime[‘type’] == 'TV']


▍Получение среза датафрейма


Эта техника напоминает получение среза списка. А именно, речь идёт о получении фрагмента датафрейма, содержащего строки, соответствующие заданной конфигурации индексов.

anime[1:3]


04e4cae8384f7484c380066d6f13d3ed.png


Результаты выполнения команды

▍Фильтрация по значению


Из датафреймов можно выбирать строки, соответствующие заданному условию. Обратите внимание на то, что при использовании этого метода сохраняются существующие индексные значения.

anime[anime['rating'] > 8]


b2bc4a191c3178a18410ce592562b97a.png


Результаты выполнения команды

9. Сортировка


Для сортировки датафреймов по значениям столбцов можно воспользоваться функцией df.sort_values:

anime.sort_values('rating', ascending=False)


4caeae3c921af60b4d39fc9ef4947b43.png


Результаты выполнения команды

10. Агрегирование


▍Функция df.groupby и подсчёт количества записей


Вот как подсчитать количество записей с различными значениями в столбцах:

anime.groupby('type').count()


092795c51d4be09e6f0211d5b14ef8ce.png


Результаты выполнения команды

▍Функция df.groupby и агрегирование столбцов различными способами


Обратите внимание на то, что здесь используется reset_index(). В противном случае столбец type становится индексным столбцом. В большинстве случаев я рекомендую делать то же самое.

anime.groupby(["type"]).agg({
  "rating": "sum",
  "episodes": "count",
  "name": "last"
}).reset_index()


▍Создание сводной таблицы


Для того чтобы извлечь из датафрейма некие данные, нет ничего лучше, чем сводная таблица. Обратите внимание на то, что здесь я серьёзно отфильтровал датафрейм, что ускорило создание сводной таблицы.

tmp_df = rating.copy()
tmp_df.sort_values('user_id', ascending=True, inplace=True)
tmp_df = tmp_df[tmp_df.user_id < 10] 
tmp_df = tmp_df[tmp_df.anime_id < 30]
tmp_df = tmp_df[tmp_df.rating != -1]
pd.pivot_table(tmp_df, values='rating', index=['user_id'], columns=['anime_id'], aggfunc=np.sum, fill_value=0)


2912d74af64b3e114e543433d482eb6e.png


Результаты выполнения команды

11. Очистка данных


▍Запись в ячейки, содержащие значение NaN, какого-то другого значения


Здесь мы поговорим о записи значения 0 в ячейки, содержащие значение NaN. В этом примере мы создаём такую же сводную таблицу, как и ранее, но без использования fill_value=0. А затем используем функцию fillna(0) для замены значений NaN на 0.

pivot = pd.pivot_table(tmp_df, values='rating', index=['user_id'], columns=['anime_id'], aggfunc=np.sum)
pivot.fillna(0)


39562f618aba5dadee84f65c5eec1de6.png


Таблица, содержащая значения NaN

ca5c889ce4fcaeefcf6073a53a854800.png


Результаты замены значений NaN на 0

12. Другие полезные возможности


▍Отбор случайных образцов из набора данных


Я использую функцию df.sample каждый раз, когда мне нужно получить небольшой случайный набор строк из большого датафрейма. Если используется параметр frac=1, то функция позволяет получить аналог исходного датафрейма, строки которого будут перемешаны.

anime.sample(frac=0.25)


1960bb0441f16d1f5a637f53125b0133.png


Результаты выполнения команды

▍Перебор строк датафрейма


Следующая конструкция позволяет перебирать строки датафрейма:

for idx,row in anime[:2].iterrows():
    print(idx, row)


e3e1d6d24c6b3ef1797cbfb64dfebc6b.png


Результаты выполнения команды

▍Борьба с ошибкой IOPub data rate exceeded


Если вы сталкиваетесь с ошибкой IOPub data rate exceeded — попробуйте, при запуске Jupyter Notebook, воспользоваться следующей командой:

jupyter notebook — NotebookApp.iopub_data_rate_limit=1.0e10


Итоги


Здесь я рассказал о некоторых полезных приёмах использования pandas в среде Jupyter Notebook. Надеюсь, моя шпаргалка вам пригодится.

Уважаемые читатели! Есть ли какие-нибудь возможности pandas, без которых вы не представляете своей повседневной работы?

iqfib45pgphfrxv--zfemt0qnmw.jpeg

© Habrahabr.ru