【问题标题】:read_csv use col_double() instead for all numeric columnsread_csv 对所有数字列使用 col_double()
【发布时间】:2019-07-08 20:07:51
【问题描述】:

我想使用readr::read_csv 而不是read.csv,因为它的速度和自动转换日期。然而,它如何处理主要是整数的数字存在一个问题,其中有一些浮点数。

有没有办法强制它对所有数字使用col_double,同时仍然对所有其他列使用col_guess

在我看来,猜测 col_integer 似乎是包开发人员的次优选择。对我来说,真实数据似乎经常发生这种情况。例如,当非零很少见时。

我在不知道列类型或名称的情况下打开文件。

以下是该问题的说明:

df<-data.frame(
    i=as.integer(c(1:5)),
    d=seq.Date(as.Date('2019-01-01'), length.out = 5, by=1),
    mix = c('1','2','3.1','4','5'),
    stringsAsFactors = F
)%>%as.tbl
write_csv(df, '~/temp.csv')


这很好! 3.1 值被正确读入。

read_csv('~/temp.csv')
# A tibble: 5 x 3
      i d            mix
  <int> <date>     <dbl>
1     1 2019-01-01   1  
2     2 2019-01-02   2  
3     3 2019-01-03   3.1
4     4 2019-01-04   4  
5     5 2019-01-05   5 


50k 行数据帧,后面的行有小数位值。

df_large <-data.frame(
    i = as.integer(c(1:(1e4))),
    d=seq.Date(as.Date('2019-01-01'), length.out = 1e4, by=1),
    mix = as.character(c(1:(1e4))),
    stringsAsFactors = F
)%>%as.tbl
bind_rows(df_large, df)%>%tail(7)
# A tibble: 7 x 3
      i d          mix  
  <int> <date>     <chr>
1  9999 2046-05-17 9999 
2 10000 2046-05-18 10000
3     1 2019-01-01 1    
4     2 2019-01-02 2    
5     3 2019-01-03 3.1  
6     4 2019-01-04 4    
7     5 2019-01-05 5   


这很糟糕! 3.1 现在不适用了。

bind_rows(df_large, df)%>%write_csv(., '~/temp.csv')
read_csv('~/temp.csv')%>%tail(7)
# A tibble: 7 x 3
      i d            mix
  <int> <date>     <int>
1  9999 2046-05-17  9999
2 10000 2046-05-18 10000
3     1 2019-01-01     1
4     2 2019-01-02     2
5     3 2019-01-03    NA
6     4 2019-01-04     4
7     5 2019-01-05     5


这可行,但我如何提前设置 guess_max

read_csv('~/temp.csv', guess_max = 1e5)%>%as.tbl%>%tail(7)
# A tibble: 7 x 3
      i d              mix
  <int> <date>       <dbl>
1  9999 2046-05-17  9999  
2 10000 2046-05-18 10000  
3     1 2019-01-01     1  
4     2 2019-01-02     2  
5     3 2019-01-03     3.1
6     4 2019-01-04     4  
7     5 2019-01-05     5  


随着 guess_max 的增长,运行时间也会增长。这似乎是过采样。

system.time(read_csv('~/temp.csv', guess_max = 1e5)%>%as.tbl%>%tail(7))
   user  system elapsed 
  0.020   0.001   0.022 
system.time(read_csv('~/temp.csv', guess_max = 1e7)%>%as.tbl%>%tail(7))
   user  system elapsed 
  0.321   0.010   0.332 
system.time(read_csv('~/temp.csv', guess_max = 1e9)%>%as.tbl%>%tail(7))
   user  system elapsed 
 34.138   5.848  39.821 


这行得通,但可以有超过 30 列,而且我不会提前输入。

read_csv('~/temp.csv', col_types = 'dDd')%>%as.tbl%>%tail(7)


data.table::fread 速度很快,可以很好地处理数字,但不能转换日期。

data.table::fread('~/temp.csv')%>%as.tbl%>%tail(7)
# A tibble: 7 x 3
      i d              mix
  <int> <chr>        <dbl>
1  9999 2046-05-17  9999  
2 10000 2046-05-18 10000  
3     1 2019-01-01     1  
4     2 2019-01-02     2  
5     3 2019-01-03     3.1
6     4 2019-01-04     4  
7     5 2019-01-05     5 

【问题讨论】:

    标签: r tidyverse readr


    【解决方案1】:

    您可以做的一件事是读取 CSV 的第一行(或前 n 行),找出哪些列被解析为整数,并将它们作为参数传递给 cols

    library(readr)
    
    read_csv_dbl <- function(file, ...){
      types <- sapply(suppressMessages(read_csv(file, n_max = 1)), class) 
      int_cols <- names(types[types == "integer"])
      args <- structure(replicate(length(int_cols), col_double()), names = int_cols)
      read_csv(file, col_types = do.call(cols, args), ...)
    }
    
    read_csv_dbl("~/temp.csv") %>% tail(7)
    # A tibble: 7 x 3
    #      i d              mix
    #  <dbl> <date>       <dbl>
    #1  9999 2046-05-17  9999  
    #2 10000 2046-05-18 10000  
    #3     1 2019-01-01     1  
    #4     2 2019-01-02     2  
    #5     3 2019-01-03     3.1
    #6     4 2019-01-04     4  
    #7     5 2019-01-05     5  
    

    这个方法也比改guess_max快很多:

    system.time(read_csv_dbl("~/temp.csv"))
    #   user  system elapsed 
    #   0.02    0.00    0.01 
    

    【讨论】:

      【解决方案2】:

      您可以尝试使用 hablar 中的 retype 进行数据类型解析,并使用 fread 进行快速阅读。

      library(hablar)
      data.table::fread('~/temp.csv') %>%
        retype() %>% 
        tail(7)
      

      这给了我:

      # A tibble: 7 x 3
            i d              mix
        <int> <date>       <dbl>
      1  9999 2046-05-17  9999  
      2 10000 2046-05-18 10000  
      3     1 2019-01-01     1  
      4     2 2019-01-02     2  
      5     3 2019-01-03     3.1
      6     4 2019-01-04     4  
      7     5 2019-01-05     5  
      

      【讨论】:

        猜你喜欢
        • 2015-08-10
        • 1970-01-01
        • 2019-09-05
        • 2019-11-30
        • 2016-09-27
        • 1970-01-01
        • 2018-09-16
        • 2019-09-02
        • 2019-01-30
        相关资源
        最近更新 更多