aes_string在使用 ggplot2 編程時,我使用了一些方便的行為。但aes_string已被棄用(我相信自 ggplot2 版本 3.4.0 以來明顯)。我正在努力解決如何很好地替換它。
具體來說,我之前創建了通過省略號接受任意字串引數的函式,并通過 do.call 將它們傳遞給 aes_string,如下面的第一個 reprex 所示。
自從注意到我試圖避免的棄用警告aes_string后,發現自己實際上只是以一種相當“hacky”的方式模仿它。據推測,aes_string導致它被棄用的任何缺陷,也適用于我的 hacky 解決方法。見第二個代表。
有沒有更優雅的解決方案?我想繼續將變數名作為字串傳遞。
用 aes_string 代表我的舊方法
library(ggplot2)
plotterOld <- function(...) {
args <- list(...)
pointAes <- do.call(aes_string, args = args)
ggplot(mpg, aes(displ, cty))
geom_point(mapping = pointAes)
}
plotterOld(colour = "cyl", size = "year")
#> Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
#> ? Please use tidy evaluation ideoms with `aes()`

# it can accept NULLs, and e.g. intuitively doesn't map size to anything
plotterOld(colour = "cyl", size = NULL)

# no arguments also works fine
plotterOld()

使用 reprex v2.0.2 創建于 2022-11-11
代表我替換 aes_string 行為的駭人聽聞的嘗試?
library(ggplot2)
# arbitrary aesthetics passed as strings using ellipses, aes, quo and .data
myAesString <- function(...) {
dots <- list(...)
# early exits
stopifnot(rlang::is_named2(dots))
if (length(dots) == 0) {
return(NULL)
}
# initialise empty mapping object and fill it with quosures where appropriate
mapping <- aes()
for (n in names(dots)) {
v <- dots[[n]]
if (!is.null(v)) {
if (!rlang::is_string(v)) stop(n, " must be a string or NULL")
mapping[[n]] <- quo(.data[[v]])
}
}
return(mapping)
}
plotterNew <- function(...) {
pointAes <- myAesString(...)
ggplot(mpg, aes(displ, cty))
geom_point(mapping = pointAes)
}
plotterNew(colour = "cyl", size = "year")

plotterNew(colour = "cyl", size = NULL, shape = "drv")

plotterNew()

# seems to work fine
p <- plotterNew(colour = "cyl", size = "year")
p$layers[[1]]$mapping
#> Aesthetic mapping:
#> * `colour` -> `.data[["cyl"]]`
#> * `size` -> `.data[["year"]]`
使用reprex v2.0.2創建于 2022-11-11
會話資訊sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.2.1 (2022-06-23)
#> os macOS Big Sur ... 10.16
#> system x86_64, darwin17.0
#> ui X11
#> language (EN)
#> collate en_GB.UTF-8
#> ctype en_GB.UTF-8
#> tz Europe/Amsterdam
#> date 2022-11-11
#> pandoc 2.18 @ /Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> assertthat 0.2.1 2019-03-21 [1] CRAN (R 4.2.0)
#> cli 3.4.1 2022-09-23 [1] CRAN (R 4.2.0)
#> colorspace 2.0-3 2022-02-21 [1] CRAN (R 4.2.0)
#> curl 4.3.3 2022-10-06 [1] CRAN (R 4.2.0)
#> DBI 1.1.3 2022-06-18 [1] CRAN (R 4.2.0)
#> digest 0.6.30 2022-10-18 [1] CRAN (R 4.2.1)
#> dplyr 1.0.10 2022-09-01 [1] CRAN (R 4.2.0)
#> evaluate 0.18 2022-11-07 [1] CRAN (R 4.2.0)
#> fansi 1.0.3 2022-03-24 [1] CRAN (R 4.2.0)
#> farver 2.1.1 2022-07-06 [1] CRAN (R 4.2.0)
#> fastmap 1.1.0 2021-01-25 [1] RSPM (R 4.2.0)
#> fs 1.5.2 2021-12-08 [1] RSPM (R 4.2.0)
#> generics 0.1.3 2022-07-05 [1] CRAN (R 4.2.0)
#> ggplot2 * 3.4.0 2022-11-04 [1] CRAN (R 4.2.1)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.2.0)
#> gtable 0.3.1 2022-09-01 [1] CRAN (R 4.2.0)
#> highr 0.9 2021-04-16 [1] RSPM (R 4.2.0)
#> htmltools 0.5.3 2022-07-18 [1] CRAN (R 4.2.0)
#> httr 1.4.4 2022-08-17 [1] CRAN (R 4.2.0)
#> knitr 1.40 2022-08-24 [1] CRAN (R 4.2.0)
#> labeling 0.4.2 2020-10-20 [1] CRAN (R 4.2.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.2.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.2.0)
#> mime 0.12 2021-09-28 [1] RSPM (R 4.2.0)
#> munsell 0.5.0 2018-06-12 [1] CRAN (R 4.2.0)
#> pillar 1.8.1 2022-08-19 [1] CRAN (R 4.2.0)
#> pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.2.0)
#> purrr 0.3.5 2022-10-06 [1] CRAN (R 4.2.0)
#> R.cache 0.16.0 2022-07-21 [1] CRAN (R 4.2.0)
#> R.methodsS3 1.8.2 2022-06-13 [1] CRAN (R 4.2.0)
#> R.oo 1.25.0 2022-06-12 [1] CRAN (R 4.2.0)
#> R.utils 2.12.1 2022-10-30 [1] CRAN (R 4.2.0)
#> R6 2.5.1 2021-08-19 [1] CRAN (R 4.2.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.2.0)
#> rlang 1.0.6 2022-09-24 [1] CRAN (R 4.2.0)
#> rmarkdown 2.18 2022-11-09 [1] CRAN (R 4.2.1)
#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.2.0)
#> scales 1.2.1 2022-08-20 [1] CRAN (R 4.2.0)
#> sessioninfo 1.2.2 2021-12-06 [1] RSPM (R 4.2.0)
#> stringi 1.7.8 2022-07-11 [1] CRAN (R 4.2.0)
#> stringr 1.4.1 2022-08-20 [1] CRAN (R 4.2.0)
#> styler 1.8.1 2022-11-07 [1] CRAN (R 4.2.0)
#> tibble 3.1.8 2022-07-22 [1] CRAN (R 4.2.0)
#> tidyselect 1.2.0 2022-10-10 [1] CRAN (R 4.2.0)
#> utf8 1.2.2 2021-07-24 [1] CRAN (R 4.2.0)
#> vctrs 0.5.0 2022-10-22 [1] CRAN (R 4.2.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.2.0)
#> xfun 0.34 2022-10-18 [1] CRAN (R 4.2.0)
#> xml2 1.3.3 2021-11-30 [1] RSPM (R 4.2.0)
#> yaml 2.3.6 2022-10-18 [1] CRAN (R 4.2.1)
#>
#> [1] /Library/Frameworks/R.framework/Versions/4.2/Resources/library
#>
#> ──────────────────────────────────────────────────────────────────────────────
```
uj5u.com熱心網友回復:
一種選擇是使用以下方法將帶引號的字串串列轉換為符號sym:
library(ggplot2)
plotterOld <- function(...) {
args <- lapply(list(...), function(x) if (!is.null(x)) sym(x))
pointAes <- do.call(aes, args = args)
ggplot(mpg, aes(displ, cty))
geom_point(mapping = pointAes)
}
更新!!!我們可以通過使用來進一步簡化do.call:
plotterOld <- function(...) {
args <- lapply(list(...), function(x) if (!is.null(x)) sym(x))
ggplot(mpg, aes(displ, cty))
geom_point(mapping = aes(!!!args))
}
plotterOld(colour = "cyl", size = "year")

plotterOld(colour = "cyl", size = NULL)

plotterOld()

uj5u.com熱心網友回復:
您可以使用ensyms將命名字串引數轉換為命名符號引數,因此與舊繪圖函式等效的可能是
library(ggplot2)
plotterNew <- function(...) {
ggplot(mpg, aes(displ, cty))
geom_point(mapping = aes(!!!ensyms(...)))
}
plotterNew(colour = "cyl", size = "year")

使用reprex v2.0.2創建于 2022-11-12
uj5u.com熱心網友回復:
Stefan 建議sym()和 Allan Cameron 建議的答案ensyms()都確實幫助我指出了理解這個問題的正確方向,所以所有的功勞都歸功于他們。
這個答案比較了這兩種方法并添加了第三種方法data_sym()
aes_string 的兩種行為,如下所示,我能夠使用 sym() 進行復制,但不能使用 ensyms()
- aes_string 可以接受 NULL 作為引數的值,
- aes_string 可以使用已經存盤在物件中的字串
library(rlang)
library(ggplot2)
plotterAesString <- function(...) {
pointAes <- do.call(aes_string, args = list(...))
ggplot(mpg, aes(displ, cty))
geom_point(mapping = pointAes)
}
colourObject <- "cyl"
plotterAesString(colour = colourObject, size = NULL, shape = "drv")
#> Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
#> ? Please use tidy evaluation ideoms with `aes()`

Stefan 對 sym() 的建議使我采用了這種方法,這似乎是使用 aes_string 的一個很好的替代品
plotterSym <- function(...) {
args <- list(...)
args <- lapply(args, function(x) if (is_string(x)) sym(x) else x)
ggplot(mpg, aes(displ, cty)) geom_point(mapping = aes(!!!args))
}
plotterSym(colour = colourObject, size = NULL, shape = "drv")

我發布這個答案是因為 rlang 的 data_sym() 函式可以防止意外匹配環境變數而不是資料變數的問題。(原來的 aes_string 方法也有這種行為!)
driv <- "misspelled variable?"
plotterSym(shape = "driv")

plotterDataSym <- function(...) {
args <- list(...)
args <- lapply(args, function(x) if (is_string(x)) data_sym(x) else x)
ggplot(mpg, aes(displ, cty)) geom_point(mapping = aes(!!!args))
}
它就像 sym 方法一樣作業
plotterDataSym(colour = colourObject, size = NULL, shape = "drv")

但是拼寫錯誤的名稱會出錯,這對我來說是可取的行為。
plotterDataSym(shape = "driv")
#> Error in `geom_point()`:
#> ! Problem while computing aesthetics.
#> ? Error occurred in the 1st layer.
#> Caused by error in `.data$driv`:
#> ! Column `driv` not found in `.data`.
#> Backtrace:
#> ▆
#> 1. ├─base::tryCatch(...)
#> 2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#> 3. │ ├─base (local) tryCatchOne(...)
#> 4. │ │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 5. │ └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#> 6. │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#> 7. │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 8. ├─base::withCallingHandlers(...)
#> 9. ├─base::saveRDS(...)
#> 10. ├─base::do.call(...)
#> 11. ├─base (local) `<fn>`(...)
#> 12. ├─global `<fn>`(input = base::quote("pious-rhino_reprex.R"))
#> 13. │ └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#> 14. │ └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#> 15. │ └─knitr:::process_file(text, output)
#> 16. │ ├─base::withCallingHandlers(...)
#> 17. │ ├─knitr:::process_group(group)
#> 18. │ └─knitr:::process_group.block(group)
#> 19. │ └─knitr:::call_block(x)
#> 20. │ └─knitr:::block_exec(params)
#> 21. │ └─knitr:::eng_r(options)
#> 22. │ ├─knitr:::in_input_dir(...)
#> 23. │ │ └─knitr:::in_dir(input_dir(), expr)
#> 24. │ └─knitr (local) evaluate(...)
#> 25. │ └─evaluate::evaluate(...)
#> 26. │ └─evaluate:::evaluate_call(...)
#> 27. │ ├─evaluate (local) handle(...)
#> 28. │ │ └─base::try(f, silent = TRUE)
#> 29. │ │ └─base::tryCatch(...)
#> 30. │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#> 31. │ │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#> 32. │ │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 33. │ ├─base::withCallingHandlers(...)
#> 34. │ ├─base::withVisible(value_fun(ev$value, ev$visible))
#> 35. │ └─knitr (local) value_fun(ev$value, ev$visible)
#> 36. │ └─knitr (local) fun(x, options = options)
#> 37. │ ├─base::withVisible(knit_print(x, ...))
#> 38. │ ├─knitr::knit_print(x, ...)
#> 39. │ └─knitr:::knit_print.default(x, ...)
#> 40. │ └─evaluate (local) normal_print(x)
#> 41. │ ├─base::print(x)
#> 42. │ └─ggplot2:::print.ggplot(x)
#> 43. │ ├─ggplot2::ggplot_build(x)
#> 44. │ └─ggplot2:::ggplot_build.ggplot(x)
#> 45. │ └─ggplot2:::by_layer(...)
#> 46. │ ├─rlang::try_fetch(...)
#> 47. │ │ ├─base::tryCatch(...)
#> 48. │ │ │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#> 49. │ │ │ └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#> 50. │ │ │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#> 51. │ │ └─base::withCallingHandlers(...)
#> 52. │ └─ggplot2 (local) f(l = layers[[i]], d = data[[i]])
#> 53. │ └─l$compute_aesthetics(d, plot)
#> 54. │ └─ggplot2 (local) compute_aesthetics(..., self = self)
#> 55. │ └─ggplot2:::scales_add_defaults(...)
#> 56. │ └─base::lapply(aesthetics[new_aesthetics], eval_tidy, data = data)
#> 57. │ └─rlang (local) FUN(X[[i]], ...)
#> 58. ├─driv
#> 59. ├─rlang:::`$.rlang_data_pronoun`(.data, driv)
#> 60. │ └─rlang:::data_pronoun_get(...)
#> 61. └─rlang:::abort_data_pronoun(x, call = y)
#> 62. └─rlang::abort(msg, "rlang_error_data_pronoun_not_found", call = call)
關于我對 Allan Cameron 建議的 ensyms 方法的測驗的注釋我發現這種方法不能接受包含字串或 NULL 引數的物件,我找不到解決這些問題的方法 ensyms
plotterEnsyms <- function(...) {
ggplot(mpg, aes(displ, cty)) geom_point(mapping = aes(!!!ensyms(...)))
}
# 1. uses name of object as symbol, instead of converting the string itself
plotterEnsyms(colour = colourObject)

# 2. can't accept NULLs, (and ensyms directly uses the args, so NULLs can't be removed first, I think...)
plotterEnsyms(size = NULL)
#> Error in `sym()`:
#> ! Can't convert NULL to a symbol.
#> Backtrace:
#> ▆
#> 1. └─global plotterEnsyms(size = NULL)
#> 2. ├─ggplot2::geom_point(mapping = aes(!!!ensyms(...)))
#> 3. │ └─ggplot2::layer(...)
#> 4. ├─ggplot2::aes(!!!ensyms(...))
#> 5. │ └─ggplot2:::arg_enquos("x")
#> 6. │ └─rlang::eval_bare(expr[[2]][[2]][[2]], env)
#> 7. └─rlang::ensyms(...)
#> 8. └─rlang:::map(...)
#> 9. └─base::lapply(.x, .f, ...)
#> 10. └─rlang (local) FUN(X[[i]], ...)
#> 11. └─rlang::sym(expr)
#> 12. └─rlang:::abort_coercion(x, "a symbol")
#> 13. └─rlang::abort(msg, call = call)
使用reprex v2.0.2創建于 2022-11-13
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/533299.html
