В первом посте обучающей серии DS4CS я поделился кодом, использованным для создания моего первого рассказа о Чрезвычайной субсидии на заработную плату в Канаде, которая была корпоративным безумием исторических масштабов. В этом посте я покажу, как создавать визуальные эффекты для истории CEWS с помощью ggplot2, сосредоточив внимание на маркированных диаграммах. Попутно я также поделюсь несколькими советами по более эффективной работе с R, включая итерацию с map и автоматизацию создания диаграмм с помощью пользовательских функций.

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

Подожди! Что такое пулевая диаграмма?

Пулевая диаграмма — это обновленная версия гистограммы с гистограммой внутри гистограммы, созданная экспертом по визуализации данных и приборным панелям Стивеном Фью. Маркированные диаграммы являются подходящим дополнением к гистограмме в случаях, когда требуется сравнение двух количественных переменных, часто связанных с третьей группирующей переменной. Что отличает маркированную диаграмму от обычной гистограммы?

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

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

От Data Viz к Data Storytelling

Было бы вполне возможно и правильно (также, вероятно, по умолчанию во многих программах, таких как Excel), визуализировать эти данные в виде сгруппированной гистограммы, как показано ниже, с желтыми полосами, обозначающими net_profits, и красными полосами. представляет subsidy, полученных отраслью. Это доносит основную идею: большинство отраслей получили миллиардные субсидии, получая гораздо больше чистой прибыли. Но в любом случае это не особо впечатляющая визуализация.

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

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

Кроме того, формат маркированной диаграммы позволяет добавлять метки значений, обозначающие внутреннюю полосу в процентах от внешней полосы. Просматривая сгруппированную линейчатую диаграмму, мы часто пытаемся произвести в уме такой расчет: «Этот столбец составляет сколько процентов от того столбца?» На маркированной диаграмме слои столбцов и то, как они внутренний стержень закрепляет основание стержня, уже привлекая внимание зрителя, чтобы увидеть его. Этот тип маркировки интуитивно понятен для гистограммы, но его будет сложно реализовать на обычной гистограмме с группировкой.

Получение и подготовка данных

Использование map и анонимных функций для итерации в эффективных рабочих процессах обработки данных

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

Одна из замечательных особенностей перехода от рабочих процессов укажи и щелкни в программном обеспечении, таком как Excel, к функциональному языку сценариев, такому как R, — это возможность значительно повысить эффективность и результативность вашей работы, одновременно снизив нагрузку на чрезмерно утомительные, но общие данные. задачи науки. Не повторяйся — базовый принцип хорошего кодинга. Если часть кода будет реплицироваться несколько раз, почти всегда лучше использовать функции и/или итерацию. Это не только сэкономит ваше время и усилия за счет автоматизации, но и избавит вас от многих головных болей в будущем, поскольку более краткий код с меньшей вероятностью сломается и его будет легче отлаживать, когда он сломается.

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

Вот простой пример DRY в действии с использованием функции map из абсолютно необходимого пакета purrr. Функция map принимает любой вектор или список в качестве первого аргумента .x, а второй аргумент .f — это вызов функции, который перебирает каждый элемент в .x. Вместо того, чтобы трижды вводить вызов read_rds и here с полным путем к файлу, мы можем использовать анонимную функцию в пределах map до paste вектора строк cews_paths в пути к файлу, чтобы импортировать все три таблицы. в один звонок. Анонимная функция может быть определена на лету без необходимости присваивать ее объекту в среде. Если понятие анонимной функции для вас новое, то стоит прочитать краткое введение здесь.

purrr имеет специальный сокращенный синтаксис ~, который делает использование анонимных функций в вызовах map еще более простым и кратким. Поместите ~ непосредственно перед вызовом функции .f в map и используйте .x для управления тем, где в функции будут размещены элементы, которые вы хотите перебрать. Если это возвращает объект ошибки .x не найден, вы, вероятно, забыли использовать ~, чтобы сообщить R, что это анонимная функция, поэтому она ищет фактический объект с именем .x и не перебирает элементы, предоставленные .x.

Работа со списками в R

По умолчанию map возвращает результат предоставленной функции в виде list с одним элементом для каждой итерации свыше .x. В этом случае по одной табличке для каждого пути к файлу.

## [1] "list"

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

## $profits 
## # A tibble: 13 x 4 
## naics_final net_profits subsidy pct_profits 
## <fct> <dbl> <dbl> <dbl> 
## 1 Manufacturing 66407000000 16398789000 0.247 
## 2 Construction 51026000000 11274636000 0.221 
## 3 Arts/Acc/Food/Ent/Rec -5013000000 10584779000 -2.11 
## 
## $size 
## # A tibble: 3 x 6 
## claim_period size_of_applicant applications subsidy pct_total pct_total_subsi~ 
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> 
## 1 All Periods Small (25 or few~ 3352710 2.37e10 0.776 0.256 
## 2 All Periods Medium (26 to 25~ 901540 3.88e10 0.209 0.419 
## 3 All Periods Large (over 250 ~ 56920 3.01e10 0.013 0.325 
## 
## $lobby 
## # A tibble: 50 x 6 
## id ref_date corp ref_year source subsidy 
## <dbl> <date> <chr> <dbl> <chr> <dbl> 
## 1 914017 2021-07-15 Air Canada 2021 Canada Revenue Ag~ 5.86e8 
## 2 911084 2021-04-15 Suncor Energy Inc. 2021 Canada Revenue Ag~ 
## 
# ... with 40 more rows

После импорта данных в последнюю минуту вносятся некоторые быстрые корректировки, чтобы подготовить данные для построения графика. Один из способов работы со списками — использование оператора подмножества $ для доступа к элементам списка по имени. В таблице profits отфильтровываются убыточные отрасли (хотя их можно включить в диаграмму с некоторыми дополнительными усилиями) и устанавливаются уровни факторов в порядке убывания net_profits. Мы делаем последнее, потому что при использовании ggplot2 для построения графика уровни будут порядком построения элементов по умолчанию.

Доступ к элементам списка также можно получить по числовому индексу list[[n]], как это делается для получения 15 крупнейших получателей CEWS из таблицы lobby ниже.

Мастерское создание маркированных диаграмм с помощью ggplot2

Основная структура данных для маркированной диаграммы

Базовая структура данных, необходимая для создания маркированной диаграммы с помощью ggplot2, выглядит следующим образом:

  • Данные должны быть в форме фрейма данных или таблицы.
  • Категориальная переменная x, формирующая категории осей
  • Категориальная переменная z с метками для двух количественных переменных
  • Числовая переменная y со значениями, связанными с x и z
  • Столбец ширины, обозначающий переменную ширину столбцов маркированной диаграммы.

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

## # A tibble: 2 x 4 
## x z y width 
## <chr> <chr> <dbl> <dbl> 
## 1 Total, all industries profits 301022000000 0.7 
## 2 Total, all industries subsidy 92794229000 0.3

Использование грамматики графики для построения базовой маркированной диаграммы

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

Переменные, которые вы хотите сопоставить с эстетическими выводами для создания диаграмм, необходимо передать функции aes() в вызове ggplot(). После сопоставления эстетики с помощью aes формы добавляются к графику с помощью функций geom_, в данном случае добавляя полосы с помощью geom_col() и захватывая ширину полос из исходной таблицы с помощью $width.

Систему координат графика можно изменить с помощью функций coord_. Тип создаваемого графика будет варьироваться в зависимости от различных комбинаций геометрии и координат. После добавления столбцов на диаграмму измените координаты, чтобы превратить ее в горизонтальную гистограмму с помощью coord_flip(). Количество geoms, которые может иметь ggplot, не ограничено, если они соответствуют вашим данным допустимыми способами, но диаграмма может (обычно) вмещать только одну систему координат.

Вывод диаграммы по умолчанию без каких-либо изменений стиля и масштаба обычно не будет выглядеть великолепно. Масштабы осей и масштабы, сопоставленные с эстетическими переменными (например, цвет, заливка, размер, прозрачность), можно добавлять и настраивать с помощью функций scale_ всех типов. Теперь мы можем добавить пользовательские цвета заливки с помощью scale_fill_manual и настроить единицы измерения меток шкалы оси с помощью scale_y_continuous. Уже выглядит немного лучше.

Добавление меток значений к столбчатым диаграммам

Метки значений можно добавлять к линейчатым диаграммам, используя geom_text и передавая числовые значения эстетике label. Для этих маркированных диаграмм мы хотим добавить метку значения, показывающую внутреннюю полосу в процентах от внешней полосы. По умолчанию geom_text будет вставлять метки значений для каждой серии, но мы хотим отображать только одну метку для внутренней панели. Часто при создании гистограмм требуется добавить метки значений только к определенным столбцам, что можно сделать, изменив аргумент data вызова на geom_text и отфильтровав фрейм данных только для значений, которые будут отображаться в качестве меток.

Быстро улучшите внешний вид диаграммы, применив функцию theme_ из пакета hrbrthemes. У нас есть базовая диаграмма. В общей сложности канадские корпорации получили более 300 миллиардов долларов прибыли с 2020 года по третий квартал 2021 года, при этом на долю CEWS приходится чуть менее трети этой общей суммы.

Удовольствие от создания диаграмм с помощью функционального программирования

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

Как язык функционального программирования, функции — это главная фишка R. Пользовательскую функцию можно определить, назначив вызов function (сама функция) объекту в среде с оператором <-.

Ниже я определил функцию, которая принимает фрейм данных в качестве входных данных, преобразует столбец сравнения в длинную форму с помощью pivot_longer и вставляет ширину столбцов по группам с помощью mutate и rep. Оттуда функция автоматически создает базовую столбчатую диаграмму, помещая входные данные, захваченные точками ... в третьем аргументе, которые принимают произвольное количество входных данных, в эстетические отображения от ggplot2 до aes.

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

  1. df принимает фрейм данных или табличку
  2. pivot_by обозначает столбцы числовым индексом, чтобы свернуть в более длинный в один столбец для сравнения
  3. ... использует имена переменных, разделенных запятыми, в качестве входных данных для aes для создания диаграммы.
  4. widths управляет шириной внешней и внутренней полосы соответственно. Это необязательный аргумент, так как ему присвоено значение по умолчанию.

Использование аккуратных вычислений и пользовательских функций с ggplot2

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

Во-первых, краткое примечание об использовании пакетов из незаменимых tidyverse (включая ggplot2) в ваших собственных пользовательских функциях. Для меня понимание того, как R оценивает аргументы функций, а затем то, как заставить это работать в tidyverse, было главным препятствием, которое нужно было преодолеть при обучении написанию и использованию функций. Если вы новичок в функциональном программировании с помощью R и tidyverse, вам стоит более подробно прочитать об аккуратной оценке.

В базе R необходимо использовать df$var или df["var"] для ссылки на столбец фрейма данных. Это становится утомительным и грязным очень быстро. В tidyverse, благодаря маскировке данных, мы можем просто напрямую использовать имена переменных без необходимости каждый раз подмножать фрейм данных с $ или [. Чтобы использовать голые имена переменных в качестве входных данных в пользовательских функциях, мы должны сначала заключить входные данные в кавычки с помощью enquo, а затем снять их с кавычек, чтобы использовать их в теле функции с оператором без кавычек !!.

Оттуда ввод в filter_var, также имя переменной без кавычек, преобразуется в строку для подачи в filter с использованием deparse и substitute. Исходные данные, используемые для воссоздания графика, можно получить из любого объекта ggplot, обратившись к ggplot$data, поэтому мы берем исходные данные для фильтрации из исходной диаграммы, переданной в plot.

Вишенкой на торте станет собственная функция theme.

ggplot2 предлагает невероятный уровень настройки и контроля над тематическими элементами сюжета с помощью функции theme, которая может контролировать более 100 различных элементов сюжета. Используйте theme, чтобы добавить последние штрихи, контролируя такие элементы, как горизонтальная или вертикальная ось, метки, сетки, легенды, поля и многое другое. Также можно использовать функцию для хранения и эффективного применения пользовательской темы ggplot, как показано ниже.

Добавляем все вместе сейчас

Здесь мы объединим все три вызова функций вместе с конвейерами %>%, чтобы создать маркированную диаграмму с количественными данными другого типа, сравнивая процент CEWS, полученный красным цветом, с процентом от общего числа приложений CEWS, выделенных желтым цветом, в зависимости от размера предприятия. Чтобы закончить, преобразуйте горизонтальную ось в формат процентов и используйте анонимную функцию (~ не будет работать вне функций purrr), чтобы обернуть длинные текстовые метки.

БОНУСНАЯ ДИАГРАММА и отказ от легенд для причудливого текста в названиях сюжетов и подзаголовках

Но ждать! Остался еще один график; это не что иное, как маркированная диаграмма, поэтому мы назовем ее бонусной диаграммой. Я действительно не видел, чтобы кто-то использовал подобную диаграмму раньше, поэтому я не уверен, как это назвать. Текст как столбчатая диаграмма, возможно? Формат данных для этой диаграммы идентичен обычной гистограмме, но вместо добавления столбцов с geom_col или geom_bar используйте geom_text, чтобы вставить метки значений непосредственно там, где должны быть столбцы.

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

Это возможно благодаря замечательному пакету ggtext, который значительно улучшает поддержку рендеринга текста для ggplot. Используя element_markdown, можно включить как уценку, так и html в текст графика.

Первоначально опубликовано на https://ds4cs.netlify.app 3 декабря 2021 г.