【问题标题】:properly evaluating `rlang` expression inside list column正确评估列表列中的“rlang”表达式
【发布时间】:2018-08-31 14:03:50
【问题描述】:

这是 (using `rlang` for conditional labelling in `ggplot` using `ggrepel`) 的后续问题,它解决了我在自定义函数中遇到的问题,该函数使用表达式在标记数据点时过滤掉数据。但是答案又提出了一个我不知道如何解决的问题。

这是一个自定义函数,它使用rlang 评估用户输入的表达式以过滤掉数据,同时将标签附加到数据点。当不在列表列中使用时,此功能可以正常工作。例如-

# loading needed libraries
library(tidyverse)
library(ggplot2)
library(ggrepel)

# custom function
label_adder <- function(data, x, y, label.var, exp = NULL) {
  param_list <- as.list(match.call())

  if ("exp" %in% names(param_list)) {
    my_exp <- rlang::enquo(exp)
  }
  else {
    a <- "dplyr::row_number(x = .) > 0"
    my_exp <- rlang::quo(!!rlang::sym(a))
  }

  plot <-
    ggplot(mapping = aes(
      x = !!rlang::enquo(x),
      y = !!rlang::enquo(y)
    )) +
    geom_point(data = data) +
    geom_smooth(data = data, method = "lm") +
    geom_label_repel(
      data = data %>% filter(!!my_exp),
      mapping = aes(label = !!rlang::enquo(label.var))
    )
  return(plot)
}

# using the function
label_adder(
  data = datasets::iris,
  x = Sepal.Length,
  y = Sepal.Width,
  label.var = Species,
  exp = Sepal.Length > 7
)

但是当我使用与purrr::map 相同的功能时,它会失败。

# creating a list column
df.listcol <- datasets::iris %>%
  dplyr::mutate(.data = ., Species2 = Species) %>% # just creates a copy of this variable
  dplyr::group_by(.data = ., Species) %>%
  tidyr::nest(data = .)

# running function on dataframe with list columns
df.listcol %>% # creates a nested dataframe with list column called `data`
  dplyr::mutate( # creating a new list column of ggstatsplot outputs
    .data = .,
    plot = data %>%
      purrr::map(
        .x = .,
        .f = ~label_adder(
          data = .,
          x = Sepal.Length,
          y = Sepal.Width
        )
      )
  )
#> Error in mutate_impl(.data, dots): Evaluation error: Evaluation error: object 'dplyr::row_number(x = .) > 0' not found..

但如果我通过指定label.varexp 来使用该函数,它就可以正常工作。

# running function on dataframe with list columns
df.listcol %>% # creates a nested dataframe with list column called `data`
  dplyr::mutate( # creating a new list column of ggstatsplot outputs
    .data = .,
    plot = data %>%
      purrr::map(
        .x = .,
        .f = ~label_adder(
          data = .,
          x = Sepal.Length,
          y = Sepal.Width,
          label.var = Species,
          exp = Sepal.Length > 7
        )
      )
  )
#> # A tibble: 3 x 3
#>   Species    data              plot    
#>   <fct>      <list>            <list>  
#> 1 setosa     <tibble [50 x 5]> <S3: gg>
#> 2 versicolor <tibble [50 x 5]> <S3: gg>
#> 3 virginica  <tibble [50 x 5]> <S3: gg>

所以我的问题是为什么在未指定label.varexp 时函数会失败以及如何解决此问题?

reprex package (v0.2.0.9000) 于 2018 年 8 月 31 日创建。

【问题讨论】:

  • 您能否真正访问上一个示例中的情节。我得到Error: Column label` 必须是一维原子向量或列表`。代码确实运行没有错误,但情节列表不可行
  • 是的,那个错误是因为你在标签中使用了 Species 而不是 Species2。

标签: r ggplot2 rlang


【解决方案1】:

所以这里我们编辑函数,让过滤更加灵活。

# loading needed libraries
library(tidyverse)
library(ggplot2)
library(ggrepel)

# custom function
label_adder <- function(data, x, y, label.var, exp = NULL) {
  param_list <- as.list(match.call())
  label_data <- data %>% {if ("exp" %in% names(param_list)) filter(., !!enquo(exp)) else .}

  plot <-
    ggplot(mapping = aes(
      x = !!rlang::enquo(x),
      y = !!rlang::enquo(y)
    )) +
    geom_point(data = data) +
    geom_smooth(data = data, method = "lm") +
    geom_label_repel(
      data = label_data,
      mapping = aes(label = !!rlang::enquo(label.var))
    )
  return(plot)
}

df.listcol <- datasets::iris %>%
  dplyr::mutate(.data = ., Species2 = Species) %>% # just creates a copy of this variable
  dplyr::group_by(.data = ., Species) %>%
  tidyr::nest(data = .)



test <- df.listcol %>% mutate(plot = map(data, ~label_adder(., x = Sepal.Length, label.var = Species2, y = Sepal.Width)))
test
#> # A tibble: 3 x 3
#>   Species    data              plot    
#>   <fct>      <list>            <list>  
#> 1 setosa     <tibble [50 × 5]> <S3: gg>
#> 2 versicolor <tibble [50 × 5]> <S3: gg>
#> 3 virginica  <tibble [50 × 5]> <S3: gg>
test$plot[[2]]

test2 <- df.listcol %>% mutate(plot = map(data, ~label_adder(., x = Sepal.Length, label.var = Species2, y = Sepal.Width, exp = Sepal.Length > 6.5)))
test2
#> # A tibble: 3 x 3
#>   Species    data              plot    
#>   <fct>      <list>            <list>  
#> 1 setosa     <tibble [50 × 5]> <S3: gg>
#> 2 versicolor <tibble [50 × 5]> <S3: gg>
#> 3 virginica  <tibble [50 × 5]> <S3: gg>
test2$plot[[2]]

reprex package (v0.2.0) 于 2018 年 8 月 31 日创建。

【讨论】:

  • 抱歉,我还有一个小问题:为什么在使用label.expression 参数时,此函数不能与purrr::pmap 一起使用?它无法评估列表中提供的表达式,或者这是我对正在发生的事情的猜测。
  • 我不确定。您是否在列表中列出 args 并使用 pmap?
  • 我猜是引用问题
  • 是的,我做错了什么。会更多地玩弄它并尝试调试它。
猜你喜欢
  • 2017-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多