Как предотвратить перекрытие меток в гистограмме с накоплением ggplot?

Даны эти два набора данных, df_bars и df_lines:

library (ggplot2)
library (ggtext)

df_bars <-
  structure(
    list(
      col1 = structure(
        c(
          1L,
          2L,
          3L,
          4L,
          5L,
          6L,
          7L,
          8L,
          9L,
          10L,
          11L,
          12L,
          1L,
          2L,
          3L,
          4L,
          5L,
          6L,
          7L,
          8L,
          9L,
          10L,
          11L,
          12L,
          1L,
          2L,
          3L,
          4L,
          5L,
          6L,
          7L,
          8L,
          9L,
          10L,
          11L,
          12L,
          1L,
          2L,
          3L,
          4L,
          5L,
          6L,
          7L,
          8L,
          9L,
          10L,
          11L,
          12L,
          1L,
          2L,
          3L,
          4L,
          5L,
          6L,
          7L,
          8L,
          9L,
          10L,
          11L,
          12L
        ),
        levels = c(
          "AAA\nN =  172",
          "BBB",
          "CCC",
          "DDD",
          "EEE",
          "FFF",
          "GGG",
          "HHH",
          "III",
          "JJJ",
          "KKK",
          "LLL"
        ),
        class = c("ordered",
                  "factor")
      ),
      Status = structure(
        c(
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          1L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          2L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          3L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          4L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L,
          5L
        ),
        levels = c("x", "aaa.kp", "bbb.kp",
                   "ccc.kp", "ddd.kp"),
        class = "factor"
      ),
      Values = c(
        1,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        NA,
        0,
        0,
        0,
        0,
        0,
        0.0813953488372093,
        0.226744186046512,
        0.36046511627907,
        0.447674418604651,
        0.476744186046512,
        0.476744186046512,
        NA,
        0.941860465116279,
        0.831395348837209,
        0.738372093023256,
        0.703488372093023,
        0.680232558139535,
        0.581395348837209,
        0.418604651162791,
        0.273255813953488,
        0.186046511627907,
        0.151162790697674,
        0.0523255813953488,
        NA,
        0,
        0,
        0.0406976744186047,
        0.0406976744186047,
        0.0523255813953488,
        0.0523255813953488,
        0.0581395348837209,
        0.0581395348837209,
        0.0581395348837209,
        0.0581395348837209,
        0.0581395348837209,
        NA,
        0.0581395348837209,
        0.168604651162791,
        0.22093023255814,
        0.255813953488372,
        0.267441860465116,
        0.284883720930233,
        0.296511627906977,
        0.308139534883721,
        0.308139534883721,
        0.313953488372093,
        0.412790697674419
      )
    ),
    row.names = c(NA, -60L),
    class = "data.frame"
  )


df_lines <-
  structure(
    list(
      col1 = structure(
        1:12,
        levels = c(
          "AAA\nN =  172",
          "BBB",
          "CCC",
          "DDD",
          "EEE",
          "FFF",
          "GGG",
          "HHH",
          "III",
          "JJJ",
          "KKK",
          "LLL"
        ),
        class = c("ordered",
                  "factor")
      ),
      aaa.l.rev = c(
        NA,
        1,
        1,
        1,
        1,
        1,
        0.968604651162791,
        0.854651162790698,
        0.866279069767442,
        0.912790697674419,
        0.97093023255814,
        1
      ),
      eee.l = c(
        NA,
        0.0381395348837209,
        0.09046511627907,
        0.0930232558139535,
        0.0348837209302326,
        0.0232558139534884,
        0.0174418604651163,
        0.0174418604651163,
        0.0116279069767442,
        0,
        0.00581395348837209,
        0.0988372093023256
      )
    ),
    class = "data.frame",
    row.names = c(NA, -12L)
  )

Я создаю столбчатую диаграмму с накоплением, с которой пока все в порядке. Здесь я также отображаю метки значений.

Кроме того, есть две строки, где также отображаются метки значений.

    ggplot(df_bars, aes(x = col1, y = Values)) +
  geom_bar(
    data = df_bars,
    mapping = aes(x = col1, y = Values, fill = Status),
    position = "stack",
    stat = "identity"
  ) +
  geom_text(
    data = df_bars,
    aes(
      x = col1,
      y = Values,
      geom = Status,
      label = ifelse(Values >= 0.01, paste0 (sprintf(
        "%1.1f%%", 100 * Values
      )), "")
    ),
    size = 2.5,
    position = position_stack(vjust = 0.5),
    color = (farbe <- c("white", rep(c(
      "black"
    ), times = 55)))
  ) +
  geom_line(data = df_lines,
            mapping = aes(
              x = col1,
              y = eee.l,
              group = 1,
              colour = "Lorem"
            )) +
  geom_line(data = df_lines,
            mapping = aes(
              x = col1,
              y = aaa.l.rev,
              group = 1,
              colour = "Ipsum"
            )) +
  geom_text(
    data = df_lines,
    mapping = aes(
      x = col1,
      y = aaa.l.rev,
      label = ifelse(aaa.l.rev >= 0.1, paste0 (sprintf(
        "%1.1f%%", -100 * aaa.l.rev + 100
      )), "")
    ),
    size = 2.5,
    position = position_stack(vjust = .98)
  ) +
  geom_text(
    data = df_lines,
    mapping = aes(
      x = col1,
      y = eee.l,
      label = paste0 (sprintf("%1.1f%%", 100 * eee.l))
    ),
    size = 2.5,
    position = position_stack(vjust = .9)
  ) +
  scale_y_continuous(expand = c(NA, 100),
                     labels = NULL) +
  labs(title = "Title\n\n", colour = "") +
  labs(caption = paste(
    sprintf("**Bla/Blubb**: %s", "Blubb"),
    sprintf("\n\n**As of**: %s", format(Sys.time(), "%b %Y"))
  )) +
  ylab ("") + xlab ("") +
  scale_x_discrete(labels = df_bars$col1, position = "top") +
  coord_cartesian(clip = "off") +
  guides(fill = guide_legend(title = "")) +
  theme(
    legend.position = "top",
    legend.direction = "horizontal",
    legend.box = "vertical",
    legend.box.just = "left",
    legend.spacing.x = unit(0.2, 'cm'),
    legend.box.spacing = unit(0, "pt"),
    legend.margin = margin(0, 0, 0, 0),
    legend.text = element_text(size = 7, color = "black"),
    legend.background = element_rect(fill = "white", colour = "white"),
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.x = element_line(color = "#EAEAEA"),
    panel.grid.major.y = element_line(color = "#EAEAEA"),
    plot.background = element_rect(fill = "white",
                                   color = "#EAEAEA"),
    panel.background = element_rect(fill = "white", colour = NA),
    plot.title = element_textbox(
      size = 11,
      lineheight = 1,
      face = "bold",
      width = unit(0.9, "npc"),
    ),
    plot.margin = margin(0.3, 0.5, 0.3, 0.5, "cm"),
    plot.caption.position = "plot",
    plot.caption = element_textbox(
      size = 7,
      lineheight = 1.2,
      hjust = 0.3,
      vjust = 0.5,
      width = unit(1.02, "npc"),
      margin = margin(12, 0, 0, 2)
    ),
    axis.text = element_text(size = 7, color = "black")
  )

В некоторых местах метки значений баров и линий перекрываются. Есть ли способ избежать этого автоматически?

Пакет ggrepel — мой выбор для автоматического разделения перекрывающихся меток.

Jon Spring 04.04.2023 17:58

Один обходной путь, который я могу придумать, - это добавить данные линии к данным столбца, а затем изобразить их как невидимые столбцы или такой же розовый цвет, как на вашем графике. Таким образом, вам понадобится только одна линия geom_text() и вы сможете контролировать перекрытие. Также взгляните на geom_bartext() в erocoar.github.io/ggpol и geom_bar_text() в wilkox.org/ggfittext

Tung 04.04.2023 17:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

# after loading library(dplyr) as well
...


 ggrepel::geom_text_repel(
    aes(col1, Values_pos, label = label, fontface = fontface),
    direction = "y",
    box.padding = 0.1, label.padding = 0.1, 
    size = 2.5,
    data = bind_rows(
      df_bars %>% 
        arrange(Status) %>%
        group_by(col1) %>%
        arrange(desc(Status)) %>%
        mutate(Values_cuml = cumsum(Values),
               Values_pos = Values_cuml - Values/2,
               fontface = "plain",
               label = ifelse(Values >= 0.01, paste0 (sprintf(
                 "%1.1f%%", 100 * Values
               )), "")) %>%
        ungroup(),
      
      df_lines %>%
        group_by(col1) %>%
        mutate(Values_cuml = cumsum(aaa.l.rev),
               Values_pos = Values_cuml - 0.02*aaa.l.rev,
               fontface = "bold",
               label = ifelse(aaa.l.rev >= 0.1, paste0 (sprintf(
                 "%1.1f%%", -100 * aaa.l.rev + 100
               )), "")) %>%
        ungroup(),
      
      df_lines %>%
        group_by(col1) %>%
        mutate(Values_cuml = cumsum(eee.l),
               Values_pos = Values_cuml - 0.1*eee.l,
               fontface = "bold",
               label = paste0 (sprintf("%1.1f%%", 100 * eee.l))) %>%
        ungroup()
    )
  ) +
Ответ принят как подходящий

Вот два подхода.

Во-первых, быстрым и грязным обходным путем было бы выравнивание по левому краю одного набора меток и выравнивание по правому краю другого. Здесь я добавил hjust = 1.1 к geom_text() вызовам для столбцов и fontface = "bold", hjust = -0.1 к 2 вызовам geom_text() для линий (украл идею @JonSpring о выделении жирным шрифтом меток линий). Весь остальной код остается без изменений.

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

library(ggplot2)
library(ggtext)
library(ggiraph)

p <- ggplot(df_bars, aes(x = col1, y = Values)) +
  geom_bar_interactive(
    mapping = aes(
      fill = Status, 
      tooltip = ifelse(Values >= 0.01, paste0(sprintf("%1.1f%%", 100 * Values)), ""),
      data_id = paste(col1, Status)
    ),
    position = "stack",
    stat = "identity"
  ) +
  # remove following geom_text() call
  geom_line(
    # rest of plot code as in OP
    # ...

girafe(
  ggobj = p,
  options = list(opts_hover("filter:brightness(120%);"))
)

Скриншот при наведении курсора на зеленую часть панели "FFF":

Хорошо, выравнивание кажется мне очень интуитивным способом различить их.

Jon Spring 04.04.2023 20:51

Другие вопросы по теме