ggplot: ограничить пределы оси / разрывы отдельного фасета - PullRequest
2 голосов
/ 08 марта 2020

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

Что меня раздражает при таком подходе, так это метка оставшейся оси под аннотацией. Смотрите изображение ниже (метки осей 15, 100 и 2.5). Я хотел бы ограничить метки оси x максимальным значением в каждом фасете (что-то близкое к нему), а не расширять до аннотаций.

Мне было интересно, существует ли лучший подход.

(я знаю, что мог бы создать желаемый график с помощью group_split и, например, лоскутного одеяла. Меня интересует, существует ли прямой способ ограничения пределов осей / меток для каждого отдельного фасета).

Большое спасибо.

library(tidyverse)
#> Warning: package 'dplyr' was built under R version 3.6.2
#> Warning: package 'forcats' was built under R version 3.6.3

mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise(n_obs=n()) %>% 
  mutate(n_obs=case_when(gear==4 ~ n_obs*100,
                         TRUE ~ as.numeric(n_obs))) %>% 
  group_by(gear) %>% 
  mutate(n_obs_max=max(n_obs, na.rm=T)) %>% 
  ggplot()+
  geom_bar(aes(y=cyl,
               x=n_obs),
           stat="identity")+
  geom_text(aes(y=cyl,
                x=n_obs_max*1.20,
                label=n_obs))+
  facet_wrap(vars(gear),
             scales="free_x")

Создано в 2020-03-08 пакетом prex (v0.3.0)

ОБНОВЛЕНИЕ

После полезного ответа @ stafan, приведенного ниже, здесь дано изменение и частичный ответ на мой вопрос.

Функция передана аргументу breaks

  my_breaks <- function(x) {

    #calculates the max value on the x axis for each facet
    new_x=max(x) 

    #adjusts this max value for a) the extension of the x axis by the 
    #expand=expansion(mult=c(0, 0.3)) which was needed to have enough space 
    #for the annotation; and the factor added to the position of the 
    #annotations with   x=max_n_obs*1.10; the result is the maximum value 
    #of the bars in each facet;
    old_max <- new_x/1.3/1.1 

    #create 5 labels; the maximum is the highest bar in each facet
    my_pretty=labeling::extended(0, old_max, m=5) 

    #round these values 
    my_pretty=signif(my_pretty, digits=-2) 

    #remove the highest label(s)
    my_pretty=head(unique(my_pretty), -1) 

    #combine the remaining labels and the maximum value of the highest bar
    my_pretty=c(my_pretty, old_max) 
    my_pretty
}

Применительно к моему (модифицированному) образцу это дает то, что я искал (см. График ниже).

library(tidyverse)
#> Warning: package 'dplyr' was built under R version 3.6.2
#> Warning: package 'forcats' was built under R version 3.6.3

my_breaks <- function(x) {
  new_x=max(x)
  old_max <- new_x/1.2/1.05
  #old_max
  my_pretty=labeling::extended(0, old_max, m=5)
  my_pretty=signif(my_pretty, digits=-2)
  my_pretty=head(unique(my_pretty), -1)
  my_pretty=c(my_pretty, old_max)
  my_pretty

}  

mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise(n_obs=n()) %>% 
  mutate(n_obs=case_when(gear==4 ~ n_obs*100,
                         TRUE ~ as.numeric(n_obs))) %>% 
  group_by(gear) %>% 
  mutate(n_obs_max=max(n_obs, na.rm=T)) %>% 
  ggplot()+
  geom_bar(aes(y=cyl,
               x=n_obs),
           stat="identity")+
  geom_text(aes(y=cyl,
                x=n_obs_max*1.20,
                label=n_obs))+
  scale_x_continuous(breaks=my_breaks1,
                     expand=expansion(mult=c(0, 0.05)))+
  facet_wrap(vars(gear),
             scales="free_x")

Недостаток этой функции является то, что значения для расширения шкалы (1,3) и т Фактор для размещения меток (1.1) «жестко запрограммирован» в функцию. Было бы удобно указать эти значения при передаче функции в команде ggplot scale, например что-то вроде

scale_x_continuous(breaks=my_breaks(expansion=1.3, pos.factor=1.1))

К сожалению, я не выяснил, как это работает.

Создано в 2020-03-09 пакетом Представ (v0.3.0)

1 Ответ

1 голос
/ 08 марта 2020

Попробуйте это.

  1. Я расширил ось Y.
  2. Я поправил перерывы. Я заимствовал общую идею у здесь . Функция my_breaks возвращает pretty_breaks, но удаляет последнее значение.

(Примечание: я также переключил эстетику, y = nobs и x = cyl и использовалordin_flip, потому что выполнение вашего кода на моей машине не воспроизвел ваш сюжет (ggplot 3.3.0)):

library(tidyverse)
#> Warning: package 'forcats' was built under R version 3.6.3

my_breaks <- function(x, n = 5, drop = 2) {
  breaks <- seq(x[[1]], x[[2]], length.out = n)
  breaks <- scales::pretty_breaks()(breaks)
  breaks <- breaks[1:(length(breaks) - drop)]
  breaks
}

mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise(n_obs = n()) %>% 
  mutate(n_obs = case_when(
    gear == 4 ~ n_obs * 100,
    TRUE ~ as.numeric(n_obs))) %>% 
  group_by(gear) %>% 
  mutate(n_obs_max = max(n_obs, na.rm=T)) %>% 
  ggplot(aes(x = cyl))+
  geom_bar(aes(y = n_obs), stat="identity")+
  geom_text(aes(y = n_obs_max * 1.2, label = n_obs))+
  facet_wrap(vars(gear), scales = "free_x") + 
  scale_y_continuous(breaks = function(x) my_breaks(x, 5, 2),
                     expand = expand_scale(mult = c(0.05, .2))) +
  coord_flip()
#> Warning: `expand_scale()` is deprecated; use `expansion()` instead.

Создано в 2020-03-09 пакетом Представить ( v0.3.0)

...