【问题标题】:Coerce multiple columns to factors at once一次将多个列强制转换为因子
【发布时间】:2016-01-15 18:31:44
【问题描述】:

我有一个如下示例数据框:

data <- data.frame(matrix(sample(1:40), 4, 10, dimnames = list(1:4, LETTERS[1:10])))

我想知道如何选择多个列并将它们一起转换为因子。我通常以data$A = as.factor(data$A) 之类的方式进行操作。但是当数据框非常大并且包含很多列时,这种方式将非常耗时。有谁知道更好的方法吗?

【问题讨论】:

标签: r dataframe r-factor


【解决方案1】:

选择一些列来强制转换为因子:

cols <- c("A", "C", "D", "H")

使用lapply() 强制替换所选列:

data[cols] <- lapply(data[cols], factor)  ## as.factor() could also be used

检查结果:

sapply(data, class)
#        A         B         C         D         E         F         G 
# "factor" "integer"  "factor"  "factor" "integer" "integer" "integer" 
#        H         I         J 
# "factor" "integer" "integer" 

【讨论】:

  • 不需要data[,cols] &lt;- lapply(data[,cols], factor)(列前导逗号)吗?
  • @Tgsmith61591- 两者都可以。带逗号的是矩阵型子集,不带逗号的是列表子集。数据框可以被任何一个子集,所以任何一种方式都可以工作。
  • 如何扩展此解决方案以包括因子水平和标签?
  • @Ben - 最好问一个新问题
  • 这是一个很好的解决方案,我现在用于更改列类的首选代码。但是,我认为使用 sapply 来查看课程很难看/难以阅读。 str(data) 对我来说效果更好。
【解决方案2】:

这是一个使用dplyr 的选项。 magrittr 中的 %&lt;&gt;% 运算符使用结果值更新 lhs 对象。

library(magrittr)
library(dplyr)
cols <- c("A", "C", "D", "H")

data %<>%
       mutate_each_(funs(factor(.)),cols)
str(data)
#'data.frame':  4 obs. of  10 variables:
# $ A: Factor w/ 4 levels "23","24","26",..: 1 2 3 4
# $ B: int  15 13 39 16
# $ C: Factor w/ 4 levels "3","5","18","37": 2 1 3 4
# $ D: Factor w/ 4 levels "2","6","28","38": 3 1 4 2
# $ E: int  14 4 22 20
# $ F: int  7 19 36 27
# $ G: int  35 40 21 10
# $ H: Factor w/ 4 levels "11","29","32",..: 1 4 3 2
# $ I: int  17 1 9 25
# $ J: int  12 30 8 33

或者,如果我们使用data.table,请使用for 循环和set

setDT(data)
for(j in cols){
  set(data, i=NULL, j=j, value=factor(data[[j]]))
}

或者我们可以在.SDcols 中指定'cols',然后将(:=)rhs 分配给'cols'

setDT(data)[, (cols):= lapply(.SD, factor), .SDcols=cols]

【讨论】:

    【解决方案3】:

    最近的tidyverse方式是使用mutate_at函数:

    library(tidyverse)
    library(magrittr)
    set.seed(88)
    
    data <- data.frame(matrix(sample(1:40), 4, 10, dimnames = list(1:4, LETTERS[1:10])))
    cols <- c("A", "C", "D", "H")
    
    data %<>% mutate_at(cols, factor)
    str(data)
     $ A: Factor w/ 4 levels "5","17","18",..: 2 1 4 3   
     $ B: int  36 35 2 26
     $ C: Factor w/ 4 levels "22","31","32",..: 1 2 4 3
     $ D: Factor w/ 4 levels "1","9","16","39": 3 4 1 2
     $ E: int  3 14 30 38
     $ F: int  27 15 28 37
     $ G: int  19 11 6 21
     $ H: Factor w/ 4 levels "7","12","20",..: 1 3 4 2
     $ I: int  23 24 13 8
     $ J: int  10 25 4 33
    

    【讨论】:

    • 如果只进行一次转换,你甚至不需要使用funsmutate_at(cols, factor) 就足够了。
    【解决方案4】:

    为了完整起见,关于this question asking about changing string columns only,还有mutate_if

    data <- cbind(stringVar = sample(c("foo","bar"),10,replace=TRUE),
                  data.frame(matrix(sample(1:40), 10, 10, dimnames = list(1:10, LETTERS[1:10]))),stringsAsFactors=FALSE)     
    
    factoredData = data %>% mutate_if(is.character,funs(factor(.)))
    

    【讨论】:

      【解决方案5】:

      如果您有另一个目标是从表中获取值然后使用它们进行转换,您可以尝试以下方式

      ### pre processing
      ind <- bigm.train[,lapply(.SD,is.character)]
      ind <- names(ind[,.SD[T]])
      ### Convert multiple columns to factor
      bigm.train[,(ind):=lapply(.SD,factor),.SDcols=ind]
      

      这会选择特定基于字符的列,然后将它们转换为因子。

      【讨论】:

        【解决方案6】:

        您可以使用mutate_if (dplyr):

        例如,将integer 强制转换为factor

        mydata=structure(list(a = 1:10, b = 1:10, c = c("a", "a", "b", "b", 
        "c", "c", "c", "c", "c", "c")), row.names = c(NA, -10L), class = c("tbl_df", 
        "tbl", "data.frame"))
        
        # A tibble: 10 x 3
               a     b c    
           <int> <int> <chr>
         1     1     1 a    
         2     2     2 a    
         3     3     3 b    
         4     4     4 b    
         5     5     5 c    
         6     6     6 c    
         7     7     7 c    
         8     8     8 c    
         9     9     9 c    
        10    10    10 c   
        

        使用函数:

        library(dplyr)
        
        mydata%>%
            mutate_if(is.integer,as.factor)
        
        # A tibble: 10 x 3
               a     b c    
           <fct> <fct> <chr>
         1     1     1 a    
         2     2     2 a    
         3     3     3 b    
         4     4     4 b    
         5     5     5 c    
         6     6     6 c    
         7     7     7 c    
         8     8     8 c    
         9     9     9 c    
        10    10    10 c    
        

        【讨论】:

          【解决方案7】:

          这是一个data.table 示例。我在此示例中使用了grep,因为这就是我经常通过对其名称使用部分匹配来选择许多列的方式。

          library(data.table)
          data <- data.table(matrix(sample(1:40), 4, 10, dimnames = list(1:4, LETTERS[1:10])))
          
          factorCols <- grep(pattern = "A|C|D|H", x = names(data), value = TRUE)
          
          data[, (factorCols) := lapply(.SD, as.factor), .SDcols = factorCols]
          

          【讨论】:

            【解决方案8】:

            这是使用 purrr 包中的 modify_at() 函数的另一种 tidyverse 方法。

            library(purrr)
            
            # Data frame with only integer columns
            data <- data.frame(matrix(sample(1:40), 4, 10, dimnames = list(1:4, LETTERS[1:10])))
            
            # Modify specified columns to a factor class
            data_with_factors <- data %>%
                purrr::modify_at(c("A", "C", "E"), factor)
            
            
            # Check the results:
            str(data_with_factors)
            # 'data.frame':   4 obs. of  10 variables:
            #  $ A: Factor w/ 4 levels "8","12","33",..: 1 3 4 2
            #  $ B: int  25 32 2 19
            #  $ C: Factor w/ 4 levels "5","15","35",..: 1 3 4 2
            #  $ D: int  11 7 27 6
            #  $ E: Factor w/ 4 levels "1","4","16","20": 2 3 1 4
            #  $ F: int  21 23 39 18
            #  $ G: int  31 14 38 26
            #  $ H: int  17 24 34 10
            #  $ I: int  13 28 30 29
            #  $ J: int  3 22 37 9
            

            【讨论】:

              【解决方案9】:

              似乎在 data.frame 上使用 SAPPLY 将变量立即转换为因子不起作用,因为它会生成矩阵/数组。我的做法是改用 LAPPLY,如下。

              ## let us create a data.frame here
              
              class <- c("7", "6", "5", "3")
              
              cash <- c(100, 200, 300, 150)
              
              height <- c(170, 180, 150, 165)
              
              people <- data.frame(class, cash, height)
              
              class(people) ## This is a dataframe 
              
              ## We now apply lapply to the data.frame as follows.
              
              bb <- lapply(people, as.factor) %>% data.frame() 
              
              ## The lapply part returns a list which we coerce back to a data.frame
              
              class(bb) ## A data.frame
              
              ##Now let us check the classes of the variables 
              
              class(bb$class)
              
              class(bb$height)
              
              class(bb$cash) ## as expected, are all factors. 
              
              

              【讨论】:

                【解决方案10】:

                一个简单且更新的解决方案

                data <- data %>%
                    mutate_at(cols, list(~factor(.)))
                

                【讨论】:

                  【解决方案11】:

                  截至 2021 年,当前的 tidyverse/dplyr 方法将是使用 across&lt;tidy-select&gt; 语句。

                  library(dplyr)
                  
                  data %>% mutate(across(*<tidy-select>*, *function*))
                  

                  across(&lt;tidy-select&gt;) 允许非常一致且轻松地选择要转换的列。 一些例子:

                  data %>% mutate(across(c(A, B, C, E), as.factor)) # select columns A to C, and E (by name)
                  
                  data %>% mutate(across(where(is.character), as.factor)) # select character columns
                  
                  data %>% mutate(across(1:5, as.factor)) # select first 5 columns (by index)
                  

                  【讨论】:

                  • 你能添加你的引文来解释为什么我们需要/应该使用'across'吗?我在 R4DS 或 ?dplyr 页面中看不到它
                  • dplyr.tidyverse.org/reference/across.html "across() 取代了 summarise_at()、summarise_if() 和 summarise_all() 等“范围变体”系列。”
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-07
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-09-02
                  • 2015-01-22
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多