R:根据条件列表创建指标列

R: Create Indicator Columns from list of conditions

我有一个数据框和一些条件。每个条件都应该检查数据帧特定列中的值是否在一组有效值内。

这是我试过的:

# create the sample dataframe
age <- c(120, 45)
sex <- c("x", "f")

df <-data.frame(age, sex)

# create the sample conditions
conditions <- list(
  list("age", c(18:100)),
  list("sex", c("f", "m"))
)

addIndicator <- function (df, columnName, validValues) {
  indicator <- vector()

  for (row in df[, toString(columnName)]) {
    # for some strange reason, %in% doesn't work correctly here, but always returns FALSe
    indicator <- append(indicator, row %in% validValues)
  }
  df <- cbind(df, indicator)

  # rename the column
  names(df)[length(names(df))] <- paste0("I_", columnName)

  return(df)
}

for (condition in conditions){
  columnName <- condition[1]
  validValues <- condition[2]
  df <- addIndicator(df, columnName, validValues)
}

print(df)

然而,这导致所有条件都被认为不满足——这不是我所期望的:

  age sex I_age I_sex
1 120   x FALSE FALSE
2  45   f FALSE FALSE

我认为 %in% 不是 return 预期的结果。我检查了 typeof(row) 并试图将其归结为一个最小的例子。在一个简单的 ME 中,变量的类型和值相同,%in% 可以正常工作。因此,在我尝试应用它的上下文中一定有问题。由于这是我第一次尝试用 R 编写任何东西,所以我被困在这里。

我哪里做错了,我怎样才能达到我想要的效果?

conditions 似乎是一个嵌套列表。当您使用:

validValues <- condition[2]

在你的 for 循环中,你的结果也是一个列表。

要获取用于 %in% 的值向量,您可以通过以下方式提取 [[

validValues <- condition[[2]]

获取指标的简化方法可以使用一个简单的列表:

conditions_lst <- list(age = 18:100, sex = c("f", "m"))

并使用 sapply 而不是 for 循环:

cbind(df, sapply(setNames(names(df), paste("I", names(df), sep = "_")), function(x) {
  df[[x]] %in% conditions_lst[[x]]
}))

输出

  age sex I_age I_sex
1 120   x FALSE FALSE
2  45   f  TRUE  TRUE

如果您更喜欢使用 tidyverse 系列软件包的方法:

library(tidyverse)

allowed_values <- list(age = 18:100, sex = c("f", "m"))

df %>%
  imap_dfr(~ .x %in% allowed_values[[.y]]) %>%
  rename_with(~ paste0('I_', .x)) %>%
  bind_cols(df)

imap_dfr 允许您使用 lambda 函数操作 df 中的每一列。 .x 引用列内容,.y 引用名称。

rename_with 使用另一个 lambda 函数重命名列,bind_cols 将结果与原始数据框组合。

我从本的回答中借用了简化的条件列表。我发现我的方法更具可读性,但这是一个品味问题,也取决于您是否已经在其他地方使用 tidyverse。

另一种使用 acrosscur_column() 的方法(并且严重依赖 severin 的解决方案):

library(tidyverse)

df <- tibble(age = c(12, 45), sex = c('f', 'f'))
allowed_values <- list(age = 18:100, sex = c("f", "m"))

df %>%
  mutate(across(c(age, sex),
                c(valid = ~ .x %in% allowed_values[[cur_column()]])
                )
         )

参考:https://dplyr.tidyverse.org/articles/colwise.html#current-column

相关问题: