【问题标题】:Convert type of multiple columns of a dataframe at once一次转换数据框多列的类型
【发布时间】:2011-12-02 14:48:19
【问题描述】:

我似乎花了很多时间从文件、数据库或其他东西创建数据框,然后将每一列转换为我想要的类型(数字、因子、字符等)。有没有办法一步一步做到这一点,可能是通过给出一个类型的向量?

foo<-data.frame(x=c(1:10), 
                y=c("red", "red", "red", "blue", "blue", 
                    "blue", "yellow", "yellow", "yellow", 
                    "green"),
                z=Sys.Date()+c(1:10))

foo$x<-as.character(foo$x)
foo$y<-as.character(foo$y)
foo$z<-as.numeric(foo$z)

而不是最后三个命令,我想做类似的事情

foo<-convert.magic(foo, c(character, character, numeric))

【问题讨论】:

  • 使用colClasses 参数到read.table
  • 值的范围也可以简单地使用:for(n in names(foo)[1:2]{foo[[n]]&lt;-as.character(foo[[n]])} 方便大量列的转换。
  • 了解到如果将多个字段从因子转换为数字,您将需要再次调用 as.characterlevels。见:stackoverflow.com/questions/3418128/…

标签: r type-conversion


【解决方案1】:

包里有一个简单的解决方案hablar

代码

library(hablar)
library(dplyr)
df <- data.frame(x = "1", y = "2", z = "4")

df %>% 
  convert(int(x, z),
          chr(y))

结果

# A tibble: 1 x 3
      x y         z
  <int> <chr> <int>
1     1 2         4

您可以简单地放置多个列名来转换多个列,例如zz 为整数,如上例所示。

【讨论】:

    【解决方案2】:

    使用purrrbase

    foo<-data.frame(x=c(1:10), 
                    y=c("red", "red", "red", "blue", "blue", 
                        "blue", "yellow", "yellow", "yellow", 
                        "green"),
                    z=Sys.Date()+c(1:10))
    types <- c("character", "character", "numeric")
    types<-paste0("as.",types)
    purrr::map2_df(foo,types,function(x,y) do.call(y,list(x)))
    # A tibble: 10 x 3
       x     y          z
       <chr> <chr>  <dbl>
     1 1     red    18127
     2 2     red    18128
     3 3     red    18129
     4 4     blue   18130
    

    【讨论】:

      【解决方案3】:

      type.convert(foo, as.is = TRUE) 类似,还有readr::type_convert 将数据帧转换为适当的类而不指定它们

      readr::type_convert(foo) 
      

      如果您将所有列都保留为字符,我们还可以使用readr::parse_guess,它会自动将数据框转换为正确的类。考虑这个修改后的数据框

      foo <- data.frame(x = as.character(1:10), 
                        y = c("red", "red", "red", "blue", "blue", "blue", "yellow", 
                           "yellow", "yellow", "green"),
                        z = as.character(Sys.Date()+c(1:10)), stringsAsFactors = FALSE)
      
      str(foo)
      
      #'data.frame':  10 obs. of  3 variables:
      # $ x: chr  "1" "2" "3" "4" ...
      # $ y: chr  "red" "red" "red" "blue" ...
      # $ z: chr  "2019-08-12" "2019-08-13" "2019-08-14" "2019-08-15" ...
      

      在每一列上应用parse_guess

      foo[] <- lapply(foo, readr::parse_guess)
      
      #'data.frame':  10 obs. of  3 variables:
      # $ x: num  1 2 3 4 5 6 7 8 9 10
      # $ y: chr  "red" "red" "red" "blue" ...
      # $ z: Date, format: "2019-08-12" "2019-08-13" "2019-08-14" "2019-08-15" ...
      

      【讨论】:

        【解决方案4】:

        我知道我很晚才回答,但是使用循环和属性函数是解决问题的简单方法。

        names <- c("x", "y", "z")
        chclass <- c("character", "character", "numeric")
        
        for (i in (1:length(names))) {
          attributes(foo[, names[i]])$class <- chclass[i]
        }
        

        【讨论】:

          【解决方案5】:

          如果您想自动检测列数据类型而不是手动指定它(例如在数据整理之后等),函数type.convert() 可能会有所帮助。

          函数type.convert() 接受一个字符向量并尝试确定所有元素的最佳类型(这意味着它必须每列应用一次)。

          df[] <- lapply(df, function(x) type.convert(as.character(x)))
          

          因为我喜欢dplyr,所以我更喜欢:

          library(dplyr)
          df <- df %>% mutate_all(funs(type.convert(as.character(.))))
          

          【讨论】:

          • 您的第一个选项应该是df[] &lt;- lapply(df, function(x) type.convert(as.character(x))。我会放弃 apply 选项,因为它通常用于生成矩阵或数组。您在第三个选项中拼错了dplyr。最后,这并不是对 OP 问题的真正回答,而是对相关问题的回答。
          • 感谢格式化提示。我花了很长时间才找到像 type.convert 这样的功能,所以我认为把它放在一个更频繁出现的类似问题上会帮助像我这样的人在路上。
          • 还不错,虽然值得看看this question
          【解决方案6】:

          你似乎描述了变换:

          foo <- transform(foo, x=as.character(x), y=as.character(y), z=as.numeric(z))
          

          【讨论】:

            【解决方案7】:

            一个稍微简单的 data.table 解决方案,但如果您要更改为许多不同的列类型,则需要几个步骤。

            dt <- data.table( x=c(1:10), y=c(10:20), z=c(10:20), name=letters[1:10])
            
            dt <- dt[, lapply(.SD, as.numeric), by= name]
            

            这会将除by 中指定的列之外的所有列更改为数字(或您在lapply 中设置的任何内容)

            【讨论】:

              【解决方案8】:

              除了@joran 的回答,convert.magic 不会在因子到数字的转换中保留数值:

              convert.magic <- function(obj,types){
                  out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
                  character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
                  names(out) <- colnames(obj)
                  as.data.frame(out,stringsAsFactors = FALSE)
              }
              
              foo<-data.frame(x=c(1:10), 
                                  y=c("red", "red", "red", "blue", "blue", 
                                      "blue", "yellow", "yellow", "yellow", 
                                      "green"),
                                  z=Sys.Date()+c(1:10))
              
              foo$x<-as.character(foo$x)
              foo$y<-as.character(foo$y)
              foo$z<-as.numeric(foo$z)
              
              str(foo)
              # 'data.frame': 10 obs. of  3 variables:
              # $ x: chr  "1" "2" "3" "4" ...
              # $ y: chr  "red" "red" "red" "blue" ...
              # $ z: num  16777 16778 16779 16780 16781 ...
              
              foo.factors <- convert.magic(foo, rep("factor", 3))
              
              str(foo.factors) # all factors
              
              foo.numeric.not.preserved <- convert.magic(foo.factors, c("numeric", "character", "numeric"))
              
              str(foo.numeric.not.preserved)
              # 'data.frame': 10 obs. of  3 variables:
              # $ x: num  1 3 4 5 6 7 8 9 10 2
              # $ y: chr  "red" "red" "red" "blue" ...
              # $ z: num  1 2 3 4 5 6 7 8 9 10
              
              # z comes out as 1 2 3...
              

              以下应保留数值:

              ## as.numeric function that preserves numeric values when converting factor to numeric
              
              as.numeric.mod <- function(x) {
                  if(is.factor(x))
                    as.numeric(levels(x))[x]
                else
                    as.numeric(x)
              }
              
              ## The same than in @joran's answer, except for as.numeric.mod
              convert.magic <- function(obj,types){
                  out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],
                  character = as.character,numeric = as.numeric.mod, factor = as.factor); FUN1(obj[,i])})
                  names(out) <- colnames(obj)
                  as.data.frame(out,stringsAsFactors = FALSE)
              }
              
              foo.numeric <- convert.magic(foo.factors, c("numeric", "character", "numeric"))
              
              str(foo.numeric)
              # 'data.frame': 10 obs. of  3 variables:
              # $ x: num  1 2 3 4 5 6 7 8 9 10
              # $ y: chr  "red" "red" "red" "blue" ...
              # $ z: num  16777 16778 16779 16780 16781 ...
              
              # z comes out with the correct numeric values
              

              【讨论】:

                【解决方案9】:

                编辑请参阅this 相关问题,了解有关此基本思想的一些简化和扩展。

                我使用switch 对布兰登的回答发表评论:

                convert.magic <- function(obj,types){
                    for (i in 1:length(obj)){
                        FUN <- switch(types[i],character = as.character, 
                                                   numeric = as.numeric, 
                                                   factor = as.factor)
                        obj[,i] <- FUN(obj[,i])
                    }
                    obj
                }
                
                out <- convert.magic(foo,c('character','character','numeric'))
                > str(out)
                'data.frame':   10 obs. of  3 variables:
                 $ x: chr  "1" "2" "3" "4" ...
                 $ y: chr  "red" "red" "red" "blue" ...
                 $ z: num  15254 15255 15256 15257 15258 ...
                

                对于真正的大数据框,您可能希望使用lapply 而不是for 循环:

                convert.magic1 <- function(obj,types){
                    out <- lapply(1:length(obj),FUN = function(i){FUN1 <- switch(types[i],character = as.character,numeric = as.numeric,factor = as.factor); FUN1(obj[,i])})
                    names(out) <- colnames(obj)
                    as.data.frame(out,stringsAsFactors = FALSE)
                }
                

                执行此操作时,请注意 R 中强制数据的一些复杂性。例如,从因子转换为数字通常涉及as.numeric(as.character(...))。另外,请注意 data.frame()as.data.frame()s 将字符转换为因子的默认行为。

                【讨论】:

                • +1 为后代,虽然我不明白有什么区别。
                • +1 推荐lapply。过去我一直在努力优化这类问题,结果发现[&lt;- 操作相当慢。
                • 此函数是否将数字因子转换为数字(即 3.6 = 3.6,而不是因子订单号)?如何将其合并到功能中?我试过as.numeric(as.character),还是不行。
                • @MatthewDowle:介意发布 data.table 解决方案吗?还没有做太多事情,所以这对我来说不一定是一件容易的事。不过听起来很有趣。
                • @MattBannert 你好。在this answer 的最后一次编辑中循环set 是我这样做的方式。将 - 替换为对 as(...) 的调用或类似的调用。
                【解决方案10】:

                我刚刚使用 RSQLite fetch 方法遇到了类似的情况……结果以原子数据类型返回。就我而言,这是一个日期时间戳让我感到沮丧。 我发现setAs 函数对于帮助使as 按预期工作非常有用。这是我的小例子。

                ##data.frame conversion function
                convert.magic2 <- function(df,classes){
                  out <- lapply(1:length(classes),
                                FUN = function(classIndex){as(df[,classIndex],classes[classIndex])})
                  names(out) <- colnames(df)
                  return(data.frame(out))
                }
                
                ##small example case
                tmp.df <- data.frame('dt'=c("2013-09-02 09:35:06", "2013-09-02 09:38:24", "2013-09-02 09:38:42", "2013-09-02 09:38:42"),
                                     'v'=c('1','2','3','4'),
                                     stringsAsFactors=FALSE)
                classes=c('POSIXct','numeric')
                str(tmp.df)
                #confirm that it has character datatype columns
                ##  'data.frame':  4 obs. of  2 variables:
                ##    $ dt: chr  "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
                ##    $ v : chr  "1" "2" "3" "4"
                
                ##is the dt column coerceable to POSIXct?
                canCoerce(tmp.df$dt,"POSIXct")
                ##  [1] FALSE
                
                ##and the conver.magic2 function fails also:
                tmp.df.n <- convert.magic2(tmp.df,classes)
                
                ##  Error in as(df[, classIndex], classes[classIndex]) : 
                ##    no method or default for coercing “character” to “POSIXct” 
                
                ##ittle reading reveals the setAS function
                setAs('character', 'POSIXct', function(from){return(as.POSIXct(from))})
                
                ##better answer for canCoerce
                canCoerce(tmp.df$dt,"POSIXct")
                ##  [1] TRUE
                
                ##better answer from conver.magic2
                tmp.df.n <- convert.magic2(tmp.df,classes)
                
                ##column datatypes converted as I would like them!
                str(tmp.df.n)
                
                ##  'data.frame':  4 obs. of  2 variables:
                ##    $ dt: POSIXct, format: "2013-09-02 09:35:06" "2013-09-02 09:38:24" "2013-09-02 09:38:42" "2013-09-02 09:38:42"
                ##   $ v : num  1 2 3 4
                

                【讨论】:

                  【解决方案11】:

                  我发现我也经常遇到这种情况。这是关于如何导入数据。所有 read...() 函数都有某种类型的选项来指定不将字符串转换为因子。这意味着文本字符串将保留字符,看起来像数字的东西将保留为数字。当您的元素为空而不是 NA 时,就会出现问题。但同样, na.strings = c("",...) 也应该解决这个问题。我首先会仔细检查您的导入过程并进行相应的调整。

                  但是你总是可以创建一个函数并推送这个字符串。

                  convert.magic <- function(x, y=NA) {
                  for(i in 1:length(y)) { 
                  if (y[i] == "numeric") { 
                  x[i] <- as.numeric(x[[i]])
                  }
                  if (y[i] == "character")
                  x[i] <- as.character(x[[i]])
                  }
                  return(x)
                  }
                  
                  foo <- convert.magic(foo, c("character", "character", "numeric"))
                  
                  > str(foo)
                  'data.frame':   10 obs. of  3 variables:
                   $ x: chr  "1" "2" "3" "4" ...
                   $ y: chr  "red" "red" "red" "blue" ...
                   $ z: num  15254 15255 15256 15257 15258 ...
                  

                  【讨论】:

                  • 尝试将if 语句替换为对switch 的调用,这实际上可以返回适当的函数:switch(expr,character = as.character, numeric = as.numeric,...)
                  • 嗯,把它写成答案,这样你就可以获得奖励积分:) 我只是快速整理了一些东西。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-08-20
                  • 2022-01-02
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多