Создание анимированной пузырьковой диаграммы Ханса Рослинга на языке R

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

В процессе работы у меня возникло несколько вопросов.

  • Смогу ли я создать диаграмму с помощью экономических данных, взятых у Всемирного банка, без необходимости использовать локальные файлы данных или пакетные данные, которые могут оказаться неактуальными?
  • Получится ли создать нечто, близкое к подлинной диаграмме Рослинга?
  • Можно ли сделать это посредством одной команды на языке R?

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

  • Полную версию кода можно найти на Github.

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

Настройка и сбор данных

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

  • dplyr;
  • ggplot2;
  • gganimate;
  • viridis для работы с цветом;
  • wbstats для получения данных из Всемирного банка.

Чтобы все выглядело точно так же, как у меня, вам понадобится установить семейство шрифтов Oswald из Google Fonts. Однако это не обязательно.

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

  • Ось X: логарифм (Log) ВВП на душу населения (использование Log помогает распределить данные, которые в противном случае будут довольно асимптотическими).
  • Ось Y: ожидаемая продолжительность жизни с момента рождения.
  • Размер пузыря будет пропорционален численности населения.
  • Цветовая кодировка пузыря зависит от экономических регионов, которые соответствуют регионам Всемирного банка.
  • Переход год за годом с 1960 года до последних доступных данных (2020 год на момент написания этой статьи).

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

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

Например, если вы ищете GDP per Capita (current US$)  —  ВВП на душу населения по текущему курсу американского доллара,  —  то попадете на эту страницу. Далее нажимаете на кнопку “Details” (“Подробности”) и видите ID. В данном случае это NY.GDP.PCAP.CD.

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

rosling <-
# получение данных по странам из Всемирного банка - три показателя
wbstats::wb_data(
indicator = c("SP.DYN.LE00.IN", "NY.GDP.PCAP.CD", "SP.POP.TOTL"),
country = "countries_only",
start_date = 1960,
end_date = 2020
)

Аргумент country = "countries_only" важен, поскольку данные Всемирного банка также включают региональные и общемировые средние показатели, которые в данном случае не понадобятся.

Это почти все, что нам нужно из данных. Но для цветового кода необходимо еще присвоить странам регионы Всемирного банка. В wbstats есть удобная функция wb_countries(), позволяющая выбрать код страны iso3c и ее регион и присоединить к предыдущей таблице, чтобы присвоить странам регионы. Это делается следующим образом:

rosling <-
# получение данных по странам из Всемирного банка - три показателя
wbstats::wb_data(
indicator = c("SP.DYN.LE00.IN", "NY.GDP.PCAP.CD", "SP.POP.TOTL"),
country = "countries_only",
start_date = 1960,
end_date = 2020
) |>
# получение мэппинга стран и регионов и присоединение
dplyr::left_join(
wbstats::wb_countries() |>
dplyr::select(iso3c, region)
)

В итоге у вас должен получиться подобный датафрейм:

Изображение сгенерировано автором

Создание статического графика для одного года

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

Построение графиков с помощью ggplot2 интуитивно понятно. Вот алгоритм действий.

  • Определение эстетики построения: x  —  логарифм ВВП на душу населения, y  —  продолжительность жизни, size  —  численность населения, color  —  регион (использую функцию ggplot()).
  • Установка подобия графика разброса со значением alpha 0,5, чтобы придать пузырькам некоторую прозрачность (использую geom_point()).
  • Масштабирование размеров пузырьков, чтобы они соответствовали внешнему виду и стилю графика, с помощью scale_size().
  • Установка границ для осей с помощью scale_x_continuous() и scale_y_continuous().
  • Установка красивой цветовой схемы пузырьков с помощью viridis::scale_color_viridis().
  • Установка меток осей с помощью labs().
  • Помещение графика в красивую чистую тему с помощью theme_classic().
  • Установка даты на светло-сером фоне в центре, чтобы она не мешала визуальному восприятию (в соответствии с оригиналом Рослинга). Ее можно установить с помощью geom_text().

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

# постройте график данных (передайте в предыдущий код с помощью |>)
ggplot2::ggplot(
aes(x = log(NY.GDP.PCAP.CD),
y = SP.DYN.LE00.IN,
size = SP.POP.TOTL)
) +
ggplot2::geom_point(alpha = 0.5, aes(color = region)) + ggplot2::scale_size(range = c(.1, 16), guide = "none") + ggplot2::scale_x_continuous(limits = c(2.5, 12.5)) + ggplot2::scale_y_continuous(limits = c(30, 90)) + viridis::scale_color_viridis(
discrete = TRUE,
name = "Region",
option = "viridis"
) +
ggplot2::labs(
x = "Log GDP per capita",
y = "Life expectancy at birth"
) +
ggplot2::theme_classic() +
ggplot2::geom_text(
aes(x = 7.5, y = 60, label = date),
size = 14,
color = 'lightgrey',
family = 'Oswald'
)

Анимация графика

Это последняя и самая простая часть работы. Теперь, когда статическая анимация готова, нужно просто использовать пакет gganimate для ее “оживления”. gganimate нужно лишь знать, что является переменной перехода (в данном случае это столбец даты) и некоторые детали, касающиеся тайминга и типа перехода. Вы можете решить эти задачи, добавив простую команду анимации:

# создайте анимацию, показывающую движение тренда в течение нескольких лет (добавьте к предыдущему коду с помощью +) 
gganimate::transition_states(
date,
transition_length = 1,
state_length = 1
) +
gganimate::ease_aes('cubic-in-out')

Если вы эффективно скомбинировали код, то увидите результат, набрав в консоли rosling. Здесь я убрал легенду цвета для лучшего эффекта. Вы можете сделать это, добавив аргумент guide = "none" к команде scale_color_viridis() выше.

Gif создано автором

Вы можете сохранить объект анимации rosling как gif-файл и задать некоторые параметры, используя, например, anim_save():

# сохраните анимацию в виде gif-файла
anim_save(
"rosling_noguide.gif",
rosling,
nframes = 125,
height = 600,
width = 1000
)

Есть и другие варианты рендеринга, в том числе рендеринг видео, с которыми вы можете поэкспериментировать, если у вас в системе установлен рендерер типа FFMPEG.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Keith McNulty: How to create Hans Rosling’s famous animated bubble chart in R

Предыдущая статьяРешение крупномасштабных задач машинного обучения на Python
Следующая статьяКак использовать типы пересечения в TypeScript: советы от профессионала