根据表达式的字符数组对数据框的列进行突变?

时间:2018-11-02 19:38:05

标签: r tidyverse

如何基于表达式的字符数组来更改数据框的列?例如,

我有:

library(tidyverse)
dat <- data_frame(id = 0:4, 
                  brand = c(NA, 'coke', 'pepsi', 'other', 'pepsi'), 
                  price = as.character(c(NA, 1, 1.10, 1.25, .99)))

model_feature_definitions_tmp <-
  data_frame(feature_id = 0:3,
             feature_name = c("intercept", "brand_coke", "brand_pepsi", "price"),
             feature_definition = c("as.numeric(id != 0)", "as.numeric(brand == 'coke')",
                                    "as.numeric(brand == 'pepsi')", "as.numeric(price)"))

我想要:

# # A tibble: 5 x 4
# intercept brand_coke brand_pepsi price
# <dbl>      <dbl>       <dbl> <dbl>
# 1         0         NA          NA    NA
# 2         1          1           0  1.00
# 3         1          0           1  1.10
# 4         1          0           0  1.25
# 5         1          0           1  0.99

以下作品:

library(tidyverse)
res_list <- list()
n <- nrow(model_feature_definitions_tmp)

for (i in 1:n) {
  mfd_i <- slice(model_feature_definitions_tmp, i)
  dat %>%
    transmute(eval(parse(text=mfd_i$feature_definition))) ->
    res_list[[i]]
}

res_list %>%
  bind_cols() %>% 
  setNames(model_feature_definitions_tmp$feature_name) ->
  model_feature_space

但是我怀疑这是最好的方法。我想有一种更好的方法,它不涉及for循环或*apply函数。也许可以在这里使用purrr包?

tidyverse解决方案是理想的,但不是必需的。

1 个答案:

答案 0 :(得分:1)

取消引号拼接(rlang的!!!)可以很好地完成此任务。

library(tidyverse)

dat <-
  data_frame(
    id = 0:4, 
    brand = c(NA, 'coke', 'pepsi', 'other', 'pepsi'), 
    price = as.character(c(NA, 1, 1.10, 1.25, .99))
  )

defs <-
  data_frame(
    feature_name = c("intercept", "brand_coke", "brand_pepsi", "price"),
    feature_definition = 
      c("as.numeric(id != 0)", "as.numeric(brand == 'coke')",
        "as.numeric(brand == 'pepsi')", "as.numeric(price)")
  )

本质上,您正在尝试执行以下操作(我认为吗?):

dat %>%
  transmute(
    intercept   = as.numeric(id != 0),
    brand_coke  = as.numeric(brand == 'coke'),
    brand_pepsi = as.numeric(brand == 'pepsi'),
    price       = as.numeric(price)
  )

这等同于首先捕获带引号的表达式,然后将它们拼接到...的{​​{1}}中:

dplyr::transmute

但是,您将表达式存储为字符串,因此必须将它们解析为可以加引号的表达式。在这里,我在字符串上进行映射以生成一个表达式列表,然后将它们拼接到quosures1 <- quos( intercept = as.numeric(id != 0), brand_coke = as.numeric(brand == 'coke'), brand_pepsi = as.numeric(brand == 'pepsi'), price = as.numeric(price) ) transmute(dat, !!! quosures1) 中,以构成一个数量表。我为列表中的元素命名,以便它们在quos中用作LHS名称:

transmute

当然,我认为第一个版本(不带引号和剪接)对于以后的读者来说会更容易阅读,但是如果您想减少代码重复,我可以看到第二个示例的参数(quosures2 <- quos(!!! map(defs$feature_definition, rlang::parse_expr)) %>% set_names(defs$feature_name) transmute(dat, !!! quosures2) )。因此,我倾向于避免将表达式存储为字符串。