【问题标题】:dplyr 0.7 tidy eval: convert character variables to factorsdplyr 0.7 tidy eval:将字符变量转换为因子
【发布时间】:2017-08-08 14:56:36
【问题描述】:

我有一个包含许多变量的数据集,其中一些是字符变量,我想将其转换为因子。由于要转换的变量很多,我想使用来自dplyr_0.7 的新 tidy eval 功能来完成此操作。这是我的数据中的一个最小示例:

data <- data.frame(factor1 = c("K", "V"), 
                   factor2 = c("E", "K"), 
                   other_var = 1:2, 
                   stringsAsFactors = FALSE)

我有一个命名列表,其中包含我要转换的每个变量的data.frame。列表中的这些data.frames 都具有相同的结构,可以在此示例中看到:

codelist_list <- list(factor1 = data.frame(Code = c("K", "V"), 
                                           Bezeichnung = c("Kauf", "Verkauf"), 
                                           stringsAsFactors = FALSE),
                      factor2 = data.frame(Code = c("E", "K"), 
                                           Bezeichnung = c("Eigengeschaeft", "Kundengeschaeft"), 
                                           stringsAsFactors = FALSE))

想做的是为每个变量定义这样的因子:

mutate(df, factor1 = factor(factor1, 
                            levels = codelist_list[["factor1"]][["Code"]],
                            labels = codelist_list[["factor1"]][["Bezeichnung"]]))

到目前为止我尝试过的如下:

convert_factors <- function(variable, df) {
  factor_variable <- enquo(variable)
  df %>% 
    mutate(!!quo_name(factor_variable) := factor(!!quo_name(factor_variable), 
                                                 levels = codelist_list[[variable]][["Code"]],
                                                 labels = codelist_list[[variable]][["Bezeichnung"]]))
}

首先,我想通过调用返回的convert_factors("factor1", data) 来检查我的函数convert_factors() 是否正常工作

  factor1 factor2 other_var
1    <NA>       E         1
2    <NA>       K         2

变量不显示值标签,而是替换为NA

最终目标是map 对我要转换的所有变量。在这里,我尝试了map(c("factor1", "factor2"), convert_factors, df = data),它返回了

(function (x, strict = TRUE) 中的错误:参数已被评估

我尝试按照http://dplyr.tidyverse.org/articles/programming.html 的指示进行操作,但这就是我想出的全部内容。

有谁知道问题出在哪里(希望能向我解释我的错误)。

【问题讨论】:

    标签: r dplyr rlang tidyeval


    【解决方案1】:

    我认为你混淆了quosures和strings:

    1. 在你的函数中,variable 是一个字符串,而不是一个表达式。所以你应该用rlang::sym而不是enquo将它转换成现状。

    2. quo_name 用于将表达式转换为字符串。由于variable已经是一个字符串,你可以直接在mutate的rhs(右手边)上做!!variable

    3. mutate 的 lhs 中,您需要使用 !! 取消 factor_variable,而不是尝试使用 quo_name 将其转换为字符串。

    纠正上述错误后,您的功能将起作用:

    convert_factors <- function(variable, df) {
        factor_variable <- rlang::sym(variable)
        df %>% 
            mutate(!!variable := factor(
                !!factor_variable, 
                levels = codelist_list[[variable]][["Code"]],
                labels = codelist_list[[variable]][["Bezeichnung"]]))
    }
    
    # > convert_factors('factor1', data)
    #   factor1 factor2 other_var
    # 1    Kauf       E         1
    # 2 Verkauf       K         2
    

    这是我的尝试:

    params <- lapply(codelist_list, setNames, nm = c('levels', 'labels'))
    
    convert_factors <- function(variable, df) {
        factor_variable <- rlang::sym(variable)
        factor_param <- c(list(factor_variable), params[[variable]])
    
        df %>% mutate(!!variable := do.call(factor, factor_param))
    }
    
    convert_factors('factor1', data)
    #   factor1 factor2 other_var
    # 1    Kauf       E         1
    # 2 Verkauf       K         2
    

    【讨论】:

    • 不错!我也试图回答这个问题,但我还不知道 rlang::sym 所以这对我来说太难了。很高兴我学会了那个,我今天也在为类似的事情而苦苦挣扎。我正在考虑传递 codelist 的各个元素以使函数自包含,但这已经是一个非常好的解决方案,所以我宁愿继续我的硕士论文.. ;)
    • @friep, sym 是将字符串转换为 quo 的便捷函数。 quo,enquo,quo_name,均来自rlang。我从社区学到了这个功能。祝你的论文好运。
    • 感谢您的回答。我以前不知道sym()。但是,正如我在问题中所说,这不是我的“最终目标”的解决方案。我忽略了我需要 mutate_at 来转换同一个 data.frame 中的所有变量,而不是每个结果中的一个。
    【解决方案2】:

    使用 tidy eval 和 dplyr 的 mt1022 很好的解决方案。然而,这个任务可以在不使用 base-R 的情况下完成:

    data[,names(codelist_list)] <- lapply(names(codelist_list), function(x) 
      data[,x] <- factor(data[,x],
                         levels = codelist_list[[x]][["Code"]],
                         labels = codelist_list[[x]][["Bezeichnung"]]))
    

    【讨论】:

    • 非常好!对我来说,base-R 表示法可以在没有任何特定附加组件的情况下处理这个问题并不明显。这看起来很干净。
    【解决方案3】:

    您可以使用mutate_at 处理此问题,使用funs 中的. 编码将函数一次应用于多个列。

    这种方法仍然涉及使用tidyevalcodelist_list 中提取正确的列表,同时通过. 引用变量。

    mutate_at(data, c("factor1", "factor2"), 
              funs( factor(., levels = codelist_list[[quo_name(quo(.))]][["Code"]],
                          labels = codelist_list[[quo_name(quo(.))]][["Bezeichnung"]]) ) )
    
      factor1         factor2 other_var
    1    Kauf  Eigengeschaeft         1
    2 Verkauf Kundengeschaeft         2
    

    如果您想将函数传递给mutate_at,您可以这样做,只需稍作改动即可。

    convert_factors = function(variable) {
         var2 = enquo(variable)
         factor(variable, levels = codelist_list[[quo_name(var2)]][["Code"]],
                labels = codelist_list[[quo_name(var2)]][["Bezeichnung"]]) 
    }
    
    mutate_at(data, c("factor1", "factor2"), convert_factors)
    
     factor1         factor2 other_var
    1    Kauf  Eigengeschaeft         1
    2 Verkauf Kundengeschaeft         2
    

    【讨论】:

    • aosmith,仅出于我自己的理解:如果我想定义一个函数,然后将在mutate_at 中调用,这会是什么样子。那么,是否必须更改quo_name...等等?
    • @der_grund 请参阅编辑以获取一个选项。主要开关是使用enquoquo_name 仍用于将带引号的符号转换为字符串,以便从列表中提取适当的元素。
    【解决方案4】:

    由于您只是使用字符串和 SE 函数(因子构造函数),因此您不需要表达式或 quosures。只需将名称取消引用与:=

    convert_factors <- function(variable, df) {
      factor <- factor(variable,
        levels = codelist_list[[variable]][["Code"]],
        labels = codelist_list[[variable]][["Bezeichnung"]]
      )
      mutate(df, !! variable := factor)
    }
    
    map(c("factor1", "factor2"), convert_factors, df = data)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多