ggplot2 пропущенные метки после пользовательского масштабирования оси - PullRequest
7 голосов
/ 14 мая 2019

Я пытаюсь применить пользовательское масштабирование оси X с использованием ggplot2 и scales::trans_new(). Тем не менее, когда я делаю некоторые из меток оси пропадают. Может кто-нибудь помочь мне понять, почему?

Установка:

library(tidyverse)

# the data
ds <- tibble(
  myx = c(1, .5, .1, .01, .001, 0),
  myy = 1:6
)

# the custom transformation
forth_root_trans_rev <- scales::trans_new(
  name = "sign_fourth_root_rev",
  transform = function (x) { - abs(x)^(1/4) },
  inverse = function (x) { x^4 }
)

Участок 1:

Когда я пытаюсь построить это, метка для x = 0 теряется.

# plot - missing x-label at `0`
ggplot(ds, aes(x = myx, y = myy)) + 
  geom_line() + 
  geom_point() + 
  scale_x_continuous(
    trans = forth_root_trans_rev,
    breaks = sort(unique(ds$myx)),
  )

image0">

Участок 2

Когда я добавляю немного места с обеих сторон графика, теряются еще больше X-меток.

# plot - missing x-labels below 0.5
ggplot(ds, aes(x = myx, y = myy)) + 
  geom_line() + 
  geom_point() +
  scale_x_continuous(
    trans = forth_root_trans_rev,
    breaks = sort(unique(ds$myx)),
    expand = expand_scale(mult = c(.1, .6))
  )

missing x-labels below 0.5

Полагаю, это связано со старой проблемой: https://github.com/tidyverse/ggplot2/issues/980. Тем не менее, я не могу понять, как применить это преобразование и сохранить все x-метки.

Куда я иду не так?

1 Ответ

4 голосов
/ 15 мая 2019

Проблема здесь связана с сочетанием двух факторов:

  1. Ваши значения по оси x (после преобразования) попадают в диапазон [-1, 0], поэтому любое расширение (аддитивное или мультипликативное) подтолкнет окончательный диапазон к положительным и отрицательным значениям.

  2. Ваше пользовательское преобразование не является однозначным в [<some negative number>, <some positive number>] регионе.

Как это произошло

Где-то глубоко внутри всего кода, используемого для создания объекта ggplot (вы можете запустить ggplot2:::ggplot_build.ggplot перед печатью графика и перейти к layout$setup_panel_params(), но я не рекомендую это для случайных пользователей ... кроличья нора проходит очень глубоко там), разрывы по оси X рассчитываются следующим образом:

  1. Получить пределы для преобразованных значений (для c(1, .5, .1, .01, .001, 0) в вопросе это будет (-1, 0)).
  2. Добавьте расширение к пределам, если применимо (расширение по умолчанию для непрерывной оси составляет 5% с обеих сторон, поэтому ограничения становятся (-1.05, 0.05)).
  3. Применение обратного преобразования к пределам (взятие x^4 для пределов дает (1.215506, 0.000006)).
  4. Примените преобразование для обоих введенных пользователем перерывов и пределов (для перерывов c(1, .5, .1, .01, .001, 0) становится (-1.0000000, ..., 0.0000000), но для пределов (1.215506, 0.000006) теперь становится (-1.05, -0.05), что на уже , чем (-1.05, 0.05)).
  5. Разрывы за пределами диапазона ограничения сбрасываются (поскольку ограничения теперь останавливаются на -0,05, разрыв на 0 сбрасывается).

Как обойти это

Вы можете изменить свое преобразование с помощью sign(), чтобы сохранить положительные / отрицательные значения, так, чтобы преобразование было один к одному во всем диапазоне, как это было предложено Хэдли в дискуссии по проблеме GH, которую вы связали , Например:

# original
forth_root_trans_rev <- scales::trans_new(
  name = "sign_fourth_root_rev",
  transform = function (x) { - abs(x)^(1/4) },
  inverse = function (x) { x^4 }
)

# new
forth_root_trans_rev2 <- scales::trans_new(
  name = "sign_fourth_root_rev",
  transform = function (x) { -sign(x) * abs(x)^(1/4) },
  inverse = function (x) { -sign(x) * abs(x)^4 }
)

library(dplyr)
library(tidyr)

# comparison of two transformations
# y1 shows a one-to-one mapping in either (-Inf, 0] or [0, Inf) but not both;
# y2 shows a one-to-one mapping in (-Inf, Inf)
data.frame(x = seq(-1, 1, 0.01)) %>%
  mutate(y1 = x %>% forth_root_trans_rev$transform() %>% forth_root_trans_rev$inverse(),
         y2 = x %>% forth_root_trans_rev2$transform() %>% forth_root_trans_rev2$inverse()) %>%
  gather(trans, y, -x) %>%
  ggplot(aes(x, y, colour = trans)) +
  geom_line() +
  geom_vline(xintercept = 0, linetype = "dashed") +
  facet_wrap(~trans)

comparison plot

Использование

p <- ggplot(ds, aes(x = myx, y = myy)) + 
  geom_line() + 
  geom_point() + 
  theme(panel.grid.minor = element_blank())

p + 
  scale_x_continuous(
    trans = forth_root_trans_rev2,
    breaks = sort(unique(ds$myx))
  )
p + 
  scale_x_continuous(
    trans = forth_root_trans_rev2,
    breaks = sort(unique(ds$myx)),
    expand = expand_scale(mult = c(.1, .6)) # with different expansion factor, if desired
  )

plots

...