Как включить процентные изменения, подобные think-cell, в каскадную диаграмму, созданную в ggplot2

Я пытаюсь сделать R инструментом визуализации данных в своей компании. Типичным типом графика, используемым в моем отделе, являются каскадные диаграммы (https://en.wikipedia.org/wiki/Waterfall_chart).

В R есть несколько пакетов и подсказок для ggplot для создания каскадной диаграммы (https://learnr.wordpress.com/2010/05/10/ggplot2-waterfall-charts/), которым я уже пользовался.

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

См. пример ниже:

Аннотации процентного изменения к линейчатой/водопадной диаграмме

Или вот в этом видео (https://www.youtube.com/watch?v=WMHf7uFR6Rk< /а>)

Программное обеспечение, используемое для создания таких графиков, — это think Cell (https://www.think-cell.com/), который является дополнением к Excel и Powerpoint.

У меня проблема в том, что я не знаю, как начать заниматься этой темой. Мои первые мысли идут в этом направлении:

  • Используйте geom_segment для создания стрелок и прямоугольников.
  • Используйте функцию аннотирования ggplot, чтобы разместить текст на стрелках или в полях.
  • Рассчитывайте позиции автоматически на основе данных, предоставленных на каскадной диаграмме.

Могу я спросить вас, есть ли у вас дополнительные мысли/идеи по реализации таких графиков в ggplot?

С наилучшими пожеланиями Маркус Полезно Бесполезно


person Markus Maly    schedule 26.12.2018    source источник
comment
Я бы подошел к этому, используя функцию для добавления необходимых слоев на ваш график, принимая в качестве параметров исходную таблицу, начальный и конечный годы и, возможно, что-то, чтобы указать вертикальное смещение. У него могут быть следующие слои: 1) geom_segment со стрелкой; 2) эллипс (в ggforce может быть такой?); 3) текстовое поле с чем-то вроде paste0 (CAGR\n, first_yr, -, last_yr, \n, [change_coefficient]^[1/years_elapsed] - 1 %›% [formatting_code])   -  person Jon Spring    schedule 27.12.2018


Ответы (1)


Вот пример подхода, который я бы выбрал.

Шаг 1. Выберите, какие элементы следует добавить, и добавляйте их по одному.

Допустим, мы начинаем с этой простой диаграммы:

df <- data.frame(x = c(2007, 2008, 2009),
                 y = c(100, 120, 140))
ggplot(df, aes(x, y, label = y)) +
  geom_col() +
  geom_text(vjust = -0.5)

введите здесь описание изображения

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

ggplot(df, aes(x, y, label = y)) +
  geom_col() +
  geom_text(vjust = -0.5) +
  scale_y_continuous(expand = expand_scale(add = c(10, 50))) # Add 50 y padding

введите здесь описание изображения

Теперь я постепенно добавляю слои, пока не будет выглядеть так, как я хочу:

# Semi-manual proof of concept
ggplot(df, aes(x, y, label = y)) +
  geom_col() +
  geom_text(vjust = -0.5) +
  scale_y_continuous(expand = expand_scale(add = c(10, 50))) + # Add 50 y padding

  # Line with arrow
  geom_segment(aes(x    = df$x[3], y    = df$y[3] + 50,
                   xend = df$x[3], yend = df$y[3] + 50), 
               arrow = arrow(length = unit(0.02, "npc"), type = "closed")) +

  # Background box
  geom_tile(aes(x = mean(c(df$x[3], df$x[3])),
                y = mean(c(df$y[3], df$y[3])) + 50, width = 1, height = 40), 
            fill = "white", color = "black", size = 0.5) +

  # Text
  geom_text(aes(x = mean(c(df$x[3], df$x[3])),
                y = mean(c(df$y[3], df$y[3])) + 50,
                label = paste0("CAGR\n", 
                               df$x[3], "-", df$x[3], "\n", 
                               scales::percent((df$y[3] / df$y[3]) ^ (1/(df$x[3]-df$x[3])) - 1))))

введите здесь описание изображения

Шаг 2. Превратите это в функцию

Теперь я перемещаю слои, связанные с CAGR, в функцию, заменяя большинство констант параметрами функции.

add_CAGR <- function(df, first_val_pos, second_val_pos, 
                     y_offset, box_width = 1, box_height) {
  list(
    # Line with arrow
    geom_segment(aes(x    = df$x[first_val_pos], 
                     xend = df$x[second_val_pos], 
                     y    = df$y[first_val_pos]  + y_offset,
                     yend = df$y[second_val_pos] + y_offset), 
                 arrow = arrow(length = unit(0.02, "npc"), type = "closed")), 

      # Background box
      geom_tile(aes(x = mean(c(df$x[first_val_pos], df$x[second_val_pos])),
                    y = mean(c(df$y[first_val_pos], df$y[second_val_pos])) + y_offset, 
                    width = box_width, height = box_height), 
                fill = "white", color = "black", size = 0.5),

      # Text
      geom_text(aes(x = mean(c(df$x[first_val_pos], df$x[second_val_pos])),
                    y = mean(c(df$y[first_val_pos], df$y[second_val_pos])) + y_offset,
                    label = paste0("CAGR\n", 
                                   df$x[first_val_pos], "-", df$x[second_val_pos], "\n", 
                                   scales::percent((df$y[second_val_pos] / df$y[1]) ^ 
                                                     (1/(df$x[second_val_pos]-df$x[first_val_pos])) - 1))),
                lineheight = 0.8)
  )
}

Шаг 3: Используйте в сюжете

ggplot(df, aes(x, y, label = y)) +
  geom_col() +
  geom_text(vjust = -0.5) +
  scale_y_continuous(expand = expand_scale(add = c(0, 50))) + # Add 50 y padding
  add_CAGR(df, first_val_pos = 1, second_val_pos = 3, 
           y_offset = 50,
           box_width = 0.7, box_height = 40)

введите здесь описание изображения

Или то же самое только между первыми двумя тактами:

ggplot(df, aes(x, y, label = y)) +
  geom_col() +
  geom_text(vjust = -0.5) +
  scale_y_continuous(expand = expand_scale(add = c(0, 50))) + # Add 50 y padding
  add_CAGR(df, first_val_pos = 1, second_val_pos = 2, 
           y_offset = 50,
           box_width = 0.7, box_height = 40)

введите здесь описание изображения

person Jon Spring    schedule 27.12.2018
comment
Дорогой Джон! Большое спасибо за развернутый ответ. С наилучшими пожеланиями. Маркус - person Markus Maly; 27.12.2018