使用 apply 或 map 将列名传递到函数中

Pass column names into a function using apply or map

我想对同一个数据框应用多个函数。但是,我无法成功地将列名作为 purrr::imap 中的参数传递。我一直收到以下错误:

Error in UseMethod("select") : no applicable method for 'select' applied to an object of class "character"

我尝试了很多组合进行评估(例如,使用 !!![[enquosys.lang 等等)。当我将函数(例如 check_1)直接应用于数据框时,select 工作正常。但是,当我尝试使用 imapexec 将列名作为参数传递时,它不起作用。列名的格式是问题的一部分(例如,1.1.),但我试过引号和单引号等

这是 的后续,但 post 和解决方案侧重于将多个函数应用于各个列。现在,我需要应用多个函数,这些函数在数据框中使用多个列;因此,需要在函数中指定列名。

最小示例

数据

df <- structure(
  list(
    `1.1.` = c("Andrew", "Max", "Sylvia", NA, "1",
               NA, NA, "Jason"),
    `1.2.` = c(1, 2, 2, NA, 4, 5, 3, NA),
    `1.2.1.` = c(
      "cool", "amazing", "wonderful", "okay",
      NA, NA, "chocolate", "fine"
    )
  ),
  class = "data.frame",
  row.names = c(NA, -8L)
)

我试过的

library(purrr)
library(dplyr)

check_1 <- function(x, col1, col2) {
  x %>%
    dplyr::select(col1, col2) %>%
    dplyr::mutate(row.index = row_number()) %>%
    dplyr::filter(col1 == "Jason" & is.na(col2) == TRUE) %>%
    dplyr::select(row.index) %>%
    unlist() %>%
    as.vector()
}

check_2 <- function(x, col1, col2) {
  index <- x %>%
    dplyr::select(col1, col2) %>%
    dplyr::mutate(row.index = row_number()) %>%
    dplyr::filter(col1 >= 3 & col1 <= 5 & is.na(col2) == TRUE) %>%
    dplyr::select(row.index) %>%
    unlist() %>%
    as.vector()
  return(index)
}

checks <-
  list("df" = list(fn = check_1, pars = list(col1 = "1.1.", col2 = "1.2.")),
       "df" = list(fn = check_2, pars = list(col1 = "1.2.", col2 = "1.2.1.")))

results <-
  purrr::imap(checks, ~ exec(.x$fn, x = .y,!!!.x$pars))

预期输出

> results
$df
[1] 8

$df
[1] 5 6

除了“class 字符”错误外,当我尝试单独测试 check_2 函数时,我还会遇到另一个错误,其中 returns 没有预期值。

[1] 1.2.      1.2.1.    row.index
<0 rows> (or 0-length row.names)

我看过许多其他类似的 SO posts(例如,),但是 none 已经为我解决了这个问题。

第一个问题是您传递了数据框的名称而不是数据框本身。这就是为什么当您尝试 select 从字符串中得到第一个错误时。要解决此问题,请将数据框添加到您正在循环的列表中。

第二个问题是,当您将列名作为字符串传递时,您必须告诉 dplyr 这些字符引用数据中的列。这可以通过例如实现使用 .data 代词。

最后,您可以简单地使用 dplyr::pull:

而不是 select + unlist + as.vector
library(purrr)
library(dplyr)

check_1 <- function(x, col1, col2) {
  x %>%
    dplyr::select(all_of(c(col1, col2))) %>%
    dplyr::mutate(row.index = row_number()) %>%
    dplyr::filter(.data[[col1]] == "Jason" & is.na(.data[[col2]]) == TRUE) %>%
    dplyr::pull(row.index)
}

check_2 <- function(x, col1, col2) {
  x %>%
    dplyr::select(all_of(c(col1, col2))) %>% 
    dplyr::mutate(row.index = row_number()) %>%
    dplyr::filter(.data[[col1]] >= 3 & .data[[col1]] <= 5 & is.na(.data[[col2]]) == TRUE) %>%
    dplyr::pull(row.index)
}

checks <-
  list(df = list(df = df, fn = check_1, pars = list(col1 = "1.1.", col2 = "1.2.")),
       df = list(df = df, fn = check_2, pars = list(col1 = "1.2.", col2 = "1.2.1.")))

purrr::map(checks, ~ exec(.x$fn, x = .x$df, !!!.x$pars))
#> $df
#> [1] 8
#> 
#> $df
#> [1] 5 6

使用select({{col1}},{{col2}}) 这很可能对你有帮助