【问题标题】:Addressing x and y in aes by variable number通过变量编号寻址 aes 中的 x 和 y
【发布时间】:2013-03-10 14:24:44
【问题描述】:

我需要绘制一个散点图,其中使用列号而不是名称来寻址变量,即,而不是 ggplot(dat, aes(x=Var1, y=Var2)) 我需要类似 ggplot(dat, aes(x=dat[,1], y=dat[,2])) 的东西。 (我说“某事”是因为后者不起作用)。

这是我的代码:

showplot1<-function(indata, inx, iny){
  dat<-indata
  print(nrow(dat)); # this is just to show that object 'dat' is defined
  p <- ggplot(dat, aes(x=dat[,inx], y=dat[,iny]))
  p + geom_point(size=4, alpha = 0.5)
}

testdata<-data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
showplot1(indata=testdata, inx=2, iny=3)
# Error in eval(expr, envir, enclos) : object 'dat' not found

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    您的问题是aes 不知道您的函数的环境,它只在global environment 中查找。因此,函数中声明的变量 datggplot2aes 函数不可见除非你明确地传递它

    showplot1<-function(indata, inx, iny) {
        dat <- indata
        p <- ggplot(dat, aes(x=dat[,inx], y=dat[,iny]), environment = environment())
        p <- p + geom_point(size=4, alpha = 0.5)
        print(p)
    }
    

    注意ggplot() 命令中的参数environment = environment()。它现在应该可以工作了。

    【讨论】:

    • 是的,我怀疑是范围的问题,现在清楚了,我更喜欢这个解决方案。谢谢!
    【解决方案2】:

    我强烈建议使用aes_q 而不是将向量传递给aes(@Arun 的回答)。它可能看起来有点复杂,但它更灵活,例如更新数据。

    showplot1 <- function(indata, inx, iny){
      p <- ggplot(indata, 
                  aes_q(x = as.name(names(indata)[inx]), 
                        y = as.name(names(indata)[iny])))
      p + geom_point(size=4, alpha = 0.5)
    }
    

    这就是为什么它更可取的原因:

    # test data (using non-standard names)
    testdata<-data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
    names(testdata) <- c("a-b", "c-d", "e-f", "g-h", "i-j")
    testdata2 <- data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
    names(testdata2) <- c("a-b", "c-d", "e-f", "g-h", "i-j")
    
    # works
    showplot1(indata=testdata, inx=2, iny=3)
    # this update works in the aes_q version
    showplot1(indata=testdata, inx=2, iny=3) %+% testdata2
    

    注意:ggplot2 v2.0.0 起,aes_q() 已替换为 aes_(),以与其他软件包中 NSE 功能的 SE 版本保持一致。 p>

    【讨论】:

    • 确实,您的解决方案看起来更灵活,我也喜欢它制作正确的轴标题...谢谢!
    • 从 ggplot2 v2.0.0 开始:aes_q() 已被替换为 aes_() 以与其他包中 NSE 函数的 SE 版本保持一致 github.com/hadley/ggplot2/blob/master/…
    • 这个答案对我也很有效,谢谢。我必须说,ggplot 似乎不是为处理大量数量(数据框中的行)的人设计的。
    【解决方案3】:

    试试:

    showplot1 <- function(indata, inx, iny) {
        x <- names(indata)[inx] 
        y <- names(indata)[iny] 
        p <- ggplot(indata, aes_string(x = x, y = y))
        p + geom_point(size=4, alpha = 0.5)
    }
    

    已编辑以显示正在发生的事情 - aes_string 使用带引号的参数,名称使用您的数字获取它们。

    【讨论】:

    • 对不起@alexwhan,这对我来说不是很清楚 - 你能解释一下吗?谢谢!
    • 第一个版本实际上并没有回答您的问题 - 尝试编辑
    • @alexwhan,请在发帖前检查您的答案。我已对您的两个答案分别进行了编辑。
    • 最后一个版本适用于此示例(之前的编辑,使用aes 而不是aes_string,不起作用)。虽然它不适用于我的真实数据,因为我的表的名称中有连字符,这会在处理时出错:例如,如果该列名为 someName-one,我会收到错误 Error in eval(expr, envir, enclos) : object 'someName' not found。同时,当我将您的解决方案与environment() 一起使用时,此名称的错误不会产生任何问题,因此对我来说仍然是可取的。
    • @VasilyA,最后一个版本是我的编辑。是的,我想到了这个名字不寻常的问题。但既然你在这里发帖,我觉得这是你的情况。我很高兴你在这里写了它,它会帮助别人。
    【解决方案4】:

    使用ggplot2 V3.0.0 的新功能对@Shadow 的回答进行了变体:

    showplot <- function(indata, inx, iny){
      nms <- names(indata)
      x <- nms[inx]
      y <- nms[iny]
      p <- ggplot(indata, aes(x = !!ensym(x), y = !!ensym(y)))
      p + geom_point(size=4, alpha = 0.5)
    }   
    
    testdata <- data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
    names(testdata) <- c("a-b", "c-d", "e-f", "g-h", "i-j")
    showplot(indata=testdata, inx=2, iny=3)
    

    ensym 从包含在变量中的字符串创建一个符号(所以我们首先必须在函数的开头创建这些变量),然后 !! 取消引用它,这意味着它会像你喂过一样工作函数原始名称。

    !! 仅在旨在支持它的函数的上下文中工作,通常是 tidyverse 函数,否则它仅表示“不是”(类似于 as.logical)..

    【讨论】:

    • 谢谢安托万!如果您有时间,也许您可​​以添加几句话来解释这是如何工作的? (对于像我这样的其他不那么高级的用户)如果不是,这是我的理解:!! 运算符 - 发音为 'bang-bang' - 取消引用以前由 ensym() 引用的名称,它是quasiquotation 详细描述,例如here。 (我认为值得解释,因为我花了很长时间才弄清楚;谷歌搜索“!!”显然不起作用。)
    【解决方案5】:

    为了完整起见,我认为使用列名而不是索引更安全,因为数据框中的列位置可能会更改,从而导致意外结果。

    下面的plot_duo 函数(取自this answer)可以将输入用作字符串或裸列名称

    library(rlang)
    library(purrr)
    library(dplyr)
    library(ggplot2)
    
    theme_set(theme_classic(base_size = 14))
    set.seed(123456)
    testdata <- data.frame(v1 = rnorm(100), v2 = rnorm(100), v3 = rnorm(100), 
                           v4 = rnorm(100), v5 = rnorm(100))
    
    plot_duo <- function(df, plot_var_x, plot_var_y) {
    
      # check if input is character or bare column name to 
      # use ensym() or enquo() accordingly
      if (is.character(plot_var_x)) {
        print('character column names supplied, use ensym()')
        plot_var_x <- ensym(plot_var_x)
      } else {
        print('bare column names supplied, use enquo()')
        plot_var_x <- enquo(plot_var_x)
      }
    
      if (is.character(plot_var_y)) {
        plot_var_y <- ensym(plot_var_y)
      } else {
        plot_var_y <- enquo(plot_var_y)
      }
    
      # unquote the variables using !! (bang bang) so ggplot can evaluate them
      pts_plt <- ggplot(df, aes(x = !! plot_var_x, y = !! plot_var_y)) + 
        geom_point(size = 4, alpha = 0.5)
    
      return(pts_plt)
    }
    

    使用purrr::map() 跨列应用plot_duo 函数

    ### use character column names
    plot_vars1 <- names(testdata)
    plt1 <- plot_vars1 %>% purrr::map(., ~ plot_duo(testdata, .x, "v1"))
    #> [1] "character column names supplied, use ensym()"
    #> [1] "character column names supplied, use ensym()"
    #> [1] "character column names supplied, use ensym()"
    #> [1] "character column names supplied, use ensym()"
    #> [1] "character column names supplied, use ensym()"
    
    str(plt1, max.level = 1)
    #> List of 5
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    
    # test plot
    plt1[[3]]
    

    ### use bare column names
    # Ref: https://stackoverflow.com/a/49834499/
    plot_vars2 <- rlang::exprs(v2, v3, v4)
    plt2 <- plot_vars2 %>% purrr::map(., ~ plot_duo(testdata, .x, rlang::expr(v1)))
    #> [1] "bare column names supplied, use enquo()"
    #> [1] "bare column names supplied, use enquo()"
    #> [1] "bare column names supplied, use enquo()"
    
    str(plt2, max.level = 1)
    #> List of 3
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    #>  $ :List of 9
    #>   ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
    
    plt1[[2]]
    

    reprex package (v0.2.1.9000) 于 2019-02-18 创建

    【讨论】:

      【解决方案6】:

      aes_()aes_quote() 方法现在已软弃用。与准引用一致的一种简单方法是通过.data[[col_name]] 调用列名。您可以根据位置轻松提取这些。例如:

      library(ggplot2)
      library(dplyr)
      
      showplot1<-function(indata, inx, iny){
        dat<-indata
        col_names <- names(indata)
        col_name_x <- col_names[[inx]]
        col_name_y <- col_names[[iny]]
        
        print(nrow(dat)); # this is just to show that object 'dat' is defined
        p <- ggplot(dat, aes(x=.data[[col_name_x]], y=.data[[col_name_y]]))
        p + geom_point(size=4, alpha = 0.5)
      }
      
      testdata<-data.frame(v1=rnorm(100), v2=rnorm(100), v3=rnorm(100), v4=rnorm(100), v5=rnorm(100))
      showplot1(indata=testdata, inx=2, iny=3)
      #> [1] 100
      

      reprex package (v2.0.0) 于 2021 年 9 月 22 日创建

      【讨论】:

        【解决方案7】:

        我暂时找到的临时解决方案:

        showplot1<-function(indata, inx, iny){
          dat<-data.frame(myX=indata[,inx], myY=indata[,iny])
          print(nrow(dat)); # this is just to show that object 'dat' is defined
          p <- ggplot(dat, aes(x=myX, y=myY))
          p + geom_point(size=4, alpha = 0.5)
        }
        

        但我不太喜欢它,因为在我的真实代码中,我需要来自 indata 的其他列,在这里我必须在 dat&lt;- 中明确定义所有列...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-12
          • 2022-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-27
          • 1970-01-01
          相关资源
          最近更新 更多