Поиск по сайту:

Как создавать сводные таблицы с помощью pandas


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

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

Сводные таблицы изначально были реализованы в ранних пакетах электронных таблиц и до сих пор являются широко используемой функцией последних версий. Их также можно найти в современных приложениях баз данных и в языках программирования. В этом уроке вы узнаете, как реализовать сводную таблицу в Python с помощью метода pandas DataFrame.pivot_table().

Прежде чем начать, вам следует ознакомиться с тем, как выглядит DataFrame pandas и как его можно создать. Знание разницы между DataFrame и pandas Series также окажется полезным.

Кроме того, вы можете использовать инструмент анализа данных Jupyter Notebook при работе с примерами в этом руководстве. В качестве альтернативы JupyterLab предоставит вам расширенные возможности работы с блокнотом, но вы можете использовать любую среду Python по вашему желанию.

Еще одна вещь, которая вам понадобится для этого урока, — это, конечно же, данные. Вы будете использовать данные «Представление данных о продажах — информационные панели», которые вы можете бесплатно использовать по лицензии Apache 2.0. Данные доступны для вас в файле sales_data.csv, который вы можете скачать, щелкнув ссылку ниже.

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

Column Name Data Type (PyArrow) Description
order_number int64 Order number (unique)
employee_id int64 Employee’s identifier (unique)
employee_name string Employee’s full name
job_title string Employee’s job title
sales_region string Sales region employee works within
order_date timestamp[ns] Date order was placed
order_type string Type of order (Retail or Wholesale)
customer_type string Type of customer (Business or Individual)
customer_name string Customer’s full name
customer_state string Customer’s state of residence
product_category string Category of product (Bath Products, Gift Basket, Olive Oil)
product_number string Product identifier (unique)
product_name string Name of product
quantity int64 Quantity ordered
unit_price double Selling price of one product
sale_price double Total sale price (unit_price × quantity)

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

В этом руководстве вы будете использовать библиотеку pandas, чтобы работать с DataFrames, а также новую библиотеку PyArrow. Библиотека PyArrow предоставляет пандам собственные оптимизированные типы данных, которые работают быстрее и требуют меньше памяти, чем традиционные типы NumPy, которые панды используют по умолчанию.

Если вы работаете в командной строке, вы можете установить как pandas, так и pyarrow, используя python -m pip install pandas pyarrow, возможно, внутри виртуальную среду, чтобы избежать конфликтов с существующей средой. Если вы работаете в Jupyter Notebook, вам следует использовать !python -m pip install pandas pyarrow. Имея библиотеки, вы можете считывать данные в DataFrame:

>>> import pandas as pd

>>> sales_data = pd.read_csv(
...     "sales_data.csv",
...     parse_dates=["order_date"],
...     dayfirst=True,
... ).convert_dtypes(dtype_backend="pyarrow")

Прежде всего, вы использовали import pandas, чтобы сделать библиотеку доступной в вашем коде. Чтобы создать DataFrame и прочитать его в переменную sales_data, вы использовали функцию pandas read_csv(). Первый параметр относится к читаемому файлу, а parse_dates подчеркивает, что данные столбца order_date предназначены для чтения как datetime64[ns] тип. Но есть проблема, которая помешает этому случиться.

В исходном файле даты заказа указаны в формате дд/мм/гггг, поэтому, чтобы сообщить read_csv(), что первая часть каждой даты представляет день, вы также установите для параметра dayfirst значение True. Это позволяет read_csv() читать даты заказов как типы datetime64[ns].

Если даты заказа успешно прочитаны как типы datetime64[ns], метод .convert_dtypes() может затем успешно преобразовать их в timestamp[ns][pyarrow] тип данных , а не более общий тип string[pyarrow], который в противном случае был бы использован. Хотя это может показаться немного запутанным, ваши усилия позволят вам проанализировать данные по дате, если вам это понадобится.

Если вы хотите просмотреть данные, вы можете запустить sales_data.head(2). Это позволит вам увидеть первые две строки вашего фрейма данных. При использовании .head() предпочтительнее делать это в Jupyter Notebook, поскольку отображаются все столбцы. Многие REPL Python отображают только первые и последние несколько столбцов, если вы не используете pd.set_option("display.max_columns", None) перед запуском .head().

Если вы хотите убедиться, что используются типы PyArrow, sales_data.dtypes подтвердит это за вас. Как вы увидите, каждый тип данных содержит [pyarrow] в своем имени.

Пришло время создать вашу первую сводную таблицу pandas с помощью Python. Для этого сначала вы изучите основы использования метода .pivot_table() DataFrame.

Как создать свою первую сводную таблицу с помощью pandas

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

Подсчитайте общий объем продаж по каждому типу заказов для каждого региона.

Имея под рукой такой большой объем данных и метод .pivot_table(), поддерживающий такое количество параметров, вы можете почувствовать себя немного ошеломленным. Не волнуйся. Если вы будете делать все шаг за шагом и заранее тщательно все обдумать, вы быстро создадите проницательные сводные таблицы.

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

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

Вас это устраивает, поэтому теперь вам нужно преобразовать свой план в параметры, необходимые для метода DataFrame.pivot_table(). В этом случае вам нужны параметры: значения, индекс, столбцы и aggfunc. Важно понимать, для чего они нужны, поскольку это основные параметры, используемые в большинстве сводных таблиц.

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

Параметр index позволяет указать, как данные должны быть сгруппированы. Помните, вам необходимо найти общий объем продаж по каждому типу заказов для каждого региона. Вы решили создать строку агрегированных данных на основе поля sales_region, поэтому оно будет передано как параметр index.

Вам также нужен отдельный столбец для каждого типа заказа. Это будет присвоено параметру columns.

Наконец, вам также необходимо сообщить сводной таблице, что вы хотите рассчитать общий объем продаж для каждого агрегирования. Чтобы создать простую сводную таблицу, вы устанавливаете ее параметр aggfunc, в данном случае функцию sum().

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

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="sales_region", columns="order_type",
...     aggfunc="sum",
... )
order_type          Retail   Wholesale
sales_region
Central East   $102,613.51 $149,137.89
N Central East $117,451.69 $152,446.42
N Central West  $10,006.42   $1,731.50
Northeast       $84,078.95 $127,423.36
Northwest       $34,565.62  $33,240.12
S Central East $130,742.32 $208,945.73
S Central West  $54,681.80  $51,051.03
Southeast       $96,310.12 $127,554.60
Southwest      $104,743.52 $121,977.20

Как видите, .pivot_table() использовал параметры, которые вы планировали ранее для создания сводной таблицы.

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

Передавая "display.float_format" и "$ {:,.2f}".format в функцию pandas set_option(), с этого момента вы определяете формат чисел с плавающей запятой. Они будут округлены до 2 десятичных знаков, в качестве разделителя тысяч будут использоваться запятые (,) и иметь префикс валюты ($). символ.

Если вы не хотите сохранить этот формат для будущих чисел с плавающей запятой, вам потребуется сбросить форматирование до значения по умолчанию, используя pd.reset_option("display.float_format") после . Pivot_table() был вызван.

>>> with pd.option_context("display.float_format", "${:,.2f}".format):
...     sales_data.pivot_table(
...         values="sale_price", index="sales_region",
...         columns="order_type", aggfunc="sum",
...     )

Оператор with определяет начало контекстного менеджера. Затем вы используете pd.option_context(), чтобы определить форматирование, которое будет использоваться для чисел с плавающей запятой. Это гарантирует, что ваш формат будет применен только к коду с отступом под ним, в данном случае к функции .pivot_table(). Как только код с отступом будет завершен, контекстный менеджер больше не будет доступен, и предыдущее форматирование снова вступит в силу.

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

Хотя вы в целом довольны своим результатом, вы чувствуете, что чего-то не хватает: итоговых показателей. Изначально вы не думали о включении столбцов итоговых данных, но теперь понимаете, что это будет полезно. Чтобы исправить это, вы обратитесь к документации и найдете следующее решение:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="sales_region", columns="order_type",
...     aggfunc="sum", margins=True, margins_name="Totals",
... )
order_type          Retail   Wholesale        Totals
sales_region
Central East   $102,613.51 $149,137.89   $251,751.40
N Central East $117,451.69 $152,446.42   $269,898.11
N Central West  $10,006.42   $1,731.50    $11,737.92
Northeast       $84,078.95 $127,423.36   $211,502.31
Northwest       $34,565.62  $33,240.12    $67,805.74
S Central East $130,742.32 $208,945.73   $339,688.05
S Central West  $54,681.80  $51,051.03   $105,732.83
Southeast       $96,310.12 $127,554.60   $223,864.72
Southwest      $104,743.52 $121,977.20   $226,720.72
Totals         $735,193.95 $973,507.85 $1,708,701.80

На этот раз, чтобы добавить дополнительные штрихи, вы устанавливаете margins=True и margins_name="Totals". Параметр margins=True добавил новые столбцы справа и снизу сводной таблицы. Каждый содержит итоги строк и столбцов соответственно. Параметр margins_name вставляет метки "Итоги" вместо меток по умолчанию "Все", которые отображались бы в противном случае.

Теперь ваша очередь. Попробуйте выполнить следующее упражнение, чтобы проверить свое понимание:

Создайте сводную таблицу, в которой будет показана самая высокая цена продажи для каждого региона продаж по категориям продуктов. На этот раз каждый регион продаж должен быть в отдельном столбце, а каждая категория товаров в отдельной строке. В этом примере вы можете игнорировать общие итоговые строки и столбцы, но вам следует применить денежный формат с использованием символа $ и использовать символ подчеркивания (_) в качестве тысяч. сепаратор. Опять же, необходимы два десятичных знака.

Вы найдете одно возможное решение этого упражнения в блокноте solutions.ipynb Jupyter, включенном в загружаемые материалы.

Если вы зашли так далеко, то теперь вы понимаете наиболее важные принципы создания сводных таблиц. Поздравляем! Но пока не расслабляйтесь, потому что вам предстоит узнать еще много интересного. Читайте дальше, чтобы еще больше расширить свои знания.

Включение подстолбцов в сводную таблицу

В каждой из предыдущих сводных таблиц вы назначили одну серию DataFrame ее параметру columns. Это вставит отдельный столбец в сводную таблицу для каждой уникальной записи в назначенной серии. Таким образом, присвоив "order_type" столбцам, ваша сводная таблица включала в себя столбцы Розничная и Оптовая, по одному для каждого. тип заказа. Пришло время расширить свои знания и научиться включать подстолбцы в сводную таблицу.

Вот следующая веха для вас:

Рассчитайте средний объем продаж различных типов заказов, размещенных каждым типом клиентов для каждого штата.

Как и прежде, остановитесь и подумайте. Первый шаг — продумать, что вы хотите видеть, и вписать соответствующие параметры в свой план.

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

Вы снова достаете карандаш и бумагу и быстро визуализируете:

Теперь, когда вы все обдумали и довольны, вы можете применить свой план к параметрам. Как и раньше, при расчете будут использоваться значения из столбца sale_price, поэтому вы будете использовать его в качестве параметра values. Поскольку вам нужны средние значения, вы можете установить aggfunc="mean". Однако, поскольку это значение по умолчанию, вам не нужно этого делать. Каждая строка будет основана на состоянии клиента, поэтому вам нужно будет установить index="customer_state".

Наконец, вам нужно подумать о столбцах. На этот раз, поскольку вы хотите, чтобы типы клиентов находились в верхнем столбце с разными типами заказов для каждого из них, вашим параметром columns будет список ["customer_type", "order_type"]. Можно создать несколько вложенных подстолбцов, передав более длинный список, но здесь вполне достаточно двух столбцов.

Чтобы удовлетворить ваши требования, вы используете код, показанный ниже:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index="customer_state",
...     columns=["customer_type", "order_type"], aggfunc="mean",
... )
customer_type        Business           Individual
order_type             Retail Wholesale     Retail
customer_state
Alabama               $362.67   $762.73    $137.47
Alaska                $295.33   $799.83    $137.18
Arizona               $407.50 $1,228.52    $194.46
Arkansas                 <NA> $1,251.25    $181.65
California            $110.53 $1,198.89    $170.94
...

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

Теперь взгляните на исходную визуализацию и сравните ее с тем, что на самом деле создал ваш код. Вы замечаете что-нибудь другое? Правильно, в ваших выводах нет столбца Индивидуальная оптовая продажа. Это связано с тем, что в него нет соответствующих значений. Метод .pivot_table() автоматически удалил его, чтобы предотвратить отображение пустого столбца.

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

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

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

Одно возможное решение этих упражнений вы найдете в блокноте solutions.ipynb Jupyter Notebook, включенном в загружаемые материалы.

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

Включение подстрок в сводную таблицу

В предыдущем разделе вы видели, как передача списка в параметр columns позволяет создавать подстолбцы. Возможно, вы уже догадались, что для анализа трафика по одной строке внутри другой вы передаете список в параметр index. Как и раньше, вы можете лучше понять это на примере, поэтому вот ваш следующий этап:

Рассчитайте общий объем продаж каждой из категорий продуктов, а также покажите подробную информацию о различных типах заказов, размещенных разными типами клиентов.

Как обычно, сделайте шаг назад и начните тщательно планировать свое решение.

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

Заточите карандаш:

Расчет будет основан на цене продажи, которая будет вашим параметром values, а установка aggfunc="sum" обеспечит расчет итоговых сумм. Чтобы разделить каждую категорию продуктов на отдельный столбец, вы назначаете его параметру columns. Наконец, чтобы обеспечить субанализ типа клиента по типу заказа, вы назначаете ["customer_type", "order_type"] параметру index.

Когда вы преобразуете свой план в код, вот результат:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values="sale_price", index=["customer_type", "order_type"],
...     columns="product_category", aggfunc="sum",
... )
product_category          Bath products  Gift Basket   Olive Oil
customer_type order_type
Business      Retail          $1,060.87    $3,678.50  $23,835.00
              Wholesale       $6,024.60   $18,787.50 $948,695.75
Individual    Retail         $32,711.58  $113,275.00 $560,633.00
...

Здесь вы узнали, что, передав список столбцов в index, вы можете выполнить необходимый анализ. Вы снова использовали $ {:,.2f, чтобы убедиться, что валюта отформатирована правильно.

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

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

Вы найдете одно возможное решение этого упражнения в блокноте solutions.ipynb Jupyter, включенном в загружаемые материалы.

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

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

До сих пор каждая из ваших сводных таблиц анализировала данные из одного столбца, например sale_price или quantity. Предположим, вы хотите проанализировать данные из обоих столбцов одинаково. Можете ли вы догадаться, как это сделать? Если вы думаете о том, чтобы передать оба столбца списка в параметр values, вы попали в точку.

Судя по вашему прогрессу, следующая веха уже в пределах вашей досягаемости:

Рассчитайте сумму продажных цен и количества проданной продукции каждой категории в каждом регионе продаж.

Почти подсознательно ваш мозг перешел в режим планирования:

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

Чтобы реализовать это, вы используете принципы, аналогичные тем, которые вы использовали ранее, но есть одна или две оговорки, о которых вам нужно помнить. Чтобы вычислить итоговые значения, вы устанавливаете aggfunc="sum", а для работы с любыми значениями , заменяя их нулем, вы устанавливаете fill_value до 0. Чтобы создать строки, показывающие регион продаж, проанализированный по категориям продуктов, вы передаете их оба в списке в index.

Код, который вы можете использовать для выполнения всего этого, показан ниже:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     index=["sales_region", "product_category"],
...     values=["sale_price", "quantity"],
...     aggfunc="sum", fill_value=0,
... )
                                 quantity  sale_price
sales_region   product_category
Central East   Bath Products          543   $5,315.40
               Gift Basket            267  $16,309.50
               Olive Oil             1497 $230,126.50
N Central East Bath Products          721   $6,905.36
               Gift Basket            362  $21,533.00
               Olive Oil             1648 $241,459.75
N Central West Bath Products           63     $690.92
               Gift Basket             26   $2,023.50
               Olive Oil               87   $9,023.50
...

Чтобы вычислить итоговые суммы sale_price и quantity, вы передали их в виде списка в параметр values. Одна небольшая проблема заключается в том, что столбцы вычислений отображаются не в том порядке, в котором они указаны в определяющем списке. В этом примере вы передали ["sale_price", "quantity"] в values, но если вы внимательно посмотрите на выходные данные, вы увидите, что они отображаются в алфавитном порядке.

Вы снова использовали "$ {:,.2f}", чтобы убедиться, что валюта отформатирована правильно. Обратите внимание, что это относится только к поплавкам. Форматирование целочисленных значений quantity позаботилось само собой.

Еще один момент, на который следует обратить внимание: здесь нет параметра columns. Вы могли подумать, что список, переданный в values, должен был быть передан в columns, но это не так. Параметр columns, а также index позволяют вам определить, как группируются данные. В этом примере вы не группируете товары по цене продажи или количеству. Вместо этого вы используете их в расчетах. Поэтому им необходимо присвоить значения.

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     index=["sales_region", "product_category"],
...     values=["sale_price", "quantity"],
...     aggfunc="sum", fill_value=0,
... ).loc[:, ["sale_price", "quantity"]]

Вы можете извлечь данные из сводной таблицы, используя ее атрибут .loc[]. Это позволяет вам определять строки и столбцы, которые вы хотите видеть, по их индексным меткам.

В данном случае вы хотели просмотреть все строки, поэтому передали символ двоеточия (:) в качестве первого параметра .loc[]. Затем, поскольку вы хотели видеть столбцы sale_price и quantity — именно в этом порядке — вы передали их в виде списка второму параметру.

Продолжайте и запустите этот код. Вы увидите, что столбцы расположены в том же порядке, в каком они были определены в списке, переданном в .loc[].

Пришло время еще раз помассировать это серое вещество. Посмотрите, сможете ли вы решить следующую задачу:

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

Вы найдете одно возможное решение этого упражнения в блокноте solutions.ipynb Jupyter, включенном в загружаемые материалы.

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

Выполнение более сложных агрегаций

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

Рассчитайте максимальные и минимальные продажи каждой категории продуктов для каждого типа клиентов.

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

Чтобы решить эту проблему, вам необходимо применить несколько функций агрегирования к одним и тем же данным. Раньше, когда вы анализировали данные с использованием нескольких критериев, вы делали это, передавая списки соответствующему параметру. Сможете ли вы догадаться, как применить несколько функций? Если нет, то ответ показан ниже:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values=["sale_price"], index="product_category",
...     columns="customer_type", aggfunc=["max", "min"],
... )
                        max                   min
                 sale_price            sale_price
customer_type      Business Individual   Business Individual
product_category
Bath Products       $300.00    $120.00      $5.99      $5.99
Gift Basket       $1,150.00    $460.00     $27.00     $19.50
Olive Oil         $3,276.00    $936.00     $16.75     $16.75

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

Кроме того, поскольку вы хотели видеть данные по типам клиентов, вы решили сделать этот параметр columns. Чтобы выполнить расчеты "max" и "min", вы передали оба из них параметру aggfunc в списке.

Затем вы решаете оттолкнуть лодку немного дальше, пытаясь выполнить следующее:

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

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

Чтобы создать эту сводную таблицу, вы можете сделать следующее:

>>> pd.set_option("display.float_format", "${:,.2f}".format)

>>> sales_data.pivot_table(
...     values=["sale_price", "quantity"],
...     index=["product_category"],
...     columns="customer_type",
...     aggfunc={"sale_price": "mean", "quantity": "max"},
... )
                 quantity            sale_price
customer_type    Business Individual   Business Individual
product_category
Bath Products          14          4     $53.27     $25.94
Gift Basket            14          4    $335.31    $156.24
Olive Oil              14          4  $1,385.37    $250.06

В сводной таблице теперь показаны максимальные количества для каждого из двух типов клиентов и средняя цена продажи для каждого. Этого можно добиться, добавив quantity в список параметров values, а затем передать словарь в aggfunc перед .pivot_table() сотворил чудо.

Вы идете дальше и отталкиваете лодку еще дальше, делая это:

Посчитайте, сколько сотрудников в каждом регионе продаж.

На первый взгляд это кажется простым. Вы быстро обдумываете это и верите, что этот код даст вам то, что вы хотите:

>>> sales_data.pivot_table(
...     values="employee_id",
...     index="sales_region",
...     aggfunc="count",
... )
                employee_id
sales_region
Central East            697
N Central East          832
N Central West           70
Northeast               604
...

Когда вы запустите этот код, окажется, что в каждом регионе много продавцов. Эти цифры неверны, поскольку count посчитал повторяющиеся значения employee_id. Если вы снова посмотрите на исходные данные, вы увидите, что один и тот же сотрудник указан несколько раз в своем регионе продаж. Вам нужно переосмыслить.

До этого момента вы использовали параметр aggfunc для указания функций, используемых для выполнения статистических вычислений. Каждый из них брал серию, определенную параметром values, и объединял ее в одно значение в соответствии с параметрами index и columns. Вы можете пойти дальше и написать свою собственную функцию агрегирования, при условии, что она принимает серию в качестве аргумента и возвращает одно агрегированное значение.

При написании собственной функции агрегирования вам необходимо передать подсерию значений employee_id для каждого sales_region. Затем вашей функции необходимо определить, сколько уникальных значений содержится в каждой подсерии, и вернуть результаты обратно в .pivot_table(). Один из способов написания такой функции показан ниже:

>>> def count_unique(values):
...     return len(values.unique())
...

Ваша функция count_unique() приняла серию pandas с именем values и использовала метод Series.unique() для получения массива NumPy, содержащего уникальные элементы. серии. Затем встроенная функция Python len() возвращала длину или количество элементов в каждой серии values.

Чтобы вызвать функцию и передать ей различные подсерии, вы присваиваете имя функции параметру aggfunc функции .pivot_table(). К счастью, вам не нужно беспокоиться о передаче каждой подсерии, поскольку за вас это делает .pivot_table(). Это означает, что ваша функция вызывается один раз для каждого региона продаж. Возвращаемые значения затем отображаются в итоговой сводной таблице.

Чтобы увидеть, как это работает, взгляните на код ниже:

>>> sales_data.pivot_table(
...     values="employee_id",
...     index=["sales_region"],
...     aggfunc=count_unique,
... )
                employee_id
sales_region
Central East              6
N Central East            6
N Central West            1
Northeast                 4
...

Как видите, присвоив count_unique параметру aggfunc, .pivot_table() вычислил количество уникальных значений идентификаторов сотрудников. Другими словами, подсчитали количество продавцов в каждом регионе. Очередной успех!

Время для еще одного испытания. Развлекайтесь с этим:

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

Одно из возможных решений этого упражнения включено в блокнот solutions.ipynb Jupyter, включенный в загружаемые материалы.

На этом этапе вы стали экспертом в создании сводных таблиц на Python. Наконец, вы узнаете о других способах агрегирования данных.

Использование .groupby() и crosstab() для агрегации

Хотя до сих пор ваш опыт сводных таблиц был сосредоточен на .pivot_table(), это не единственный способ выполнить агрегацию данных в pandas. DataFrames также имеет метод .groupby(), а pandas предоставляет функцию crosstab(), которая также агрегирует данные. В этом разделе вы увидите примеры того, как их можно использовать не только для .pivot_table().

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

Вот ваш следующий этап обучения:

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

Вы решаете использовать имеющиеся у вас знания для планирования и написания следующего кода. Вы уверены, что это, по крайней мере, позволит вам увидеть нужные вам данные:

>>> sales_data.pivot_table(
...     values="sale_price",
...     index="product_category",
...     aggfunc=["min", "mean", "max", "std"],
... )
                        min       mean        max        std
                 sale_price sale_price sale_price sale_price
product_category
Bath Products         $5.99     $28.55    $300.00     $23.98
Gift Basket          $19.50    $171.39  $1,150.00    $131.64
Olive Oil            $16.75    $520.78  $3,276.00    $721.49

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

Чтобы выполнить агрегирование с помощью .groupby(), вы передаете ему столбец или столбцы, по которым хотите сгруппировать. Это возвращает объект, называемый DataFrameGroupBy, который содержит метод .agg(), который позволяет вам определить используемые функции агрегирования, а также их заголовки. .

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

Приведенный выше пример можно написать с помощью .groupby(), как показано ниже:

>>> (
...     sales_data
...     .groupby("product_category")
...     .agg(
...          low_price=("sale_price", "min"),
...          average_price=("sale_price", "mean"),
...          high_price=("sale_price", "max"),
...          standard_deviation=("sale_price", "std"),
...     )
... )
                  low_price  average_price  high_price  standard_deviation
product_category
Bath Products         $5.99         $28.55     $300.00              $23.98
Gift Basket          $19.50        $171.39   $1,150.00             $131.64
Olive Oil            $16.75        $520.78   $3,276.00             $721.49

На этот раз вы можете видеть, что данные снова сгруппированы по категориям продуктов, но каждая группа имеет более аккуратный заголовок, названный в честь ключевого слова, используемого для определения кортежа. Если вы хотите глубже погрузиться в .groupby() и узнать, как его использовать, ознакомьтесь с разделом pandas GroupBy: ваше руководство по группировке данных в Python.

Еще один распространенный способ создания агрегации — использование функции crosstab(). Его функциональность аналогична .pivot_table(), поскольку, как вы узнали ранее, для выполнения анализа он использует .pivot_table(). Основное отличие состоит в том, что данные передаются в crosstab() как отдельная серия pandas.

Ваша последняя веха:

Подсчитайте количество сотрудников в каждом регионе, проанализированном по должности, с помощью crosstab().

Прочитав документацию, вы вскоре поймете, что параметры crosstab() аналогичны параметрам pivot_table(). Это означает, что вы знаете о crosstab() больше, чем вы думали. Ваши имеющиеся знания позволяют быстро спланировать и запустить следующий код:

>>> pd.crosstab(
...     index=sales_data["job_title"],
...     columns=sales_data["sales_region"],
...     margins=True,
...     margins_name="Totals",
... )
sales_region                 Central East  N Central East \
job_title
Sales Associate                         0             132
Sales Associate I                       0               0
Sales Associate II                    139               0
Sales Associate III                     0               0
...
Totals                                697             832

N Central West  ...  Southeast  Southwest  Totals
                ...
             0  ...          0        138      357
            70  ...        195        254      929
             0  ...          0         95      727
             0  ...        231          0      358
                ...
            70  ...        694        731     5130
[10 rows x 10 columns]

Параметры, используемые crosstab(), аналогичны параметрам, используемым pivot_table(). Действительно, в приведенном выше примере каждый параметр имеет тот же эффект, что и его эквивалент pivot_table(). Единственное отличие состоит в том, что функция crosstab() должна передавать ряд данных с использованием ссылки на DataFrame, то есть данные могут быть получены из нескольких DataFrame. В этом примере каждая строка анализируется по должности, а каждый столбец — по региону продаж.

Одна функция, поддерживаемая crosstab() и не включенная в .pivot_table(), — это параметр normalize. Когда вы устанавливаете normalize=True, вы делите каждую ячейку на сумму всех остальных ячеек в результирующем DataFrame:

>>> pd.set_option("display.float_format", "{:.2%}".format)

>>> pd.crosstab(
...     index=sales_data["job_title"],
...     columns=sales_data["sales_region"],
...     margins=True,
...     margins_name="Totals",
...     normalize=True,
... )
sales_region                Central East N Central East \
job_title
Sales Associate                    0.00%          2.57%
Sales Associate I                  0.00%          0.00%
Sales Associate II                 2.71%          0.00%
Sales Associate III                0.00%          0.00%
...
Totals                            13.59%         16.22%

N Central West  ... Southeast Southwest  Totals
                ...
         0.00%  ...     0.00%     2.69%    6.96%
         1.36%  ...     3.80%     4.95%   18.11%
         0.00%  ...     0.00%     1.85%   14.17%
         0.00%  ...     4.50%     0.00%    6.98%
                ...
         1.36%  ...    13.53%    14.25%  100.00%

[10 rows x 10 columns]

Этот код очень похож на предыдущий пример, за исключением того, что, установив normalize=True, вы рассчитываете каждую цифру как процент от общей суммы. Вы также использовали "{:.2%}" для отображения вывода в обычном процентном формате.

Заключение

Теперь у вас есть полное представление о том, как использовать метод .pivot_table(), его основные параметры и то, как они используются для структурирования сводных таблиц. Самое главное, вы поняли, как важно продумать то, чего вы хотите достичь, прежде чем реализовать это.

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

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

Поздравляем с завершением этого руководства и наслаждайтесь применением этих новых навыков в своих будущих проектах по анализу данных!