ggplot2 函数 - 检查用户输入变量是否应该是映射美学

ggplot2 function - checking whether user input variable should be a mapped aesthetic

我写了一个函数来制作散点图,允许用户输入点的大小作为数值(保存在 aes() 调用之外)或作为变量要映射的数据框(需要进入 aes() 调用)。我远不是 NSE 方面的专家,虽然我已经开始工作了,但我觉得一定有更好的方法来做到这一点?

函数简化版如下:

library(tidyverse)

data <- tibble(x = 1:10, y = 1:10)

test_func <- function(data, variable = 6){
  # capture the variable in vars (I think quote would also work in this function)
  variable <- vars({{variable}})
  
  # convert it to a string and check if the string starts with a digit
  # i.e. checking if this is a simple point size declaration not an aes mapping
  is_number <- variable[[1]] %>% 
    rlang::as_label() %>% 
    str_detect("^[:digit:]*$")
  
  # make initial ggplot object
  p <- ggplot(data, aes(x = x, y = y))
  
  # if variable is a simple number, add geom_point with no aes mapping
  if(is_number){
    variable <- variable[[1]] %>% 
      rlang::as_label() %>% 
      as.numeric()
    
    p <- p + geom_point(size = variable)
  } else{
    # otherwise it must be intended as an aes mapping variable  
    variable <- variable[[1]] %>% 
      rlang::as_label()
    
    p <- p + geom_point(aes(size = .data[[variable]]))
  }
  p
}

# works as a number
test_func(data, 10)

# works as a variable
test_func(data, y)

reprex package (v2.0.0)

于 2021-04-08 创建

你是对的。 NSE 可以有点 fiddle。但是如果你使用 is.object(),处理 NSE,然后在你的 geom_point 调用中使用 show.legend 可能会作弊......

test_func <- function(data, variable = 6){
  qVariable = enquo(variable)
  needLegend <- !is.object(qVariable)
  data %>% 
    ggplot(aes(x = x, y = y)) + 
    geom_point(
      aes(size = !! qVariable), 
      show.legend=needLegend
    )
}

在你的两个测试用例中给出与你的函数相同的输出。

一个选项是检查用户提供给 variable 的内容是否是 data 中的列。如果是,请在 aes() 映射中使用该列。如果不是,则评估变量并将结果提供给 aes() 之外的 size:

test_func <- function(data, variable = 6) {
  v <- enquo(variable)
  gg <- ggplot(data, aes(x=x, y=y))
  
  if(exists(rlang::quo_text(v), data))
    gg + geom_point(aes(size=!!v))
  else
    gg + geom_point(size = rlang::eval_tidy(v))
}

# All of these work as expected:
test_func(data)      # Small points
test_func(data, 10)  # Bigger points
test_func(data, x)   # Using column x

作为奖励,此解决方案允许您传递存储在变量中的值,而不是直接输入数字。只要变量名不在 data 中,它就会被正确计算并馈送到 size=:

z <- 20
test_func(data, z)   # Works