Tidyeval и оператор if в обертке ggplot

Я пытаюсь создать оболочку ggplot2, которая регулирует масштаб по оси y в зависимости от масштаба y.

  • plot_1: выдает ошибку
  • plot_2: с тайдэвалом, но на выходе нет значения по оси Y
  • plot_3: вот как это должно выглядеть

Любая идея, что вызывает эту проблему? Может есть другой подход к этой проблеме?

Я надеюсь, что пример поможет понять мою проблему. Заранее спасибо!

# setup -------------------------------------------------------------------

library(ggplot2)

df <- data.frame(
  childs = c(3, 0, 2, 4, 2, 2, 2, 3, 3, 4, 5, 4, 3, 5, 7, 2, 6, 5, 0, 2),
  agegrp = as.factor(c("Age 45-55",
                       "Age 55-65","Age 65+","Age 35-45","Age 45-55","Age 45-55",
                       "Age 45-55","Age 18-35","Age 35-45","Age 65+",
                       "Age 18-35","Age 65+","Age 18-35","Age 55-65",
                       "Age 65+","Age 18-35","Age 55-65","Age 55-65",
                       "Age 18-35","Age 35-45")),
  religion = as.factor(c("None","None",
                         "Catholic","Catholic","None","None","None","Catholic",
                         "Protestant","None","Protestant","Protestant",
                         "Catholic","Protestant","Catholic","Other",
                         "Protestant","Protestant","None","Protestant"))
)

# function ----------------------------------------------------------------

plot_1 <- function(data, x, y, fill) {
  
  plot <-
    ggplot(data = data,
           mapping = aes(x = {{ x }}, y = {{ y }}, fill = {{ fill }})) +
    geom_col(position = "dodge")

  if (is.numeric(y)) {
    scale <-
      scale_y_continuous(expand = expansion(mult = c(0, 0.05)),
                         breaks = scales::breaks_extended(),
                         labels = scales::label_number(big.mark = "'"))
  } else {
    scale <-
      scale_y_discrete(expand = expansion(mult = c(0, 0.05)),
                       breaks = scales::breaks_extended(),
                       labels = scales::label_number(big.mark = "'"))
  }

  plot +
    scale +
    cowplot::theme_minimal_hgrid()
}

plot_2 <- function(data, x, y, fill) {
  
  plot <-
    ggplot(data = data,
           mapping = aes(x = {{ x }}, y = {{ y }}, fill = {{ fill }})) +
    geom_col(position = "dodge")
  
  cond <- enquo(y) # tidyeval
  
  if (is.numeric(cond)) {
    scale <-
      scale_y_continuous(expand = expansion(mult = c(0, 0.05)),
                         breaks = scales::breaks_extended(),
                         labels = scales::label_number(big.mark = "'"))
  } else {
    scale <-
      scale_y_discrete(expand = expansion(mult = c(0, 0.05)),
                       breaks = scales::breaks_extended(),
                       labels = scales::label_number(big.mark = "'"))
  }
  
  plot +
    scale +
    cowplot::theme_minimal_hgrid()
}

plot_3 <- function(data, x, y, fill) {
  
  plot <-
    ggplot(data = data,
           mapping = aes(x = {{ x }}, y = {{ y }}, fill = {{ fill }})) +
    geom_col(position = "dodge")

  plot +
    cowplot::theme_minimal_hgrid()
}

# test --------------------------------------------------------------------

plot_1(df, agegrp, childs, religion)
#> Error in plot_1(df, agegrp, childs, religion): object 'childs' not found
plot_2(df, agegrp, childs, religion)
plot_3(df, agegrp, childs, religion)

Created on 2022-10-14 by the reprex package (v2.0.1)

Попробуйте передать y в dplyr::mutate() с новым уникальным именем столбца, например. mutate(.my_y = {{ y }}). Затем вы можете проверить data$.my_y и передать его в ggplot2.

Lionel Henry 14.10.2022 15:45

@LionelHenry ошибка возникает в строке if (is.numeric(y)), где y явно не распознается без применения маски данных. Вместо этого я предложил if (is.numeric(data[[deparse(substitute(y))]])), но я думаю, что if (is.numeric(eval_tidy(enquo(y), data = data))) больше похож на аккуратную вселенную. Я также думаю, что вы знаете более аккуратный способ выразить это?

Allan Cameron 14.10.2022 16:01

@LionelHenry спасибо за вашу идею. к сожалению, это выдает ту же ошибку. Решение Allans работает.

tricktracktriu 14.10.2022 16:40

Существует также более интуитивно понятный способ для пользователей tidyverse: cond <- pull(data, {{ y }})if (is.numeric(cond))дополнительная информация

tricktracktriu 15.10.2022 08:26

Рад, что вы нашли решение своей проблемы, извините, у меня не было времени, чтобы проиллюстрировать мою.

Lionel Henry 17.10.2022 12:40
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ошибка возникает внутри if (is.numeric(y)), так как переменная y не существует в этом контексте, где маска данных не применяется. Если вы измените его на

  if (is.numeric(data[[deparse(substitute(y))]])) {

Тогда ваша функция работает, в результате чего

plot_1(df, agegrp, childs, religion)

Я думал if (is.numeric(y)) это проблема. Но я не знал, как это решить. if (is.numeric(eval_tidy(substitute(y), data = data))) У меня работает нормально. Также спасибо за объяснение.

tricktracktriu 14.10.2022 16:33

Я бы использовал rlang::as_name() вместо deparse(), потому что последний может возвращать многострочный вектор с некоторыми выражениями. Первый безопаснее и не работает, если предоставленное выражение не является символом.

Lionel Henry 17.10.2022 12:39

@LionelHenry спасибо - я думал, ты знаешь более аккуратный способ.

Allan Cameron 17.10.2022 17:18

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