Разделение (биннинг, дискретизация, балансировка) данных с помощью qcut и cut в Pandas

Open in Colab


telegram

Введение

При работе с непрерывными числовыми данными часто бывает полезно разделить (to bin) данные на несколько сегментов для дальнейшего анализа. Существует несколько терминов: сегментирование (bucketing), дискретное разделение (discrete binning), дискретизация (discretization) или квантование (quantization). Pandas поддерживает эти подходы с помощью функций cut и qcut.

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

Оригинал статьи Криса тут

Биннинг (binning)

Один из наиболее распространенных случаев биннинга выполняется при создании гистограммы.

Рассмотрим пример с продажами. Гистограмма данных о продажах показывает, как непрерывный набор показателей продаж можно разделить на дискретные ячейки (например: 60 00070 000 долларов США), а затем использовать их для группировки и подсчета учетных записей (account number).

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

Существует множество других сценариев, в которых вы можете определить собственные интервалы (bins).

В приведенном выше примере 8 интервалов с данными. Что, если бы мы захотели разделить наших клиентов на 3, 4 или 5 групп?

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

Остальная часть статьи покажет, в чем их различия и как их использовать.

qcut

В документации qcut описывается как "функция дискретизации на основе квантилей". По сути, это означает, что qcut пытается разделить базовые данные на интервалы равного размера. Функция определяет интервалы с использованием процентилей на основе распределения данных, а не фактических числовых границ интервалов.

Если вы ранее использовали функцию description, то уже встречали пример основных концепций, представленных qcut:

Запомните значения для 25%, 50% и 75% процентилей, поскольку мы напрямую рассматрим использование qcut.

Самое простое использование qcut - определить количество квантилей и позволить pandas разделить данные.

В приведенном ниже примере мы просим pandas создать 4 группы одинакового размера:

В результате получается категориальный ряд (про категориальный тип данных в pandas см. тут), представляющий интервалы с продажами. Поскольку мы запросили квантили с q=4, поэтому интервалы соответствуют процентилям из функции describe.

Типичным вариантом использования является сохранение результатов разбиения в исходном фрейме данных (dataframe) для будущего анализа.

В следующем примере мы создадим 4 интервала (также называемых квартилями) и 10 интервалов (также называемых децилями) и сохраним результаты обратно в исходный фрейм данных:

Обратите внимание, как сильно различаются интервалы между quantile_ex_1 и quantile_ex_2. Я также добавил precision (точности), чтобы определить, сколько десятичных знаков использовать для вычисления точности интервала.

Можем посмотреть, как значения распределяются по интервалам с помощью value_counts:

Теперь для второго столбца:

Это иллюстрирует ключевую концепцию: в каждом случае в каждом интервале содержится равное количество наблюдений.

Pandas за кулисами производит вычисления, чтобы определить ширину интервалов. Например, в quantile_ex_1 диапазон первого интервала составляет 74661.15, а второго - 9861.02 (110132 - 100271).

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

Например, если мы хотим разделить наших клиентов на 5 групп (также называемых квинтилями), как в случае с часто летающими авиакомпаниями, мы можем явно назвать интервалы, чтобы их было легче интерпретировать:

В приведенном выше примере я сделал кое-что иначе.

Во-первых, явно определил диапазон используемых квантилей: q=[0, .2, .4, .6, .8, 1], а также задал метки labels=bin_labels_5 для использования при представлении интервалов.

Давайте проверим распределение:

Как и ожидалось, теперь у нас есть равное распределение клиентов по 5 интервалам, а результаты отображаются в простой для понимания форме.

При использовании qcut следует помнить об одном важном моменте: все квантили должны быть меньше 1. Вот несколько примеров распределений. В большинстве случаев проще определить q как целое число:

Может возникнуть вопрос: как узнать, какие диапазоны используются для идентификации различных интервалов?

В этом случае можно использовать retbins=True для возврата меток интервалов.

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

Вот еще один трюк, которому я научился при написании этой статьи.

Если вы попробуете df.describe для категориальных значений, то получите разные итоговые результаты:

Думаю, это является хорошим обзором того, как работает qcut.

Раз уж мы обсуждаем describe, то можем использовать аргумент percentiles (процентилей) для определения процентилей, используя тот же формат, который использовали для qcut:

Есть одно небольшое замечание.

Передача 0 или 1 означает, что 0% будет таким же, как минимум, а 100% будет таким же, как и максимум.

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

Прежде чем мы перейдем к описанию функции cut, есть еще один потенциальный способ назвать интервалы. Вместо диапазонов интервалов или пользовательских меток мы можем возвращать целые числа, передав labels=False:

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

cut

Теперь, когда мы обсудили, как использовать qcut, можем показать, чем он отличается от cut.

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

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

В реальных примерах интервалы (bins) могут определяться, исходя из задачи. Для программы часто летающих пассажиров 25 000 миль - это серебряный уровень, который не меняется в зависимости от годового изменения данных. Если мы хотим определить границы интервала (25 00050 000 и т.д.), то должны использовать cut.

Можем использовать cut для определения интервалов постоянного размера и позволить pandas определить границы интервалов.

Примеры должны прояснить это различие.

Для простоты я удаляю предыдущие столбцы:

В первом примере можем разрезать (cut) данные на 4 интервала равного размера. Pandas выполнит вычисления, чтобы определить, как разделить набор данных на эти 4 группы:

Посмотрим на распределение:

Первое, что вы заметите: все диапазоны интервалов составляют около 32 265, но распределение элементов внутри интервалов не одинаково. Интервалы имеют распределение по 12, 5, 2 и 1 элементам в каждом интервале. И это существенное различие между cut и qcut.

Если вы хотите, чтобы элементы в интервалах распределялись равномерно, используйте qcut. Если вы хотите определить свои собственные диапазоны числовых интервалов, используйте cut.

Прежде чем идти дальше, я хотел бы быстро освежить в памяти обозначения интервалов. В приведенных выше примерах широко используются () и [] для обозначения того, как определяются границы интервала. Для тех из вас (включая меня), кому может потребоваться освежить в памяти нотацию интервалов, я рекомендую этот простой сайт.

Вот диаграмма, основанная на примере выше:

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

Когда вы представляете результаты своего анализа другим, вам нужно будет четко понимать, является ли учетная запись с продажами 70 000 серебряным или золотым клиентом.

Вот пример, в котором мы хотим конкретно определить границы наших 4 интервалов, задав параметр bins.

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

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

Во-первых, мы можем использовать numpy.linspace для создания равномерного диапазона:

linspace - это функция, которая предоставляет массив равномерно распределенных чисел в заданном пользователем диапазоне.

В этом примере нам нужно 9 равномерно расположенных точек, разделенных от 0 до 200 000.

Проницательные читатели могут заметить, что у нас 9 чисел, но только 8 категорий. Если вы нарисуете схему фактических категорий, должно быть понятно, почему мы получили 8 категорий от 0 до 200 000. Во всех случаях количество разделенных точек на одну категорию меньше.

Другой вариант - использовать numpy.arange, которая предлагает аналогичную функциональность. Рекомендую эту статью для понимания обеих функций. Попробуйте оба подхода и посмотрите, какой из них лучше подходит для ваших задач.

Существует еще один дополнительный вариант для определения интервалов - interval_range. Мне пришлось посмотреть документацию pandas, чтобы разобраться в нем.

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

Вот числовой пример:

У использования interval_range есть обратная сторона: вы не можете определять собственные метки.

Как показано выше, параметр labels игнорируется при использовании interval_range.

Обычно я использую настраиваемый список диапазонов интервалов или linspace, если у меня большое количество интервалов.

Одно из различий между cut и qcut заключается в том, что вы можете использовать параметр include_lowest, чтобы определить, должен ли первый интервал включать все самые низкие значения.

Наконец, передача параметра right=False изменит интервалы, чтобы исключить самый правый элемент. Поскольку cut позволяет более точно определять интервалы, эти параметры могут быть полезны, чтобы убедиться, что интервалы определены так, как вы ожидаете.

Остальные функции cut аналогичны qcut. Мы можем вернуть интервалы, используя retbins=True, или настроить точность, используя аргумент precision.

Последний трюк, который я хочу показать: value_counts включает в себя быстрый способ для сортировки и подсчета данных. Это в некоторой степени аналогично тому, как describe может быть сокращением для qcut.

Если мы хотим разделить значение на 4 интервала и подсчитать количество случаев:

По умолчанию value_counts будет сортировать сначала по наибольшему значению.

Если передать sort=False, интервалы будут отсортированы по числовому порядку, что может быть полезным при просмотре.

Заключение

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