Содержание
Tableau Prep — делаем ранжирование данных
На данный момент Tableau Prep не поддерживает Table Calculations (вычисления, в отличие от Tableau Desktop). Однако, в Tableau Prep можно выполнить один трюк, который в определенных случаях позволит ранжировать данные (rank).
Ранжирование в Tableau Desktop
Давайте обратимся к данным Superstore Sample, в которых представлена стоимость (Sales)
различных товаров (Orders), приобретенных клиентами (Customers) в различных штатах (States).
Предположим, что мы хотим отранжировать клиентов в каждом штате по величине совершенных ими покупок. В Tableau Desktop есть табличное вычисление (Table Calculations) Rank(), которое нетрудно настроить, чтобы получить что-то такое:
Сложность с table calculation:
— нужно указать правильные поля для addressing и partitioning (в
данном примере это несложно),
— если вы уже выполнили сложный анализ, то дополнительные сложности нежелательны. Что если можно было бы отранжировать данные в источнике?
Ранжирование в Tableau Prep
Давайте используем те же данные Superstore Sample и посмотрим, как их можно отранжированить в Tableau Prep. Если мы работаем с клиентами, то нужно выполнить следующие действия:
- Создать агрегат Sales by State and Customer:
- Добавить Step после агрегации, который будет содержать агрегированные строки:
- Использовать Join, чтобы объединить Aggregate Rows с предыдущим шагом.
Обратите внимание, что используется inner join по полю State (чтобы выполнить ранжирование внутри штата) и затем включается условие что продажи Sales of the State & Customer меньше либо равны продажам в Aggregate Step.Другими словами, мы хотим объединить строки с одинаковыми штатами и при условии, что клиент совершил такие же или большие покупки. Т.е. если у клиента A наибольший объем покупок, то они будут совпадать только сами с собой (и если мы посчитаем число строк, то получим 1, и это будет ранг 1, но здесь мы забегаем вперед): - Добавить Aggregation к Join, который позволяет посчитать Number of Records (число строк) для каждого штата и клиента. Число строк – число совпадений с Join, которые дают ранг для каждого штата:
- Добавить Clean step, чтобы переименовать Number of Records в Rank Within State:
- В результате, мы может выполнить Join для Clean step и исходного набора данных, используя поля «клиенты» и «штаты». Финальный набор данных содержит исходные данные и ранг продаж для каждого штата:
Теперь вам будет проще выполнить в Tableau Desktop гораздо более сложный анализ. Можно сравнить летние продажи топ-10 клиентов. Или сравнить топ-10 клиентов одного филиала и топ-10 «в конце списка» из другого филиала? Все просто!
Перевод: Владимир Ермаков, Tableau эксперт
Источник >>
Хотите узнать, как провести анализ и сделать отчеты быстро?
Нам доверяют:
T-SQL | Функции ранжирования
140
Работа с базами данных в .NET Framework — Оконные функции T-SQL — Функции ранжирования
Исходник базы данных
Стандарт SQL поддерживает четыре оконные функции, которые служат для ранжирования. Это ROW_NUMBER, NTILE, RANK и DENSE_RANK. В стандарте первые две считаются относящимися к одной категории, а вторые две — ко второй. Это связано с различиями в отношении детерминизма. Подробнее я расскажу в процессе рассказа об отдельных функциях.
Функции ранжирования появились еще в SQL Server 2005. Тем не менее я покажу альтернативные, основанные на наборах методы для получения того же результата. Я сделаю это по двум причинам: во-первых, это может быть интересным, а во-вторых, я верю, что это помогает лучше понять нюансы работы функций. Тем не менее имейте в виду, что на практике я настоятельно рекомендую придерживаться оконных функций, потому что они и проще, и намного эффективнее, чем альтернативные решения. Подробнее об оптимизации мы поговорим позже.
Все четыре функции ранжирования поддерживают необязательное предложение секционирования и обязательное предложение упорядочения окна. Если предложение секционирования окна отсутствует, весь результирующий набор базового запроса (вспомните о входных данных этапа SELECT) считается одной секцией. Что касается предложения упорядочения окна, то оно обеспечивает упорядочение при вычислениях. Понятно, что ранжирование строк без определения упорядочения вряд ли имеет смысл. В ранжирующих оконных функциях упорядочение служит другой цели по сравнению с функциями, поддерживающими кадрирование, такими как агрегирующие оконные функции. В первом случае упорядочение имеет логический смысл для самих вычислений, а во втором — упорядочение связано с кадрированием, то есть служит целям фильтрации.
Функция ROW_NUMBER
Эта функция вычисляет последовательные номера строк, начиная с 1, в соответствующей секции окна и в соответствии с заданным упорядочением окна. Посмотрите на пример запроса:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY orderid) AS rownum FROM Sales.OrderValues;
Вот сокращенный результат выполнения этого запроса:
Этот запрос кажется тривиальным, но здесь есть несколько моментов, о которых стоит сказать.
Так как в запросе нет предложения представления ORDER BY, упорядочение представления не гарантируется. Поэтому упорядочение представления здесь нужно считать произвольным. На практике SQL Server оптимизирует запрос с учетом отсутствия предложения ORDER BY, поэтому строки могут возвращаться в любом порядке. Если нужно гарантировать упорядочение представления, нужно не забыть добавить предложение представления ORDER BY. Если нужно, чтобы упорядочение представления выполнялось на основе номера строки, можно использовать псевдоним, назначенный в процессе вычисления предложения представления ORDER BY примерно так:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY orderid) AS rownum FROM Sales.OrderValues ORDER BY rownum;
Считайте вычисление номеров строк генерацией еще одного атрибута в результирующем наборе запроса. Естественно, если хочется, можно получить упорядочение представления, которое отличается от упорядочения окна, как в следующем запросе:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY orderid) AS rownum FROM Sales.OrderValues ORDER BY val DESC;
Можно использовать оконный агрегат COUNT для создания операции которая логически эквивалентна функции ROW_NUMBER. Допустим, WPO — определение секционирования и упорядочения окна, примененное в функции ROW_NUMBER. Тогда ROW_NUMBER OVER WPO эквивалентно COUNT(*) OVER(WPO ROWS UNBOUNDED PRECEDING). Например, следующее эквивалентно запросу из листинга предыдущего примера:
SELECT orderid, val, COUNT(*) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS rownum FROM Sales.OrderValues;
Как я говорил, это хорошая тренировка — попытаться и создать альтернативные варианты вместо использования оконных функций, и не важно, что эти варианты сложнее и менее эффективные. Если уж мы говорим о функции ROW_NUMBER, то вот основанная на наборах стандартная альтернатива запросу, в которой оконные функции не используются:
SELECT orderid, val, (SELECT COUNT(*) FROM Sales.OrderValues AS O2 WHERE O2.orderid <= O1.orderid) AS rownum FROM Sales.OrderValues AS O1;
В этом решении используется агрегат COUNT во вложенном запросе для определения, у скольких строк значение упорядочения (в нашем случае orderid) меньше или равно текущему. Это просто, если у вас уникальное упорядочение, основанное на одном атрибуте. Но все сильно усложняется, если упорядочение неуникально, что я и продемонстрирую при обсуждении детерминизма.
Детерминизм
Если упорядочение окна уникально, вычисление ROW_NUMBER является детерминистическим. Это означает, что у запроса есть только одни правильный результат, то есть если не менять входные данные, вы гарантировано будете получать повторяющиеся результаты. Но если упорядочение окна не уникально, вычисление становится недетерминистическим. Функция ROW_NUMBER генерирует уникальные номера строк в рамках секции, даже для строк с одинаковыми значениями в атрибутах упорядочения окна. В качестве примера посмотрите на следующий запрос:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER(ORDER BY orderdate DESC) AS rownum FROM Sales.OrderValues;
Так как атрибут orderdate не уникальный, упорядочение строк с одинаковым значением orderdate следует считать произвольным. В принципе существует более одного корректного результата этого запроса. В качестве примера возьмем четыре строки с датой заказа 2008-05-06. Любой порядок строк с номерами от 1 до 4 считается правильным. Поэтому если вы выполните запрос снова, то в принципе можете получить другой порядок — сейчас не будем оценивать вероятность такого события, обусловленную особенностями реализации SQL Server.
Если нужно гарантировать повторяемость результатов, нужно сделать запрос детерминистическим. Это можно сделать, добавив дополнительный параметр в определение упорядочения окна, чтобы обеспечить уникальность в рамках секции. К примеру в следующем запросе уникальность упорядочения в окне достигается за счет добавления в список orderid DESC:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER(ORDER BY orderdate DESC, orderid DESC) AS rownum FROM Sales.OrderValues;
При использовании оконных функций детерминизм вычисления номеров страниц реализуется просто. Сделать то же, не прибегая к оконным функция сложнее, но вполне реально:
SELECT orderdate, orderid, val, (SELECT COUNT(*) FROM Sales. OrderValues AS O2 WHERE O2.orderdate >= O1.orderdate AND (O2.orderdate > O1.orderdate OR O2.orderid >= O1.orderid)) AS rownum FROM Sales.OrderValues AS O1;
Но вернемся к функции ROW_NUMBER: как мы выдели, ее можно использовать для создания недетерминистических вычислений при использовании неуникального упорядочения. Таким образом, недетерминизм разрешен, но странно то, что он не разрешен полностью. Я имею в виду то, что предложение ORDER BY обязательно. Но что, если вы хотите просто получить в секции уникальные номера строк не обязательно в каком-то определенном порядке? Можно создать такой запрос:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER() AS rownum FROM Sales.OrderValues;
Но, как уже говорилось, предложение ORDER BY в функциях ранжирования является обязательным:
The function 'ROW_NUMBER' must have an OVER clause with ORDER BY.
Вы можете поумничать и определить константу в списке ORDER BY:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER(ORDER BY NULL) AS rownum FROM Sales. OrderValues;
Ho SQL Server вернет такую ошибку:
Windowed functions and NEXT VALUE FOR functions do not support constants as ORDER BY clause expressions.
Однако решение есть, и я вскоре его приведу.
Предложение OVER и последовательности
Вам наверное интересно, что делает функция NEXT VALUE FOR в сообщении об ошибке при попытке применить константу в предложении OVER. Это связано с расширенной поддержкой последовательностей в SQL Server 2012 по сравнению со стандартом SQL. Последовательность SQL Server — это объект базы данных, который служит для автогенерации номеров, которые часто используются как ключи. Вот пример кода создания объекта-последовательности dbo.Seq1:
CREATE SEQUENCE dbo.Seq1 AS INT START WITH 1 INCREMENT BY 1;
Для получения очередного значения последовательности применяется функция NEXT VALUE FOR. Вот пример:
SELECT NEXT VALUE FOR dbo.Seq1;
Эту функцию можно вызывать в запросе, возвращающем несколько строк:
SELECT orderid, orderdate, val, NEXT VALUE FOR dbo. Seq1 AS seqval FROM Sales.OrderValues;
Это стандартный код. В SQL Server 2012 возможности функции NEXT VALUE FOR расширены и позволяют определять упорядочение в предложении OVER подобно тому, как это делается в оконных функциях. Таким образом можно гарантировать, что значения последовательности отражают желаемое упорядочение. Вот пример использования расширенной функции NEXT VALUE FOR:
SELECT orderid, orderdate, val, NEXT VALUE FOR dbo.Seq1 OVER(ORDER BY orderdate, orderid) AS seqval FROM Sales.OrderValues;
Аналогичный принцип детерминизма применим к предложению OVER в функции NEXT VALUE FOR, как это происходит в оконных функциях.
Итак, нет прямого способа назначить номера строкам без упорядочения, но, по-видимому, SQL Server не смущает вложенный запрос, возвращающий константу в качестве элемента упорядочения окна. Вот пример:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM Sales. OrderValues;
Функция NTILE
Эта функция позволяет разбивать строки в секции окна на примерно равные по размеру подгруппы (tiles) в соответствии с заданным числом подгрупп и упорядочением окна. Допустим, что нужно разбить строки представления OrderValues на 10 подгрупп одинакового размера на основе упорядочения по val. В представлении 830 строк, поэтому требуется 10 подгрупп, размер каждой будет составлять 83 (830 деленное на 10). Поэтому первым 83 строкам (одной десятой части), упорядоченным по val, будет назначен номер группы 1, следующим 83 строкам — номер подгруппы 2 и т. д. Вот запрос, вычисляющий номера как строк, так и подгрупп:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY val) AS rownum, NTILE(10) OVER(ORDER BY val) AS tile FROM Sales.OrderValues;
Если вы думаете, что разбиение на подгруппы похоже на разбиение на страницы, хочу вас предупредить, что не стоит их путать. При разбиении на страницы, размер страницы является константой, а число страниц меняется динамически — оно определяется делением числа строк в результате запроса на размер страницы. При разбиении на подгруппы число подгрупп является константой, а размер подгруппы меняется и определяется как число строк деленное на заданное число подгрупп. Ясно, для чего нужно разбиение на страницы, а разбиение на подгруппы обычно используется для аналитических задач — когда нужно распределить данные среди заданного числа равных по размеру сегментов с использованием упорядочения по определенному измерению.
Но вернемся к результату запроса, вычисляющего номера как строк, так и подгрупп: как видите они тесно связаны друг с другом. По сути, можно считать, что номер подгруппы вычисляется на основе номера строки. В предыдущем разделе мы говорили, что если упорядочение окна не является уникальным, функция ROW_NUMBER является недетерминистической. Если разбиение на подгруппы принципиально основано на номерах строк, то это означает, что вычисление NTILE также недетерминистично, если упорядочение окна не уникально. Это означает, что у данного запроса может быть несколько правильных результатов. Можно посмотреть на это с другой стороны: двум строкам с одним значением упорядочения могут быть назначены разные номера подгрупп. Если нужен гарантированный детерминизм, можно следовать моим рекомендациям по получению детерминистических номеров строк, а именно добавить в упорядочение окна дополнительный параметр:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY val, orderid) AS rownum, NTILE(10) OVER(ORDER BY val, orderid) AS tile FROM Sales.OrderValues;
Теперь у запроса только один правильный результат. Ранее, при описании функции NTILE я пояснил, что она позволяет разбить строки в секции окна на примерно равные подгруппы. Я использовал слово «примерно», потому что число строк, полученное в базовом запросе, может не делиться нацело на число подгрупп. Допустим, вы хотите разбить строки представления OrderValues на 100 подгрупп. При делении 830 на 100 получаем частное 8 и остаток 30. Это означает, что базовая размерность подгрупп будет 8, но часть подгрупп получать дополнительную строку. Функция NTILE не пытается распределять дополнительные строки среди подгрупп с равным расстоянием между подгруппами — она просто добавляет по одному ряду в первые подгруппы, пока не распределит остаток. При наличии остатка 30 размерность первых 30 подгрупп будет на единицу больше базовой размерности. Поэтому первые 30 будут содержать 9 рядов, а последние 70 — 8, как показано в следующем запросе:
SELECT orderid, val, ROW_NUMBER() OVER(ORDER BY val, orderid) AS rownum, NTILE(100) OVER(ORDER BY val, orderid) AS tile FROM Sales.OrderValues;
Следуя привычному методу, попытаемся создать альтернативные решения, заменяющие функцию NTILE и не содержащие оконных функций.
Я покажу один способ решения задачи. Для начала, вот код, который вычисляет число подгрупп по заданным размерности, числу подгрупп и числу строк:
DECLARE @cnt AS INT = 830, @numtiles AS INT = 100, @rownum AS INT = 42; WITH C1 AS ( SELECT @cnt / @numtiles AS basetilesize, @cnt / @numtiles + 1 AS extendedtilesize, @cnt % @numtiles AS remainder ), C2 AS ( SELECT *, extendedtilesize * remainder AS cutoffrow FROM C1 ) SELECT CASE WHEN @rownum <= cutoffrow THEN (@rownum - 1) / extendedtilesize + 1 ELSE remainder + ((@rownum - cutoffrow) - 1) / basetilesize + 1 END AS tile FROM C2;
Вычисление вполне очевидно. Для входных данных код возвращает 5 в качестве числа подгрупп.
Затем применим эту процедуру к строкам представления OrderValues. Используйте агрегат COUNT, чтобы получить размерность результирующего набора, а не входные данные @cnt, а также примените описанную ранее логику для вычисления номеров строк без использования оконных функций вместо входных данных @rownum:
DECLARE @numtiles AS INT = 100; WITH C1 AS ( SELECT COUNT(*) / @numtiles AS basetilesize, COUNT(*) / @numtiles + 1 AS extendedtilesize, COUNT(*) % @numtiles AS remainder FROM Sales.OrderValues ), C2 AS ( SELECT *, extendedtilesize * remainder AS cutoffrow FROM C1 ), C3 AS ( SELECT O1.orderid, O1.val, (SELECT COUNT(*) FROM Sales.OrderValues AS O2 WHERE O2.val <= O1.val AND (O2.val < O1.val OR O2.orderid <= O1.orderid)) AS rownum FROM Sales. OrderValues AS O1 ) SELECT C3.*, CASE WHEN C3.rownum <= C2.cutoffrow THEN (C3.rownum - 1) / C2.extendedtilesize + 1 ELSE C2.remainder + ((C3.rownum - C2.cutoffrow) - 1) / C2.basetilesize + 1 END AS tile FROM C3 CROSS JOIN C2;
Как обычно, не пытайтесь повторить это в производственной среде! Это пример предназначен для обучения, а его производительность в SQL Server ужасна по сравнению с функцией NTILE.
Функции RANK и DENSE_RANK
Функции RANK и DENSE RANK похожи на ROW_NUMBER, но в отличие от нее они не создают уникальные значения в оконной секции. При упорядочении окна по возрастанию «Обычный ранг» RANK вычисляется как единица плюс число строк со значением, по которому выполняется упорядочение, меньшим, чем текущее значение в секции. «Плотный ранг» DENSE_RANK вычисляется как единица плюс число уникальных строк со значением, по которому выполняется упорядочение, меньшим, чем текущее значение в секции. При упорядочении окна по убыванию RANK вычисляется как единица плюс число строк со значением, по которому выполняется упорядочение, большим, чем текущее значение в секции. DENSE_RANK вычисляется как единица плюс число уникальных строк со значением, по которому выполняется упорядочение, большим, чем текущее значение в секции.
Вот пример запроса, вычисляющего номера строк, ранги и «уплотненные» ранги. При этом используется секционирование окна по умолчанию и упорядочение по orderdate DESC:
SELECT orderid, orderdate, val, ROW_NUMBER() OVER(ORDER BY orderdate DESC) AS rownum, RANK() OVER(ORDER BY orderdate DESC) AS rnk, DENSE_RANK() OVER(ORDER BY orderdate DESC) AS drnk FROM Sales.OrderValues;
Атрибут orderdate не уникален. Но заметьте, что при этом номера строк уникальны. Значения RANK и DENSE_RANK не уникальны. Все строки с одной датой заказа, например 2008-05-05, получили одинаковый «неплотный» ранг 5 и «плотный» ранг 2. Ранг 5 означает, что есть четыре строки с большими (более поздними) датами заказов (упорядочение ведется по убыванию), а «плотный» ранг 2 означает, что есть одна более поздняя уникальная дата.
Альтернативное решение, заменяющее RANK и DENSE_RANK и не использующие оконные функции, создается просто:
SELECT orderid, orderdate, val, (SELECT COUNT(*) FROM Sales.OrderValues AS O2 WHERE O2.orderdate > O1.orderdate) + 1 AS rnk, (SELECT COUNT(DISTINCT orderdate) FROM Sales.OrderValues AS O2 WHERE O2.orderdate > O1.orderdate) + 1 AS drnk FROM Sales.OrderValues AS O1;
Для вычисления ранга определяется число строк с большим значением, по которому ведется упорядочение, (как вы помните, упорядочение ведется по убыванию) и к нему добавляется единица. Для вычисления плотного ранга нужно пересчитать уникальные большие значения, по которым ведется упорядочение, и добавить к полученному числу единицу.
Детерминизм
Как вы уже сами, наверное, поняли, что как RANK, так и DENSE_RANK детерминистичны по определению. При одном значении упорядочения — независимо от его уникальности — возвращается одно и то же значение ранга. Вообще говоря, эти две функции обычно интересны, если упорядочение неуникально. Если упорядочение уникально, они дают те же результаты, что и Функция ROW_NUMBER.
Как показать данные ранжирования в Excel
Даниэлла, член моей академии визуализации данных Evergreen, задала вопрос на нашем ежемесячном вебинаре Office Hours о том, как отображать данные рейтинга. Она отправила этот опросный вопрос:
Ниже представлены восемь категорий. Оцените каждый элемент, чтобы перейти к следующей странице. Расположите следующие услуги в порядке предпочтения (наиболее предпочтительный элемент вверху), где 1 = наиболее предпочтительный до 8 = наименее предпочтительный.
Служба А __
Служба В __
Служба C __
Служба D __
Служба E __
Служба F __
Служба G __
Служба H __
и она спросила:
Я использую Qualtrics, и прямо сейчас этот опрос собрал около 2900 ответов. Меня попросят предоставить заинтересованной стороне общие результаты по этому вопросу. Как я могу показать, что в целом люди присвоили определенный рейтинг определенной услуге?
Данные ранжирования может быть сложно понять, как визуализировать, потому что они являются частью целого, но не являются им. Данные, скорее всего, поступят из Qualtrics в виде таблицы, где сумма каждой строки равна 2,9.00 (при условии идеального мира без пропущенных данных).
Вероятно, только Qualtrics будет сортировать данные в том порядке, в котором сервисы были перечислены в опросе, и я пересортировал их здесь, чтобы запустить от меньшего к большему в рейтинге № 1.
Чтобы узнать, какой тип изображения лучше всего, нужно подумать о том, что ваша аудитория захочет увидеть. Эти заинтересованные стороны придут к вам, желая узнать, какая услуга получила наивысший рейтинг. Таким образом, простая гистограмма, упорядоченная от наибольшего к наименьшему (вот почему я запутался с таблицей), будет самым четким визуальным представлением. Обратите внимание, что я рисую только первые два столбца в таблице — только названия сервисов и то, как часто они занимали первое место.
Некоторые заинтересованные стороны могут захотеть узнать немного больше — например, топ-3 рейтинга — но гистограмма по-прежнему, вероятно, самое простое решение. (И как только вы остановитесь на гистограмме, вы всегда можете перейти к точечной диаграмме или диаграмме леденцов).
Это все, что вы можете взять из данных, учитывая один вопрос, который мы видим здесь в одном опросе. Но если со временем вы зададите этот вопрос одной и той же группе людей, вы сможете рассказать историю об изменениях в сервисах с самым высоким рейтингом, и столбчатая диаграмма больше не будет работать.
Я создал новую таблицу для этих данных рейтинга, предназначенную для отображения конечного рейтинга в каждом году (а не общего количества ответов на опросы или чего-то еще из исходной таблицы). Но если вы посмотрите внимательно, я не показываю реальный ранг — я показываю обратное. Сервис E был самым низким рангом на графике выше (допустим, это данные за 2014 год), а в таблице ниже сервис E указан как ранг № 1. Это имеет смысл, когда мы графически отображаем данные.
Я просто выделил таблицу и вставил простой линейный график. Excel создает линейный график с осью Y, которая проходит от 0 внизу до, в данном случае, 9на вершине. Таким образом, поменяв местами конечный ранг в таблице, услуга E оказалась в нижней части графика в 2014 году.
Затем я изменил ось Y так, чтобы она начиналась с 1, моего «самого низкого» ранга, и заканчивалась на 8, моем «самом низком» ранге. высшее» звание. Как только он был установлен, я удалил шкалу по оси Y и пометил оба конца каждой линии именем службы. Подробнее о том, как это сделать, см. в моем посте «Прямая маркировка в Excel». По сути, мы просто работаем с обычным линейным графиком. Настоящая работа заключается в том, чтобы продумать, как должна быть настроена таблица, чтобы она правильно отображалась на линейном графике — и в этом случае она вводит ранги в обратном порядке.
Во время нашего звонка в рабочее время я рассказал об этих вариантах и продемонстрировал как построить этот график. Затем я отправил файл обратно Даниэлле, чтобы у нее были готовые изображения, готовые к работе, как только ее настоящие данные поступят из Qualtrics. Это своего рода личный коучинг, который вы получаете, когда присоединяетесь к Академии.
7 визуализаций с помощью Python для отображения изменений ранга с течением времени | by Борихарн К | Ноябрь 2022 г.
Использование Python для визуализации изменений рейтинга с течением времени
Фото Austris Augusts на Unsplash
Данные ранжирования — это упорядочение данных в упорядоченном числовом ряду. Это простой способ передать информацию, поскольку он помогает читателю легко понять последовательность. Ранжирование — хорошая идея для работы с несколькими наблюдениями или категориальными данными.
Однако все постоянно меняется. С течением времени положение в рейтинге может постоянно меняться. Визуализация позиций рангов в течение периода помогает уведомлять об изменениях и прогрессе.
В этой статье вы найдете несколько идей, как визуализировать изменение ранга с течением времени.
Примеры визуализации данных с помощью Python в этой статье для представления изменений ранга с течением времени. Изображения автора
Приступим
Получить данные
Чтобы показать, что упомянутый здесь метод можно применить к наборам данных реального мира, я буду использовать набор данных «Загрязнение воздуха в Сеуле» от Kaggle (ссылка). Данные предоставлены столичным правительством Сеула (ссылка). Данные используются в соответствии с условиями лицензии Creative Commons License CC-BY.
Набор данных состоит из данных о загрязнении воздуха: SO2, NO2, CO, O3, PM10 и PM2,5, зарегистрированных в период с 2017 по 2019 год в 25 районах Сеула, Южная Корея.
В этой статье мы будем работать с угарным газом (CO), распространенным загрязнителем воздуха, который вреден для человека. Единицей измерения является часть на миллион (ppm).
Импорт данных
После загрузки набора данных начните с импорта библиотек.
импорт numpy как np
импорт pandas как pd
импорт math
импортировать matplotlib. pyplot как plt
импортировать seaborn как sns%matplotlib inline
Используйте Pandas для чтения ‘Measurement_summary.csv’
df = pd.read_csv('<расположение файла>/Measurement_summary.cs0'.cs0v'head()Изучение данных
Изучение набора данных в качестве первого шага всегда является хорошей идеей. К счастью, приведенный ниже результат показывает, что нам не нужно иметь дело с пропущенными значениями.
df.info()Посмотрим на общее число переменной «Код станции».
df['Код станции'].nunique()## output
## 25Всего 25 районов.
set(df['Код станции'])## вывод
## {101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
# # 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125}Выбор и подготовка данных
Например, я выберу коды станций 111-118. Если вы хотите отобразить другие номера станций, не стесняйтесь изменять приведенный ниже код.
list_stations = [111, 112, 113, 114, 115, 116, 117, 118]
df_select = df[df['Код станции'].isin(list_stations)]
df_select.head()Полученный набор данных не готов к нанесению. Некоторые столбцы необходимо создать или изменить перед использованием.
## crete year_month, столбцы года и месяца
year_month = [i[0:7] for i in list(df_select['Дата измерения'])]
df_select['year_month'] = year_month
df_select['year' ] = [i[0:4] для i в год_месяц]
df_select['month'] = [i[-2:] для i в year_month]## создать столбец с названием района
District = [i.split(', ')[2] для i в df_select['Address' ]]
df_select['Район'] = район## изменить тип столбца кода станции
df_select = df_select.astype({'Код станции': str})## groupby с указанием местоположения и момента времени
df_month = df_select.groupby(['Код станции','Район',
'год_месяца','год','месяц']).mean()
df_month.reset_index(inplace=True)
df_month. head()Это важный шаг. Основная идея этой статьи — создание визуализаций для ранжирования данных. Далее мы создадим столбец для ранжирования числа CO округов (ppm) в каждый момент времени.
keep = []
для i в списке (набор (df_month['year_month'])):
df = df_month[df_month['year_month']==i]
order = df['CO'].rank( по возрастанию=0)
df['rank'] = [int(i) для i по порядку]
keep.append(df)df_month = pd.concat(keep)
df_month.sort_values(['year_month', 'Код станции'], по возрастанию=Истина,
inplace=Истина, ignore_index=Истина)
df_month.head()Прежде чем продолжить, мы определим словарь цветов для облегчения процесс построения.
#извлечь цветовую палитру, палитру можно изменить .as_hex())
dict_color = dict(zip(list_dist, pal))Визуализация данных
В этой статье представлены некоторые идеи визуализации для ранжирования данных во времени. Таким образом, полученный результат должен быть прост для понимания, позволяя читателю сравнивать ранги данных между различными моментами времени.
Необходимо кое-что прояснить, прежде чем продолжить. У каждого графика есть свои плюсы и минусы. Конечно, нет ничего идеального. Некоторые идеи, представленные здесь, могут быть просто привлекательными. Но все они имеют одну и ту же цель — показать изменения в рангах данных с течением времени.
Диаграммы в этой статье можно разделить на две группы: анимации и диаграммы.
Анимация
Помимо того, что анимация является хорошей идеей для привлечения внимания, анимация может легко показать изменения ранга с течением времени.
1. Сравнение высоты гистограммы с анимированной гистограммой
Plotly — полезная графическая библиотека для создания интерактивных и анимированных графиков. Концепция применения анимированной гистограммы заключается в том, чтобы зафиксировать положение каждого района. Каждая полоса будет аннотирована номером ранжирования. Делая это, можно сравнивать количество CO с течением времени.
импортировать plotly. express как px
fig = px.bar(df_month, x='Район', y='CO',
color='Район', text='rank',
color_discrete_map= dict_color,
animation_frame= 'year_month',
animation_group='Код станции',
range_y=[0,1.2],
labels={ 'CO': 'CO (ppm)'},
)
fig.update_layout(width=1000, height= 600, showlegend=False,
xaxis = dict(tickmode = 'linear', dtick = 1))
fig.update_traces(textfont_size=16, textangle=0)
fig.show()Вуаля!!
Анимированная гистограмма показывает ежемесячный рейтинг районов и количество CO (ppm). Изображения автора.
Приведенный выше результат может показаться быстрым, так как это всего лишь пример результата. Не волнуйся; есть кнопка паузы для паузы и кнопка для выбора определенного момента времени.
2. Гонки с анимированным точечным графиком
Теперь изменим точку зрения, перемещая каждый район в соответствии с его рангом в разные моменты времени. Размеры точек рассеивания можно использовать для отображения номера CO.
Чтобы упростить построение графиков с помощью Plotly, нам нужно добавить еще два столбца в DataFrame, положение по оси X и текст для аннотации.
ym = list(set(year_month))
ym.sort()df_month['posi'] = [ym.index(i) for i in df_month['year_month']]
df_month['CO_str'] = [str(round(i,2)) для i в df_month['CO']]
df_month['CO_text'] = [str(round(i,2))+' ppm' для i в df_month['CO' ]]
df_month.head()Затем постройте анимированную точечную диаграмму.
import plotly.express as
px = px.scatter(df_month, x='posi', y='rank',
size= 'CO',
color='District', text='CO_text',
color_discrete_map = dict_color,
animation_frame='год_месяц',
animation_group='Район',
range_x=[-2,len(ym)],
range_y=[0.5,6.5]
)
fig.update_xaxes(title='', visible=False)
fig.update_yaxes(autorange='reversed', title='Rank',
visible=True, showticklabels=True)
fig.update_layout(xaxis=dict(showgrid=False),
yaxis=dict(showgrid=True))
fig. update_traces(textposition='middle left')
fig.show()Ta-da…
Анимированная точечная диаграмма показывает месячный рейтинг районов и количество CO(ppm). Изображения автора.
Диаграммы
Анимированные диаграммы обычно ограничены возможностью отображать один момент времени. Чтобы показать несколько моментов времени, можно применить некоторые диаграммы и методы для одновременного отображения многих моментов времени.
3. Рисование линий с помощью диаграммы рельефа
По сути, в диаграмме рельефа используется несколько линий, чтобы показать изменения рейтинга с течением времени. Построение диаграммы рельефа с помощью Plotly позволяет пользователям фильтровать результат и предоставлять дополнительную информацию при наведении курсора на каждую точку данных, как показано в результате ниже.
import plotly.express as px
fig = px.line(df_month, x = 'year_month', y = 'rank',
color = 'District',
color_discrete_map= dict_color,
markers=True,
hover_name = ' CO_text')
fig. update_traces(marker=dict(size=11))
fig.update_yaxes(autorange='reversed', title='Rank',
visible=True, showticklabels=True)
fig.update_xaxes(title= '', visible=True, showticklabels=True)
fig.update_layout(xaxis=dict(showgrid=False),
yaxis=dict(showgrid=False))
fig.show()Диаграмма рельефа показывает рейтинг районов и ежемесячное количество CO (ppm). Результат можно отфильтровать и предоставить дополнительную информацию, как показано. Изображения автора.
4. Создание фотоколлажа из гистограмм
Простая гистограмма может отображать ранжирование в определенный момент времени. Имея множество моментов времени, мы можем создать множество гистограмм, а затем объединить их в фотоколлаж. Начните с использования библиотеки Seaborn для создания гистограммы.
df_select = df_month[df_month['год_месяц']=='2017-01']
fig, ax = plt.subplots(figsize=(15, 6))sns.set_style('darkgrid')
sns.barplot(data = df_select,
x = 'Район', y = 'CO',
порядок =df_select. sort_values('CO', по возрастанию=False)['District'],
палитры=dict_color)
ax.bar_label(ax.containers[0],
labels=df_select.sort_values('CO', по возрастанию=False )['CO_str'],
label_type='edge', size=11)
plt.ylabel('CO (ppm)')
plt.title('2017-01')
plt.show()Бар диаграмма показывает количество CO (ppm) в районах по рейтингу. Изображения автора.
Используйте функцию цикла for для создания гистограмм в разные моменты времени. Пожалуйста, примите во внимание, что приведенный ниже код экспортирует диаграммы на ваш компьютер для последующего импорта.
keep_save = []
для t в ym:
df_ = df_month[df_month['year_month']==t]
fig, ax = plt.subplots(figsize=(8.5, 5))
sns.set_style(' darkgrid')
sns.barplot(data = df_,
x = 'Район', y = 'CO',
order = df_.sort_values('CO', по возрастанию = False) ['Район'],
палитра = dict_color )
ax.bar_label(ax.containers[0],
labels=df_.sort_values('CO', по возрастанию=False)['CO_str'],
label_type='edge', size=11)
plt. ylim([ 0, 1.2])
plt.ylabel('CO (ppm)')
plt.title(t)
plt.tight_layout()
s_name = t + '_bar.png'
keep_save.append(s_name)
plt. savefig(s_name)
plt.show()Создайте функцию для объединения диаграмм. Я нашел отличный код для объединения многих графиков по этой ссылке на Stack Overflow.
Применить функцию.
## get_collage(n_col, n_row, width, height, save_name, 'output.png')
# width = n_col * ширина фигуры
# height = n_row * высота фигурыget_collage(12, 3, 12*850, 3 *500, keep_save, 'order_bar.png')
Да-да…
Часть фотоколлажа, объединяющая гистограммы, показывает рейтинг округов и количество CO (ppm) в месяц. Изображения автора.
Результат показывает ежемесячный номер CO каждого округа, а также порядок ранжирования с течением времени. Таким образом, мы можем сравнивать ранги районов и количество загрязнений во многих моментах времени одновременно.
5. Представьте гистограммы с помощью круговой гистограммы
Используя ту же концепцию, что и в предыдущей идее, мы можем превратить обычные гистограммы в круговые гистограммы (также известные как графики гоночных треков) и объединить их в фотоколлаж.
Как упоминалось ранее, у всего есть свои плюсы и минусы. Каждый столбец на круговой диаграмме может быть трудно сравнивать из-за неравного отношения длины каждого столбца. Однако это можно считать хорошим вариантом для создания привлекающего внимание эффекта.
Начните с примера создания круговой гистограммы.
На круговой гистограмме показано количество выбросов CO (частей на миллион) в районах по ранжированию. Изображения автора.
Применение функции цикла for для получения других круговых гистограмм. Результаты будут экспортированы на ваш компьютер для последующего импорта.
Используйте функцию для создания фотоколлажа.
get_collage(12, 3, 12*860, 3*810, keep_cir, 'order_cir.png')Часть фотоколлажа, объединяющая круглые гистограммы, показывает рейтинг округов и количество CO(ppm) в месяц. Изображения автора.
6. Еще один способ представить гистограммы с помощью радиальной гистограммы
Изменение направления гистограмм, чтобы начать с центра с радиальными гистограммами. Это еще одна идея для привлечения внимания. Однако можно заметить, что бары, не расположенные рядом друг с другом, трудно сравнивать.
Начните с примера радиальной гистограммы.
Радиальная столбчатая диаграмма показывает количество выбросов CO (ppm) в районах по рейтингу. Изображения автора.
Применение функции цикла for для создания других радиальных гистограмм. Результаты также будут экспортированы на ваш компьютер для последующего импорта.
Применить функцию для получения фотоколлажа.
get_collage(12, 3, 12*800, 3*800, keep_rad, 'order_rad.png')Часть фотоколлажа, объединяющая радиальные гистограммы, показывает рейтинг округов и количество CO(ppm) в месяц. Изображения автора.
7. Использование цвета с тепловой картой
Обычно тепловая карта представляет собой обычную диаграмму для представления данных в виде двумерной диаграммы и отображения значений с помощью цветов. В нашем наборе данных можно применить цвет для отображения номеров рангов.
Начните с создания сводной таблицы с помощью pd.pivot().
df_pivot = pd.pivot(data=df_month, index='District',
columns='year_month', values='rank')
df_pivotПолучив сводную таблицу, мы можем легко создать тепловую карту всего лишь несколько строк кода.
plt.figure(figsize=(20,9.5))
sns.heatmap(df_pivot, cmap='viridis_r', annot=True, cbar=False)
plt.show()Применена тепловая карта для отображения изменений в районах ' рейтинг с течением времени. Изображения автора.
С помощью цвета и аннотации мы можем определить район с самым высоким (желтый цвет) и самым низким (темно-синий цвет) количеством CO. Изменения в рейтинге можно заметить с течением времени.
Резюме
В этой статье представлены семь идей визуализации с помощью кода Python для отображения изменений рангов данных с течением времени. Как упоминалось ранее, у всего есть свои плюсы и ограничения. Важно найти правильную диаграмму, которая соответствует данным.