Создание простых сводных таблиц в pandas с помощью sidetable
Крис Моффит, редактор сайта об автоматизации бизнес-задач на Python, разработал модуль sidetable.
Со слов автора новый модуль расширяет возможности value_counts() и использует API pandas для регистрации собственных методов.
Давайте разбираться, как он работает.
Для начала установим модуль:
pip3 install sidetable
Рассмотрим пример с грантами для школ США, если кратко: Конгресс еще при Обаме выделил 4 миллиарда у.е. для реформы образования, для получения гранта школе надо выбрать одну из моделей реформирования («Model Selected»).
Кстати, блокнот с исходным текстом лежит в Google Colab.
Начинаем, как обычно, с импорта модулей:
import pandas as pd
import sidetable
df = pd.read_csv('https://github.com/chris1610/pbpython/blob/master/data/school_transform.csv?raw=True', index_col=0)
df.head()

В результате импорта модуля sidetable появился новый метод stb.
Вызов stb.freq() позволяет построить сводную таблицу частот по штатам:
df.stb.freq(['State'])

Этот пример показывает, что CA (California) встречается 92 раза и составляет 12,15% от общего количества школ. Если включить в подсчеты FL (Florida), то будет 163 школы, что составляет 21,5% от общего числа школ, участвующих в грантах.
Можно сравнить этот результат с выводом стандартного метода value_counts() . При установке normalize в True возвращаемый объект будет содержать относительные частоты уникальных значений:
df['State'].value_counts(normalize=True)

Хм... разница заметна, даже невооруженным глазом.
Можно составить список штатов, которые составляют около 50% от общего числа с помощью аргумента thresh (рус. «молотить») и сгруппировать все остальные штаты в категорию «Others»:
df.stb.freq(['State'], thresh=.5)

Теперь видим, что 8 штатов составляют практически 50% от общего количества.
Можем для симпатичности переименовать категорию «Others», используя ключевой аргумент other_label:
df.stb.freq(['State'], thresh=.5, other_label='Остальные штаты')
sidetable позволяет группировать столбцы для лучшего понимания распределения. Посмотрим, как различные «Модели трансформации» применяются в разных регионах?
df.stb.freq(['Region', 'Model Selected'])

sidetable позволяет передавать значение value, по которому можно суммировать (вместо подсчета вхождений).
df.stb.freq(['Region'], value='Award_Amount')

Узнали, что Northeast (Северо-Восток) затратил наименьшее количество средств на реформу, а 37% от общих расходов было потрачено на школы в South (Южном) регионе.
Посмотрим на типы выбранных моделей и определим разбиение 80/20 для выделенных средств:
df.stb.freq(['Region', 'Model Selected'],
value='Award_Amount', thresh=.82,
other_label='Remaining')

Это аналог crosstab в Pandas:

Можно улучшить читабельность данных в pandas за счет добавления форматирования столбцов Percentage и Amount. Укажем для этого ключевой аргумент style=True:
df.stb.freq(['Region'], value='Award_Amount', style=True)

Пример построения таблицы пропущенных значений:
df.stb.missing()

Видим 10 пропущенных значений в столбце Region, что составляет чуть менее 1,3% от общего значения в этом столбце.
Похожий результат можно получить с помощью info() :

Ссылка на остальную документацию для модуля sidetable.
Для визуализации пропущенных значений см. модуль missingno.
Создание собственных методов в pandas
В качестве примера можно взять исходный текст модуля sidetable здесь.
В верхней части файла импортируется pandas, чтобы получить доступ к декоратору:
import pandas as pd
@pd.api.extensions.register_dataframe_accessor("stb")
class SideTableAccessor:
def __init__(self, pandas_obj):
self._validate(pandas_obj)
self._obj = pandas_obj
Эта часть кода создает класс метода и определяет значение метода (stb). В момент импорта модуля, содержащего данный код, метод stb станет доступен для всех фреймов данных.
Когда создается экземпляр класса, текущий DataFrame будет проверен с помощью метода _validate() и затем будет доступен через self._obj.
@staticmethod
def _validate(obj):
# verify this is a DataFrame
if not isinstance(obj, pd.DataFrame):
raise AttributeError("Must be a pandas DataFrame")
Вся работа выполняется в методах freq и missing. Необходимо убедиться, что вернулся правильный DataFrame.
Полная версия метода missing:
def missing(self, clip_0=False, style=False):
""" Build table of missing data in each column.
clip_0 (bool): In cases where 0 counts are generated, remove them from the list
style (bool): Apply a pandas style to format percentages
Returns:
DataFrame with each Column including total Missing Values, Percent Missing
and Total rows
"""
missing = pd.concat([self._obj.isna().sum(),
self._obj.isna().mean()],
axis='columns').rename(columns={
0: 'Missing',
1: 'Percent'
})
missing['Total'] = len(self._obj)
if clip_0:
missing = missing[missing['Missing'] > 0]
results = missing[['Missing', 'Total',
'Percent']].sort_values(by=['Missing'],
ascending=False)
if style:
format_dict = {'Percent': '{:.2%}', 'Total': '{0:,.0f}'}
return results.style.format(format_dict)
else:
return results
Спасибо, Крис!
Ссылка на оригинал статьи.