在使用列名进行“映射”时如何使用准引用/整洁评估

How to use quasiquotation / tidy evaluation when doing `map` with column names

你能帮我理解准报价的工作原理吗? 我正在使用地图和计数功能,但它似乎无法正常工作。

第一次尝试:

map(names(starwars),~starwars %>% count(.x))
Error: Column `.x` is unknown

第二次尝试:

map(names(starwars),~starwars %>% count(!!.x))
#not useful [[1]]
# A tibble: 1 x 2
  `"name"`     n
  <chr>    <int>
1 name        87

[[2]]
# A tibble: 1 x 2
  `"height"`     n
  <chr>      <int>
1 height        87

第三次尝试:

map(names(starwars),~starwars %>% count(!!!.x))
# the same

另一个例子(处理函数):

如果我想制作一个接受列表和 相对于前一个元素更改列表中的每个元素 在该列表中:

my_list <- list("a" =1 , "b" = 2, "c" = 3)

# this obviously is not working (list + number)
> my_list+1
Error in my_list + 1 : non-numeric argument to binary operator
# this is a bit strange
my_list %>% map(~+1)
#this works fine
my_list %>% map(+1)
# as this
my_list %>% map(~.x+1)

# moving on to add the previous element to the next element
imap(my_list, my_list[[.y +1]] := .x %>% +1)
Error in `:=`(my_list[[.y + 1]], .x %>% +1) : could not find function ":="

# wrong eval 1?
imap(my_list, my_list[[.y +1]] <- .x %>% +1)
Error in eval(lhs, parent, parent) : object '.x' not found

# wrong eval 2?
imap(my_list, my_list[[.y +1]] <- !!.x %>% +1)
Error in eval(lhs, parent, parent) : object '.x' not found

# wrong symbol 1?
imap(my_list, my_list[[.y +1]] = .x %>% +1)
Error: unexpected '=' in "imap(my_list, my_list[[.y +1]] ="

我认为这个问题可以分解为 quasi-quotation 部分和 map 函数部分。

首先,~ starwars %>% count(.x)) 是 shorthand function(.x){starwars %>% count(.x)} 的稍微复杂的版本。所以我打算直接使用函数。

其次,names(starwars)给你一个字符向量。

因此,为了避免 map 带来的混淆,让我们从函数开始并将字符“eye_color”传递给它们。

尝试 1:dplyr 函数将符号视为表中的列

dplyr 函数在进行交互式数据分析时非常有用,因为它们允许我们使用符号来引用列。我推荐阅读: https://dplyr.tidyverse.org/articles/programming.html 了解更多信息。

func <- function(.x) { starwars %>% count(.x) }
func("eye_color")
Error: Column `.x` is unknown

在您的第一次尝试中,这会导致问题,因为 .x 是符号,所以 R 认为 .xstarwars.

中的列

尝试 2/3:count() / group_by() 期望符号而不是字符输入。

!!.x 替换为“eye_color”。但是“eye_color”不是symbol/name而是一个字符。

func_2 <- function(.x) { starwars %>% count(!!.x) }
func_2("eye_color")

# A tibble: 1 x 2
  `"eye_color"`     n
  <chr>         <int>
1 eye_color        87

这个奇怪的输出是按字符分组的结果。无论出于何种原因,dplyr 将整个数据框分组为“eye_color”,然后告诉您有 87 行。 starwars %>% count("hooray") 给出类似的输出。

插曲:我们要的是符号

编写 dplyr 函数的一种比较直观的方法是传递 symbols/names 并使用 {{.x}} 来评估承诺。 (不那么直观,你可以做 !!enquo(.x)。)

func_3 <- function(.x) {  starwars %>% count({{.x}}) }
func_3(eye_color)

# A tibble: 15 x 2
   eye_color         n
   <chr>         <int>
 1 black            10
 2 blue             19
 3 ...

这有效!

一个解决方案是将字符转换为符号

func_4 <- function(.x) { .x = as.symbol(.x)
                         starwars %>% count({{.x}}) }
func_4("eye_color")

# A tibble: 15 x 2
   eye_color         n
   <chr>         <int>
 1 black            10
 2 blue             19
 3 ...

这也行!

带回来map

在继续之前,我认为 nniloc 的解决方案更适合您的问题。

但是你可以如下使用地图

starwars %>% 
  select_if(negate(is.list)) %>% 
  names() %>% 
  map(function(.x) {x = as.symbol(.x)
      starwars %>% count( {{ x }} )
      }) 

starwars %>% 
  select_if(negate(is.list)) %>% 
  names() %>% 
  map(as.symbol) %>%
  map(function(.x) { 
      starwars %>% count( {{  .x }} )
      }) 

当你使用~符号时,.x现在是一个直接指代符号的“代词”,所以我们可以使用!!直接访问符号。 (这个我不是很懂)

starwars %>% 
  select_if(negate(is.list)) %>% 
  names() %>% 
  map(as.symbol) %>%
  map(~ starwars %>% count( !! .x  )) 

关于 imap(),您似乎想使用 python(或其他一些具有迭代功能的语言)进行编码。 imap()map2(.x, names(.x), ...) 的缩写,因此与 python 中的 enumerate() 不同。有像 seq_along 这样的 R 函数,它可以给你在对象中的位置,但我没有将它们与地图一起使用。