【问题标题】:ggplot with aesthetics generated from input data具有从输入数据生成的美学效果的 ggplot
【发布时间】:2019-06-15 01:44:03
【问题描述】:

由于我需要在 R 中制作许多不同的图,因此我试图在准备数据时添加更多逻辑(添加与美学相对应的列名),而在图本身中添加更少的逻辑。

考虑以下默认虹膜图:

library(ggplot2)
library(data.table)
scatter <- ggplot(data=iris, aes(x = Sepal.Length, y = Sepal.Width)) 
scatter + geom_point(aes(color=Species, shape=Species))

现在我使用与所需美学匹配的列名制作修改后的 iris 数据:

iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species,
                  shape=Species)]

我想以这样一种方式在函数中绘制,它基本上构建以下命令只是稍微动态一些,所以你使用数据中提供的所有美学。

ggplot(data, aes(x=x, y=y)) + geom_point(aes(color=color, shape=shape))

我已经很久没有阅读任何关于非标准评估、表达式和引用的内容了,我注意到rlang 和 quosures (cheatsheet) 有相当多的发展。 [这个]问题有点帮助,但它并没有解决我想从数据中推断出美学的事实。

最后我尝试了很多东西,并查看了aes。在那里我看到了:

exprs <- rlang::enquos(x = x, y = y, ...)

我认为这就是我所做的所有尝试都喜欢的原因:

ggplot(iris2, aes(x=x, y=y)) +
    geom_point(aes(rlang::quo(expr(color=color))))

没有成功,因为 aes 正试图 'enquos' 我的 quosure(s)。

QUESTION有没有什么方法可以根据数据的内容以动态的方式向 aes 提供参数(所以你事先不知道你需要哪种美学?

如果我的问题不够清楚,最后我做了一些有用的东西,只是我觉得这完全没有必要,因为我不知道/理解正确的方法。所以下面的东西是有效的,是我的想法,但我就是这样。不喜欢的是我不得不修改aes:

下面的块是独立的,可以在没有上面的代码块的情况下执行。

library(data.table)
library(ggplot2)
library(rlang)
iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species, shape=Species)]
myaes <- function (x, y, myquo=NULL, ...) {    
    exprs <- rlang::enquos(x = x, y = y, ...)    
    exprs <- c(exprs, myquo)
    is_missing <- vapply(exprs, rlang::quo_is_missing, logical(1))
    aes <- ggplot2:::new_aes(exprs[!is_missing], env = parent.frame())
    ggplot2:::rename_aes(aes)
}

generalPlot <- function(data, f=geom_point,
                        knownaes=c('color'=expr(color), 'shape'=expr(shape))){
    myquo  <- list()
    for(i in names(knownaes)){
        if(i %in% names(data)){
            l <- list(rlang::quo(!!knownaes[[i]]))
            names(l) <- i
            myquo <- c(myquo, l)
        }
    }    

    ggplot(data, aes(x=x, y=y)) +
        f(myaes(myquo=myquo))   
}

generalPlot(iris2[,.(x, y, color)])
generalPlot(iris2[,.(x, y, color, shape)])

【问题讨论】:

    标签: r ggplot2 rlang quosure


    【解决方案1】:

    因此,如果您的数据是“颜色”或“形状”列,您只想将其映射到颜色或形状美学?我认为更简单的方法是

    generalPlot <- function(data, f=geom_point, knownaes=c('color', 'shape')) {
      match_aes <- intersect(names(data), knownaes)
      my_aes_list <- purrr::set_names(purrr::map(match_aes, rlang::sym), match_aes)
      my_aes <- rlang::eval_tidy(quo(aes(!!!my_aes_list)))
      ggplot(data, aes(x=x, y=y)) +
            f(mapping=my_aes)
    
    }
    

    那你就可以了

    generalPlot(iris2[,.(x, y)])
    generalPlot(iris2[,.(x, y, color)])
    generalPlot(iris2[,.(x, y, color, shape)])
    

    它不需要额外的myaes 函数。

    我有点惊讶我不得不使用eval_tidy,但由于某种原因,您似乎不能将!!!aes() 一起使用。

    x <- list(color=sym("color"))
    ggplot(iris2, aes(x,y)) + geom_point(aes(!!!x))
    # Error: Can't use `!!!` at top level
    

    (用ggplot2_3.1.0测试)

    【讨论】:

    • 好的,这太棒了!首先它可以工作,但是这个解决方案指出了我最初错过的一些事情(1)我错过了如何使用 rlang::sym(但也使用 as.symbol)创建符号,(2)使用 @987654321 的 eval_tidy @
    【解决方案2】:

    您可以使用此自定义函数来解析输入数据列名并生成 aes 文本字符串,该字符串传递给 eval()

    generateAES <- function(foo) {
        eval(parse(text = paste0("aes(", 
            paste(
                lapply(foo, function(i) paste(i, "=", i)), 
            collapse = ","), 
            ")"
        )))
    }
    

    你可以使用它:

    ggplot(iris2, generateAES(colnames(iris2))) +
        geom_point()
    

    或者用管道:

    library(magrittr)
    iris2 %>%
        ggplot(generateAES(colnames(.))) +
            geom_point()
    

    generateAES 输出为aes 喜欢:

    Aesthetic mapping: 
    * `x`      -> `x`
    * `y`      -> `y`
    * `colour` -> `color`
    * `shape`  -> `shape`
    

    这是从文本字符串"aes(x = x,y = y,color = color,shape = shape)"生成的

    【讨论】:

    • 这实际上是我考虑过的,但是,基于提到的缺点here 我认为另一个答案更可行,但这肯定也有效!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-01
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多