【问题标题】:How to dynamically change data type of columns in data frame如何动态更改数据框中列的数据类型
【发布时间】:2019-01-10 17:58:01
【问题描述】:

我将数据导入 R 的平台不支持指定数据类型,因此我的所有列都是 character。我有一个 Excel 文件,它指定哪些列是 factor,包括相关的 labelslevels。现在,我正在尝试编写一个函数来动态更改我的 data.frame 各列的数据类型

感谢对这个问题 (dplyr - mutate: use dynamic variable names) 的出色回答,我设法编写了以下函数,其中我将列名动态设置为 mutate 函数。

readFactorData <- function(filepath) {
    t <- read.xlsx(filepath)
    sapply(nrow(t), function(i) {
      colname <- as.character(t[i, "Item"])
      factorLevels <- t[i, 3:ncol(t)][which(!is.na(t[i, 3:ncol(t)]))]
      totalLevels <- length(factorLevels)
      listOfLabels <- as.character(unlist(factorLevels))

      mutate(d, !!colname := factor(d[[colname]], labels=(1:totalLevels), levels=listOfLabels))
        # requires dplyr v.0.7+
        # the syntax `!!variablename:=` forces evaluation of the variablename before evaluating the rest of the function
    })
}

它有效,每次迭代都会返回整个数据框,相关列 (colname) 更改为因子。但是,每次迭代都会覆盖前一次,所以这个函数只返回i 的最后一个结果。如何确保我最终得到 1 个单个数据框,其中保存了所有相关列?

示例数据(确保注释掉上面函数的第一行,因为我们在这里定义了t):

 d <- data.frame("id" = sample(100:999, 10), "age" = sample(18:80, 10), "factor1" = c(rep("a", 3), rep("b", 3), rep("c", 4)), "factor2" = c("x","y","y","y","y","x","x","x","x","y"), stringsAsFactors = FALSE)
 t <- data.frame("Item" = c("factor1","factor2"), "Label" = c("This is factor 1", "This is factor 2"), "level1" = c("a","x"), "level2" = c("b","y"), "level3" = c("c","NA"))

【问题讨论】:

    标签: r dplyr sapply


    【解决方案1】:

    如果我理解正确,您有一个数据框,其中包含另一个数据框的因子列值。您想从第一个 df 中提取这些并在第二个 df 中改变这些列并将它们转换为因子。

    如何保留列名的向量然后将它们全部变异?

    colnames <- t %>%
      pull(Item) %>%
      as.character()
    
    d_with_factors <- d %>%
      mutate_at(colnames, as.factor)
    

    然后

    sapply(d_with_factors, class)
    

    返回

           id       age   factor1   factor2 
    "integer" "integer"  "factor"  "factor" 
    

    【讨论】:

    • 如果我不需要手动指定每个因素的水平和标签,那也可以。你会怎么做?由于每个因素都有不同的级别和标签,我需要遍历它们,但是当使用sapply() 时,它不会采用更新的数据帧,而是每次都使用原始数据帧,最后只得到最后一个循环的结果
    • 我仍然不确定您为什么需要手动添加标签和级别。至少在您使用 t 和 d 的示例中,您在两个数据框中都有相同的因子值。我还看到您将 nrow(t) 发送到 sapply 函数,该函数最终只会将数字 2 发送到函数(如 i)。您可以使用 sapply(1:nrow(t), ...) 这样它就会在您的所有数据框(而不仅仅是最后一行)上运行。
    【解决方案2】:

    如果您想在您的data.frame 中将所有factor 转换为character,您可以使用dplyrmutate_if。否则,如果您想使用列名向量,@Eden Z 的 答案将为您完成。

    library(tidyverse)
    
    d_out <- d %>% 
      mutate_if(is.character, as.factor)
    
    d_out
    
    #    id age factor1 factor2
    #1  933  61       a       x
    #2  208  52       a       y
    #3  193  25       a       y
    #4  231  47       b       y
    #5  595  78       b       y
    #6  675  28       b       x
    #7  387  71       c       x
    #8  386  80       c       x
    #9  893  20       c       x
    #10 272  23       c       y
    

    当你可以检查你的变量的类时:

    sapply(d_out, class)
    
    #       id       age   factor1   factor2 
    #"integer" "integer"  "factor"  "factor" 
    

    【讨论】:

      【解决方案3】:

      下面的函数映射为您要更改的每个命名列指定的 readr::parse_* 函数,然后允许您为每个指定 args(例如,如果使用 parse_factor,则为 levels)。

      library(tidyverse)
      
      parse_cols <- function(df, f, col_names, levels, ...){
        # df: dataframe, f: char vec, col_names: char vec, levels: list of char vecs,
        # ...: list of other potential args for parse_*
        params_t <- tibble(x = map(col_names, ~df[[.x]]), levels = levels, ...) %>% transpose()
      
        new_cols <- map2_df(.x = structure(f, names = col_names), 
                            .y = params_t, 
                            ~R.utils::doCall(.x, args = .y, .ignoreUnusedArgs = TRUE))  
      
        df[names(new_cols)] <- new_cols
        df
      }
      
      # function inputs -- perhaps just requiring a tibble input would be safer
      parsings_vec <- c("parse_factor","parse_double", "parse_factor")
      cols_vec <- c("manufacturer", "cty", "class")
      factors_list <- list(unique(mpg[["manufacturer"]]), NULL, unique(mpg[["class"]]))
      
      parse_cols(df = mpg, f = parsings_vec, col_names = cols_vec, levels = factors_list) 
      #> # A tibble: 234 x 11
      #>    manufacturer model displ  year   cyl trans drv     cty   hwy fl    cla~
      #>    <fct>        <chr> <dbl> <int> <int> <chr> <chr> <dbl> <int> <chr> <fc>
      #>  1 audi         a4      1.8  1999     4 auto~ f        18    29 p     com~
      #>  2 audi         a4      1.8  1999     4 manu~ f        21    29 p     com~
      #>  3 audi         a4      2    2008     4 manu~ f        20    31 p     com~
      #>  4 audi         a4      2    2008     4 auto~ f        21    30 p     com~
      #>  5 audi         a4      2.8  1999     6 auto~ f        16    26 p     com~
      #>  6 audi         a4      2.8  1999     6 manu~ f        18    26 p     com~
      #>  7 audi         a4      3.1  2008     6 auto~ f        18    27 p     com~
      #>  8 audi         a4 q~   1.8  1999     4 manu~ 4        18    26 p     com~
      #>  9 audi         a4 q~   1.8  1999     4 auto~ 4        16    25 p     com~
      #> 10 audi         a4 q~   2    2008     4 manu~ 4        20    28 p     com~
      #> # ... with 224 more rows
      

      【讨论】:

        猜你喜欢
        • 2021-06-16
        • 2022-07-07
        • 2018-01-09
        • 1970-01-01
        • 1970-01-01
        • 2018-05-28
        • 1970-01-01
        • 2017-05-09
        • 2012-02-20
        相关资源
        最近更新 更多