如何告诉readr :: read_csv正确猜测双列

时间:2018-10-22 17:09:52

标签: r tidyverse readr

我的径流数据有很多零值,偶尔还有一些非零的双精度值。

'readr :: read_csv'由于存在多个零而猜测整数列类型。

如何使read_csv猜测正确的双列类型? 我事先不知道变量名的映射,因此无法给出名称类型的映射。

这是一个小例子

  # create a column of doubles with many zeros (runoff data)
  #dsTmp <- data.frame(x = c(rep(0.0, 2), 0.5)) # this works
  dsTmp <- data.frame(x = c(rep(0.0, 1e5), 0.5))
  write_csv(dsTmp, "tmp/dsTmp.csv")
  # 0.0 is written as 0 
  # read_csv now guesses integer instead of double and reports 
  # a parsing failure. 
  ans <- read_csv("tmp/dsTmp.csv")
  # the last value is NA instead of 0.5
  tail(ans)

我可以告诉它选择尝试更宽的列类型,而不发出解析失败吗?

问题645提到了此问题,但此处给出的解决方法在写作方面。我对写作方面影响不大。

3 个答案:

答案 0 :(得分:1)

这是两种技术。 (底部的数据准备。$hp$vs以及后面的是整数列。)

注意:我在大多数首次调用中都添加了cols(.default=col_guess()),这样我们就不会收到read_csv发现该列是什么的大消息。可以省略它,但会带来更多嘈杂的控制台。

  1. 使用cols(.default=...)设置将所有列都强制为double,只要您知道文件中没有非数字,就可以安全地工作:

    read_csv("mtcars.csv", col_types = cols(.default = col_double()))
    # Warning in rbind(names(probs), probs_f) :
    #   number of columns of result is not a multiple of vector length (arg 1)
    # Warning: 32 parsing failures.
    ### ...snip...
    # See problems(...) for more details.
    # # A tibble: 32 x 11
    #      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
    #    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #  1  21      NA  160    110  3.9   2.62  16.5     0     1     4     4
    #  2  21      NA  160    110  3.9   2.88  17.0     0     1     4     4
    #  3  22.8    NA  108     93  3.85  2.32  18.6     1     1     4     1
    #  4  21.4    NA  258    110  3.08  3.22  19.4     1     0     3     1
    #  5  18.7    NA  360    175  3.15  3.44  17.0     0     0     3     2
    #  6  18.1    NA  225    105  2.76  3.46  20.2     1     0     3     1
    #  7  14.3    NA  360    245  3.21  3.57  15.8     0     0     3     4
    #  8  24.4    NA  147.    62  3.69  3.19  20       1     0     4     2
    #  9  22.8    NA  141.    95  3.92  3.15  22.9     1     0     4     2
    # 10  19.2    NA  168.   123  3.92  3.44  18.3     1     0     4     4
    # # ... with 22 more rows
    
  2. 仅更改<int>col_integer())列,请多加注意。我对n_max=50的使用需要保持平衡。与guess_max=类似,多一点更好。在这种情况下,如果我选择n_max=1,则前mpg个值对将建议使用整数,这很好。但是,如果您的其他字段与其他类有歧义,则需要更多。由于您是在谈论不希望读入整个文件,而是愿意读一点以得到正确的猜测,因此我认为您可以在此处使用一个合理的值(100s?1000s?)。对于chrlgl来说很健壮。

    types <- attr(read_csv("mtcars.csv", n_max=1, col_types = cols(.default = col_guess())), "spec")
    (intcols <- sapply(types$cols, identical, col_integer()))
    #   mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb 
    #  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE 
    types$cols[intcols] <- replicate(sum(intcols), col_double())
    

    和最后的读物,请注意$hp及其后的内容现在是<dbl>(与下面的数据准备读操作不同)。

    read_csv("mtcars.csv", col_types = types)
    # # A tibble: 32 x 11
    #      mpg cyl    disp    hp  drat    wt  qsec    vs    am  gear  carb
    #    <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #  1  21   c6     160    110  3.9   2.62  16.5     0     1     4     4
    #  2  21   c6     160    110  3.9   2.88  17.0     0     1     4     4
    #  3  22.8 c4     108     93  3.85  2.32  18.6     1     1     4     1
    #  4  21.4 c6     258    110  3.08  3.22  19.4     1     0     3     1
    #  5  18.7 c8     360    175  3.15  3.44  17.0     0     0     3     2
    #  6  18.1 c6     225    105  2.76  3.46  20.2     1     0     3     1
    #  7  14.3 c8     360    245  3.21  3.57  15.8     0     0     3     4
    #  8  24.4 c4     147.    62  3.69  3.19  20       1     0     4     2
    #  9  22.8 c4     141.    95  3.92  3.15  22.9     1     0     4     2
    # 10  19.2 c6     168.   123  3.92  3.44  18.3     1     0     4     4
    # # ... with 22 more rows
    

数据:

library(readr)
mt <- mtcars
mt$cyl <- paste0("c", mt$cyl) # for fun
write_csv(mt, path = "mtcars.csv")
read_csv("mtcars.csv", col_types = cols(.default = col_guess()))
# # A tibble: 32 x 11
#      mpg cyl    disp    hp  drat    wt  qsec    vs    am  gear  carb
#    <dbl> <chr> <dbl> <int> <dbl> <dbl> <dbl> <int> <int> <int> <int>
#  1  21   c6     160    110  3.9   2.62  16.5     0     1     4     4
#  2  21   c6     160    110  3.9   2.88  17.0     0     1     4     4
#  3  22.8 c4     108     93  3.85  2.32  18.6     1     1     4     1
#  4  21.4 c6     258    110  3.08  3.22  19.4     1     0     3     1
#  5  18.7 c8     360    175  3.15  3.44  17.0     0     0     3     2
#  6  18.1 c6     225    105  2.76  3.46  20.2     1     0     3     1
#  7  14.3 c8     360    245  3.21  3.57  15.8     0     0     3     4
#  8  24.4 c4     147.    62  3.69  3.19  20       1     0     4     2
#  9  22.8 c4     141.    95  3.92  3.15  22.9     1     0     4     2
# 10  19.2 c6     168.   123  3.92  3.44  18.3     1     0     4     4
# # ... with 22 more rows

答案 1 :(得分:0)

data.table::fread似乎可以很好地工作。

write_csv(dsTmp, ttfile <- tempfile())
ans <- fread(ttfile)
tail(ans)
#      x
# 1: 0.0
# 2: 0.0
# 3: 0.0
# 4: 0.0
# 5: 0.0
# 6: 0.5

?fread帮助页面

  

在很少的情况下,文件可能会在   样本(称为样本外类型异常)。在这种情况下   fread将自动从头开始重新读取那些列   这样您就不必设置colClasses带来任何不便   你自己;

答案 2 :(得分:0)

我将r2evans解决方案的代码转移给一个小函数:

read_csvDouble <- function(
  ### read_csv but read guessed integer columns as double
  ... ##<< further arguments to \code{\link{read_csv}}
  , n_max = Inf        ##<< see \code{\link{read_csv}}
  , col_types = cols(.default = col_guess()) ##<< see \code{\link{read_csv}}
  ## the default suppresses the type guessing messages
){
  ##details<< Sometimes, double columns are guessed as integer,  e.g. with
  ## runoff data where there are many zeros, an only occasionally 
  ## positive values that can be recognized as double.
  ## This functions modifies \code{read_csv} by changing guessed integer 
  ## columns to double columns.
  #https://stackoverflow.com/questions/52934467/how-to-tell-readrread-csv-to-guess-double-column-correctly
  colTypes <- read_csv(..., n_max = 3, col_types = col_types) %>% attr("spec")
  isIntCol <- map_lgl(colTypes$cols, identical, col_integer())
  colTypes$cols[isIntCol] <- replicate(sum(isIntCol), col_double())
  ##value<< tibble as returned by \code{\link{read_csv}}
  ans <- read_csv(..., n_max = n_max, col_types = colTypes)
  ans
}