【问题标题】:How to tell readr::read_csv to guess double column correctly如何告诉 readr::read_csv 正确猜测双列
【发布时间】:2019-03-26 19:56:30
【问题描述】:

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

'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)

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

Issue 645 提到了这个问题,但那里给出的解决方法是在写作方面。我对写作方面影响不大。

【问题讨论】:

  • 您可以尝试增加guess_max 参数,以便在猜测之前进一步查看文件以查找值。
  • 可以试试data.table::fread()吗?
  • 有什么理由不能选择read.csv()
  • @12b345b6b78 base R 的 read.csv 很慢

标签: r tidyverse readr


【解决方案1】:

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

注意:我将cols(.default=col_guess()) 添加到大多数首次调用中,这样我们就不会收到read_csv 找到的列的大消息。可以以更嘈杂的控制台为代价省略它。

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

    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. 仅更改 &lt;int&gt; (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 及以上现在是&lt;dbl&gt;(与下面的数据准备读取不同)。

    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

【讨论】:

  • 谢谢,r2evans。您的解决方案 2 解决了我的问题。我将您的代码转换为一个小函数:
【解决方案2】:

我把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
}

【讨论】:

    【解决方案3】:

    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 的不便 你自己;

    【讨论】:

    • data.table::fread 确实很好用。但是我宁愿不添加更多的包依赖,而且read_csv已经在项目的很多地方使用了。
    猜你喜欢
    • 1970-01-01
    • 2021-08-11
    • 2023-02-15
    • 2018-06-29
    • 1970-01-01
    • 2015-06-28
    • 2019-09-05
    • 2021-10-05
    • 1970-01-01
    相关资源
    最近更新 更多