Я пытаюсь обновить какой-то старый код построения сюжета, который выдает сообщение:
Warning message: `aes_string()` was deprecated in ggplot2 3.0.0. Please use tidy evaluation ideoms with `aes()`
Когда я заменяю aes_string() на aes() и без кавычек !!, я начинаю получать сообщение «Ошибка: дискретное значение предоставлено для непрерывной шкалы» в неожиданных местах.
Вот упрощенный воспроизводимый пример:
# Make some test data
set.seed(1)
dat <- data.frame(x=rnorm(100),y=rnorm(100),value=rnorm(100))
xvar <- 'x'
yvar <- 'y'
cvar <- 'value'
# This works, but gives a deprecated warning for use of aes_string()
ggplot(dat,aes_string(x=xvar,y=yvar,color=cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
# This changes the plot to use aes() with !! instead of using aes_string()
# It fails with "Error: Discrete value supplied to continuous scale"
ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
Я не смог понять, что вызывает это, но похоже, что сами сопоставления обрабатываются по-разному в ggplot - с aes_string() имена переменных отображаются с ~ в начале и с aes( ) и !! они не:
# Capture each plot so it can be examined
plt_working <- ggplot(dat,aes_string(x=xvar,y=yvar,color=cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
plt_broken <- ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
summary(plt_working) # The second line says "mapping: colour = ~value, x = ~x, y = ~y"
summary(plt_broken) # The second line says "mapping: x = x, y = y, colour = value" (no ~s)
Что означает ~? В этом контексте кажется, что это не имеет ничего общего с формулами или гранями.
Почему это изменение приводит к тому, что код сюжета перестает работать, и как правильно обновить код, чтобы он продолжал работать?
Текущий (по состоянию на 2023 год) предпочтительный метод использования строк внутри aes()
— через объект ggplot .data
(спасибо Лайонелу Генри за разъяснение причин этого):
xvar <- 'x'
yvar <- 'y'
cvar <- 'value'
ggplot(dat,aes(x = .data[[xvar]], y = .data[[yvar]], color = .data[[cvar]])) + geom_point() + scale_color_gradientn(colors = rainbow(10))
Кроме того, вы можете обернуть свои строки в sym()
, а затем использовать !!
, чтобы развернуть их внутри aes()
:
set.seed(1)
dat <- data.frame(x=rnorm(100),y=rnorm(100),value=rnorm(100))
xvar <- sym('x')
yvar <- sym('y')
cvar <- sym('value')
ggplot(dat,aes(x=!!xvar,y=!!yvar,color=!!cvar)) + geom_point() + scale_color_gradientn(colors = rainbow(10))
Спасибо! Это работает, но могу ли я спросить, почему и почему это необходимо? Я не являюсь большим пользователем tidyverse, но когда я использовал другие функции tidyverse, такие как collect() и pivot_longer(), они работали с ними !! без необходимости sym(). Как узнать, когда использовать sym(), а когда нет?
Я согласен, что это кажется несовместимым с другими функциями tidyverse, но как это может работать по-другому? По умолчанию aes()
пытается оценить эстетические сопоставления (например, color = value
) как строки без кавычек. Таким образом, чтобы заменить здесь строки, необходимо ввести слой квазицитирования. Вы также можете заменить строки немного меньшим набором текста с помощью объекта .data
, который я добавил в ответ.
Спасибо! Боюсь, я все еще не понимаю - откуда мне знать, когда я должен использовать !! и когда я должен использовать !!sym() ? Я просто запоминаю !!sym() как особую вещь, которую нужно делать в aes(), или есть другие ситуации, когда мне может понадобиться сделать это таким образом?
@jdobres прав в отношении неоднозначности строк в этом случае. В этом разница между маскированием данных и аккуратным выбором, только последний может интерпретировать строки. Это различие должно быть указано в документации к функции и является фундаментальным свойством, влияющим на использование аргумента: при выборе вы используете c()
для нескольких столбцов, можете сопоставляться с starts_with()
и т. д. Кстати, теперь мы избегаем !!
, где можем. Мы рекомендуем либо all_of()
в функциях выбора, таких как pivot_longer()
, либо .data[[my_string]]
в функциях маскирования данных.
@LionelHenry спасибо!! Термин «маскирование данных» привел меня к следующему: dplyr.tidyverse.org/reference/dplyr_data_masking.html, который я опубликую позже для всех, кто погуглит. Я все еще не совсем уверен, почему !!sym(xvar) работал, но я думаю, что по крайней мере понимаю, почему .data[[xvar]] является предпочтительным способом сделать это.
да, понимание метапрограммирования и внедрение символов/вызовов/квазур оказалось слишком сложным для большинства пользователей, поэтому сейчас мы сосредоточились на более простых шаблонах в нашей документации. Если вы хотите читать дальше, на веб-сайте rlang также есть документация: rlang.r-lib.org/reference/topic-data-mask-programming.html
Дополнительная информация: версии программного обеспечения не имеют значения — это происходит в системе Linux с R 4.2.2/ggplot2 3.4.0 и в системе MacOS с R 4.2.0/ggplot2 3.3.6.