当前位置: 首页 > 知识库问答 >
问题:

在以列标签作为字符向量的函数中使用dplyr动词

令狐良骏
2023-03-14

我想创建一个函数,它接受一个数据帧和一个包含列名的字符向量作为输入,并以一种安全的方式在其中使用整齐的引用函数。

我相信我有一个我想做的工作的例子。我想知道是否有更优雅的解决方案,或者我对这个问题的想法不正确(也许我不想这么做?)。据我所知,为了避免变量作用域问题,我需要将列名包装在.data[[]中,并在取消引用之前将其作为表达式。

关于之前的问题,这个答案是正确的,但我想将代码抽象为一个函数。github问题询问了这个问题,但使用rlang::syms在我看来是行不通的,因为列标签与. data的组合使其成为表达式而不是符号。这里和这里解决了问题,但据我所知,没有考虑到一个微妙的错误,即如果变量在数据框中不作为列标签存在,或者解决方案不适用于输入是标签向量的情况,它们可能会从环境中泄漏。

# Setup
suppressWarnings(suppressMessages(library("dplyr")))
suppressWarnings(suppressMessages(library("rlang")))

# define iris with and without Sepal.Width column
iris <- tibble::as_tibble(iris)
df_with_missing <- iris %>% select(-Sepal.Width)
# This should not be findable by my function
Sepal.Width <- iris$Sepal.Width * -1

################
# Now lets try a function for which we programmatically define the column labels
programmatic_mutate_y <- function(df, col_names, safe = FALSE) {
  # Add .data[[]] to the col_names to make evalutation safer
  col_exprs <- rlang::parse_exprs(
    purrr::map_chr(
      col_names,
      ~ glue::glue(stringr::str_c('.data[["{.x}"]]'))
    )
  )

  output <- dplyr::mutate(df, product = purrr::pmap_dbl(list(!!!col_exprs), ~ prod(...)))
  output
}
################
# The desired output
testthat::expect_error(programmatic_mutate_y(df_with_missing, c("Sepal.Width", "Sepal.Length")))
programmatic_mutate_y(iris, c("Sepal.Width", "Sepal.Length"))
#> # A tibble: 150 x 6
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width Species product
#>           <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
#>  1          5.1         3.5          1.4         0.2 setosa     17.8
#>  2          4.9         3            1.4         0.2 setosa     14.7
#>  3          4.7         3.2          1.3         0.2 setosa     15.0
#>  4          4.6         3.1          1.5         0.2 setosa     14.3
#>  5          5           3.6          1.4         0.2 setosa     18  
#>  6          5.4         3.9          1.7         0.4 setosa     21.1
#>  7          4.6         3.4          1.4         0.3 setosa     15.6
#>  8          5           3.4          1.5         0.2 setosa     17  
#>  9          4.4         2.9          1.4         0.2 setosa     12.8
#> 10          4.9         3.1          1.5         0.1 setosa     15.2
#> # … with 140 more rows

由reprex包(v0.3.0)在2019-08-09创建

共有2个答案

汲永嘉
2023-03-14

我们可以将col_names转换为具有parse_expr粘贴的单个表达式,然后在以mutate进行评估时取消引用:

library(dplyr)
library(rlang)

programmatic_mutate_y <- function(df, col_names){
  mutate(df, product = !!parse_expr(paste(col_names, collapse = "*")))
}

输出:

> parse_expr(paste(c("Sepal.Width", "Sepal.Length"), collapse = "*"))
Sepal.Width * Sepal.Length

> programmatic_mutate_y(df_with_missing, c("Sepal.Width", "Sepal.Length"))
> Error: object 'Sepal.Width' not found 

> programmatic_mutate_y(iris, c("Sepal.Width", "Sepal.Length"))
# A tibble: 150 x 6
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species product
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
 1          5.1         3.5          1.4         0.2 setosa     17.8
 2          4.9         3            1.4         0.2 setosa     14.7
 3          4.7         3.2          1.3         0.2 setosa     15.0
 4          4.6         3.1          1.5         0.2 setosa     14.3
 5          5           3.6          1.4         0.2 setosa     18  
 6          5.4         3.9          1.7         0.4 setosa     21.1
 7          4.6         3.4          1.4         0.3 setosa     15.6
 8          5           3.4          1.5         0.2 setosa     17  
 9          4.4         2.9          1.4         0.2 setosa     12.8
10          4.9         3.1          1.5         0.1 setosa     15.2
# ... with 140 more rows
嵇丰
2023-03-14

我认为你把事情搞复杂了。使用< code>_at变量,您几乎可以在每个< code>dplyr函数中使用字符串作为参数。< code>purrr::pmap_dbl()用于按行映射计算。

programmatic_mutate_y_v1 <- function(df, col_names, safe = FALSE) {
    df["product"] <- purrr::pmap_dbl(dplyr::select_at(df,col_names),prod)
    return(df)
}

programmatic_mutate_y_v1(iris, c("Sepal.Width", "Sepal.Length"))
# A tibble: 150 x 6
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species product
          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
 1          5.1         3.5          1.4         0.2 setosa     17.8
 2          4.9         3            1.4         0.2 setosa     14.7
 3          4.7         3.2          1.3         0.2 setosa     15.0
 4          4.6         3.1          1.5         0.2 setosa     14.3
 5          5           3.6          1.4         0.2 setosa     18  
 6          5.4         3.9          1.7         0.4 setosa     21.1
 7          4.6         3.4          1.4         0.3 setosa     15.6
 8          5           3.4          1.5         0.2 setosa     17  
 9          4.4         2.9          1.4         0.2 setosa     12.8
10          4.9         3.1          1.5         0.1 setosa     15.2
# ... with 140 more rows
 类似资料:
  • 有没有一种更简洁的方法从带有数据库后端的tbl中获得dplyr tbl的一列作为向量(即数据帧/表不能直接子集)? 那太容易了,所以 但似乎有点笨拙。

  • 我试图把我对plyr的理解转移到dplyr中,但是我不知道如何按多个列分组。 在将plyr示例翻译成dplyr式语法时,我遗漏了什么? 编辑2017:Dplyr已更新,因此可以使用更简单的解决方案。查看当前选定的答案。

  • 我想使用<code>dplyr::mutate()</code>在数据帧中创建多个新列。应动态生成列名及其内容。 来自iris的示例数据: 我创建了一个函数,可以从<code>Petal中修改我的新列。宽度变量: 现在我创建一个循环来构建我的列: 然而,由于mutate认为varname是一个文字变量名,因此循环只创建一个新变量(称为varname),而不是四个(称为petal.2-petal.5

  • 我试图计算在一个Tibble中源向量和比较向量之间的Jaccard相似度。 jaccard_sim中的所有值都为零。但是,如果我们运行类似这样的东西,我们得到第一个条目的正确的Jaccard相似度为0.2:

  • 我想使用dplyr的mutate_at函数将一个函数应用于数据帧中的几个列,其中该函数输入它直接应用到的列以及数据帧中的另一列。 作为一个具体的例子,我希望改变以下数据帧 与调用类似 返回一个看起来像这样的数据框 所需的调用将类似于以下对的调用: 我知道这可以通过几种方式在base R中实现,但为了可读性、与数据库的接口等,我特别希望使用dplyr的mutate\u at函数来实现这一目标。 在d

  • 我想使用在数据帧中创建多个新列。列名及其内容应动态生成。 来自IRIS的示例数据: 我创建了一个函数来从变量中更改新列: