6 способов оптимизировать рабочий процесс в Pandas

Благодаря своему продвинутому и гибкому функционалу, библиотека Pandas стала незаменимым инструментом для дата-сайентистов и дата-аналитиков.

Согласно PyPI, Pandas скачивают более 3 миллионов раз ежедневно. Конечно, подобная статистика не дает точного представления о количестве пользователей. Тем не менее она подчеркивает популярность библиотеки.

Хотя многие уже ознакомились с ее основными возможностями, есть еще много скрытых, о которых вы, вероятно, не знаете. Рассмотрим 6 интересных подходов в Pandas, которые помогут повысить эффективность анализа данных.


#1. Отображение графиков в столбце DataFrame

Jupyter  —  это IDE на веб-основе. Поэтому при выводе DataFrame он отображается с использованием HTML и CSS. Это позволяет форматировать вывод так же, как и любую другую веб-страницу.

Одним из интересных способов такого форматирования является вставка встроенных графиков, которые появляются в столбце DataFrame. Их также называют “спарклайнами” (“sparklines”). В итоге мы получаем нечто подобное:

Спарклайны в Pandas DataFrame (изображение автора)

Как это создать? Смотрите код ниже.

Сначала выполним импорт:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from base64 import b64encode
from io import BytesIO
from IPython.display import HTML

%matplotlib inline

Теперь создадим выдуманный набор данных:

n = 100

data = [
    ('Bitcoin',   40000*np.random.rand(n).round(2)), 
    ('Ethereum',   2000*np.random.rand(n).round(2)), 
    ('BNB',     500*np.random.rand(n).round(2)),
    ('Litecoin',   150*np.random.rand(n).round(2)),
    
]
df = pd.DataFrame(data, columns=['Name', 'Price History'])
df.head()
       Name                                      Price History
0   Bitcoin   [24800.0, 12400.0, 14800.0, 24800.0, 20800.0, ...
1 Ethereum [1900.0, 380.0, 420.0, 1760.0, 800.0, 620.0, 1...
2       BNB   [120.0, 170.0, 255.0, 255.0, 395.0, 150.0, 180...
3  Litecoin   [126.0, 109.5, 94.5, 49.5, 81.0, 129.0, 66.0, ...

В соответствии с четырьмя строками у нас есть список случайно сгенерированных историй цен. Теперь наша цель  —  добавить линейный график в каждую строку. Таким образом, мы можем создать функцию и использовать метод apply().

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

Вот код, который мы для этого используем:

def create_line(data, **kwags):

# Преобразование данных в список
data = list(data)

# Создание объекта фигуры и оси с заданным размером и аргументами ключевых слов
fig, ax = plt.subplots(1, 1, figsize=(3, 0.25), **kwags)

# Построение графика из данных
ax.plot(data)

# Удаление границ в графике
for k,v in ax.spines.items():
v.set_visible(False)

# Удаление делений у осей x и y
ax.set_xticks([])
ax.set_yticks([])

# Создание красной точки в последней точке данных
plt.plot(len(data) - 1, data[len(data) - 1], 'r.')

# Заполнение области под графиком с помощью alpha=0.1
ax.fill_between(range(len(data)), data, len(data)*[min(data)], alpha=0.1)

# Закрытие графика, чтобы он не отображался
plt.close(fig)

# Сохранение графика как изображения в формате png и получение его бинарных данных
img = BytesIO()
fig.savefig(img, format='png')
encoded = b64encode(img.getvalue()).decode('utf-8')

# Возвращение закодированных данных изображения в виде тега изображения HTML
return '<img src="data:image/png;base64,{}"/>'.format(encoded)

Хотя часть построения довольно очевидна, сосредоточимся на том, для чего предназначены последние четыре строки кода (не включая комментарии).

Цель состоит в том, чтобы преобразовать график в изображение, которое может быть отображено на веб-странице.

В первой строке создается новый объект img. BytesIO  —  это класс в модуле io, который создает буфер байтов в памяти.

Вторая строка сохраняет сгенерированный matplotlib график в объект img как PNG-изображение, используя метод savefig фигурного объекта fig.

Третья строка кодирует содержимое объекта img в виде строки base64 с помощью функции b64encode из модуля base64. Полученная строка base64 затем декодируется в строку Unicode с помощью метода decode с кодировкой utf-8.

Последняя строка возвращает HTML-тег <img> с атрибутом source, установленным в base64-кодированную строку изображения. При отображении этой строки на веб-странице будет показано изображение, сгенерированное matplotlib.

Теперь создаем строку, вызывая метод на каждой строке DataFrame.

df['Price History Line']  = df['Price History'].apply(create_line)
HTML(df.drop(columns = ["Price History"]).to_html(escape=False))

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

#2. Написание pandas-flavor

Если вызвать существующий метод в DataFrame Pandas, скажем df.rename(), очевидно, что метод rename() определяется в классе DataFrame.

Но что, если надо присоединить к объекту DataFrame пользовательский метод, скажем, df.my_method()? Такое вполне возможно.

К счастью, Pandas  —  это библиотека с широким спектром возможностей настройки и множеством способов расширения функциональности для удовлетворения пользовательских потребностей.

Один из популярных подходов  —  использование библиотеки pandas-flavor. Она позволяет определять и присоединять пользовательские методы Pandas к объекту DataFrame.

Установить ее можно следующим образом:

!pip install pandas_flavor

Теперь напишем пользовательский метод в файле my_pandas.py.

# my_pandas.py

import pandas as pd
import pandas_flavor as pf

@pf.register_dataframe_method
def add_row(df, row):
    
    df.loc[len(df)] = row

Допустим, у вас есть следующий DataFrame:

df = pd.DataFrame([["Mercury", 1], 
                   ["Venus",   2]],
                   columns = ["Planet", "Position"])

Импортируем файл пользовательского метода my_pandas.py, и он присоединит новый метод к объекту DataFrame:

import my_pandas

new_row = ["Earth", 3]
df.add_row(new_row)

print(df)
    Planet  Position
0  Mercury         1
1    Venus         2
2    Earth         3

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

#3. Создание DataFrame из списка объектов класса данных (DataClass)

DataFrame Pandas часто создается из списка Python, словаря, путем чтения файлов и т. д. А знаете ли вы, что можно также создать DataFrame из списка объектов DataClass?

Предположим, у вас есть следующий класс данных Point:

from dataclasses import dataclass

@dataclass
class Point:
    x_loc: int
    y_loc: int

Создадим несколько объектов из этого класса.

points = [Point(1, 2),
          Point(4, 5),
          Point(3, 7)]

Если передадим этот список объектов класса данных в метод pd.DataFrame, получим на выходе DataFrame:

pd.DataFrame(points)
   x_loc  y_loc
0      1      2
1      4      5
2      3      7

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

#4. Отображение индикатора состояния при применении функции с помощью Apply()

Во время применения метода apply() по отношению к DataFrame, мы не видим хода выполнения процесса и предполагаемого оставшегося времени.

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

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

Чтобы решить эту проблему, вместо метода apply() можно использовать progress_apply() из tqdm.

Сначала интегрируйте его с Pandas следующим образом:

from tqdm.notebook import tqdm
tqdm.pandas()

Теперь при использовании df.progress_apply() получим:

Индикатор выполнения при использовании apply (изображение автора)

#5. Добавление подписей в DataFrame

При представлении данных в DataFrame добавление подписей к таблицам может обеспечить дополнительный контекст и сделать данные более понятными. Благодаря этому вам не нужно будет добавлять ячейки с разметкой в ноутбук Jupyter.

С помощью API стилизации в Pandas к DataFrame можно добавить подписи. Рассмотрим это на примере.

Допустим, у нас есть следующий DataFrame:

df = pd.DataFrame([['Roy', 25, 50000],
                   ['Bob', 30, 60000],
                   ['Joe', 35, 70000]], 
                   columns = ["Names", "Age", "Salary"])
  Names  Age  Salary
0   Roy   25   50000
1   Bob   30   60000
2   Joe   35   70000

Вызываем метод set_caption() на аксессоре style DataFrame:

Теперь, как показано выше, DataFrame отображается с подписью.

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

#6. Форматирование предварительного просмотра DataFrame

При выводе DataFrame выглядит как набор необработанных чисел (или строк).

Возьмем для примера следующий датафрейм:

df
      Name  Height  Weight  Age  Saving
0    Alice     168      63   27    1000
1      Bob     175      75   34    1200
2  Charlie     182      91   42    1100

В этом случае столбцы данных обозначают какую-то присущую им величину, которую необходимо знать читателю. Но это четко не отражено в данных.

С помощью API стилизации отформатируем вывод предварительного просмотра DataFrame, как показано ниже:

Теперь гораздо нагляднее представлено, что означают отдельные значения, чего не хватало в предварительном просмотре по умолчанию.

Кроме того, вы можете изучить пакет PrettyPandas с открытым исходным кодом, который расширяет класс styler многими другими интересными утилитами.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Avi Chawla: 6 Things That You Probably Didn’t Know You Could Do With Pandas

Предыдущая статьяКак инженеру-программисту Reactjs перейти на Swift и SwiftUI 
Следующая статьяРазветвление вселенной UI