防止fread()函数中的列类型推断

16
有没有一种方法可以让fread模仿read.table的行为,通过读取数据来设置变量的类型。
我有一些数字数据,主要数据下面有一些注释。当我使用fread读取数据时,列被转换为字符型。然而,通过在read.table中设置nrow,我可以阻止这种行为。在fread中是否也可能实现这一点。(我不想修改原始数据或制作修订副本)。谢谢
以下是一个例子
d <- data.frame(x=c(1:100, NA, NA, "fff"), y=c(1:100, NA,NA,NA)) 
write.csv(d, "test.csv",  row.names=F)

in_d <- read.csv("test.csv", nrow=100, header=T)
in_dt <- data.table::fread("test.csv", nrow=100)

这会产生

> str(in_d)
'data.frame':   100 obs. of  2 variables:
 $ x: int  1 2 3 4 5 6 7 8 9 10 ...
 $ y: int  1 2 3 4 5 6 7 8 9 10 ...
> str(in_dt)
Classes ‘data.table’ and 'data.frame':  100 obs. of  2 variables:
 $ x: chr  "1" "2" "3" "4" ...
 $ y: int  1 2 3 4 5 6 7 8 9 10 ...
 - attr(*, ".internal.selfref")=<externalptr>

作为一种解决方法,我想使用read.table读取一行数据,获取类别并设置colClasses,但是我的理解有误。

cl <- read.csv("test.csv", nrow=1,  header=T)
cols <- unname(sapply(cl, class))
in_dt <- data.table::fread("test.csv", nrow=100, colClasses=cols)
str(in_dt)

使用 Windows 8.1 操作系统,R 版本为 3.1.2(发布于2014年10月31日),平台为 x86_64-w64-mingw32/x64(64位)。


3
听起来是一个合理的计划,但是当我实际阅读帮助页面时:“如果colClasses请求,fread只会将列提升到更高的类型。它不会将列降级为较低的类型,因为这会导致缺失值。如果您真的需要数据丢失,您必须自己强制转换这些列。”即使将读取限制为5行也会失败。我记得colClasses机制是最近添加的,所以也许你应该提交一个功能请求。Matthew和Arun经常很包容。 - IRTFM
2
肯定有一种DT策略可以强制所有列转换为数字。将.SDcols设置为适当的向量,然后像这样:DT[, .SD := lapply(.SDcols, as.numeric), .SDcols=vec]。我不是DT用户,但我相信一定有某种最小输入法的方法,并且我怀疑你可以在SO答案中找到它的示例。 - IRTFM
@BondedDust; 我也不是DT的用户,只是read.table对我的数据比fread有更严重的问题。我会在SO上看看。谢谢。 - user2957945
看,停止反复修改你的标题。这不是关于“使用nrows”的问题。将字符串写入整数列是有意写入有缺陷的csv文件。你的问题是“我能否防止data.table的fread列类推断被数据中尾随的字符串注释行覆盖?”你知道注释行应该以注释字符(如#)开头。当我们将其添加到您的注释文本中时,使用read.csv可以得到正确的行为(但不是fread)。是的,fread可能需要改进。同时需要一个解决方法。 - smci
感谢您的输入。将字符串写入整数列是有意将有缺陷的 CSV 写出。这是一个 MWE,反映了我拥有的一些数据,因此它没有被注释。如果我在文件中添加 #,它将不反映我拥有的数据。顺便说一句,我没有回退。 - user2957945
好的。有人能否展示(并附上引用),证明这不是对CSV的滥用?仅仅因为read.csv(nrow)接受它并正确推断类型。 - smci
2个回答

18

选项1:使用系统命令

fread()允许在其第一个参数中使用系统命令。我们可以使用它来删除文件的第一列中的引号。

indt <- data.table::fread("cat test.csv | tr -d '\"'", nrows = 100)
str(indt)
# Classes ‘data.table’ and 'data.frame':    100 obs. of  2 variables:
#  $ x: int  1 2 3 4 5 6 7 8 9 10 ...
#  $ y: int  1 2 3 4 5 6 7 8 9 10 ...
#  - attr(*, ".internal.selfref")=<externalptr> 

系统命令cat test.csv | tr -d '\"'的解释:

  • cat test.csv将文件读取到标准输出
  • |是一个管道,使用上一个命令的输出作为下一个命令的输入
  • tr -d '\"'从当前输入中删除(-d)所有双引号('\"')的出现

选项2:读取后强制转换

由于在您的系统上选项1似乎不起作用,另一个可能性是像之前一样读取文件,但使用type.convert()转换x列。

library(data.table)
indt2 <- fread("test.csv", nrows = 100)[, x := type.convert(x)]
str(indt2)
# Classes ‘data.table’ and 'data.frame':    100 obs. of  2 variables:
#  $ x: int  1 2 3 4 5 6 7 8 9 10 ...
#  $ y: int  1 2 3 4 5 6 7 8 9 10 ...
#  - attr(*, ".internal.selfref")=<externalptr> 

顺便提一下:我通常更喜欢使用type.convert()而不是as.numeric(),以避免在某些情况下触发的“由强制转换引入的NAs”警告。例如,

x <- c("1", "4", "NA", "6")
as.numeric(x)
# [1]  1  4 NA  6
# Warning message:
# NAs introduced by coercion 
type.convert(x)
# [1]  1  4 NA  6

当然,您也可以使用as.numeric()


注意:此答案假定data.table dev v1.9.5


谢谢Richard,但是这在我的系统上会引发错误。我正在使用Windows 8.1。 - user2957945
感谢提供额外信息。我已经安装了开发版本,但在Windows上出现了相同的错误(在Linux上可以工作,并提供有用的警告)。您能否解释一下系统调用命令? - user2957945
1
@user2957945 - 好的,知道了。我添加了第二个选项,应该足够高效。 - Rich Scriven
哎呀...可能我在使用系统调用时说得太早了。实际上,自从开发版本以来,fread 似乎根本不起作用。我会从 CRAN 加载并查看是否可以解决问题。再次感谢您的帮助。 - user2957945
@user2957945 - 当你重新执行它时,请使用一个新的R会话以避免与其他对象发生冲突。 - Rich Scriven
嗯,有趣。当我在我的系统上升级到开发版本时,fread停止工作并显示错误Error in fread("test.csv") : showProgress must be 0 or 1, currently。降级后它又可以正常工作了。你的第二个解决方案可行(总是在新会话中使用 ;))。 - user2957945

-2

好的,客户正在滥用CSV格式,故意将尾随字符串行写入整数列,但这些行没有以注释字符(#)开头。

然后你希望通过使用nrow来限制只读取整数行,从而覆盖fread()的类型推断。 read.csv(..., nrow)会接受这个参数,但是fread()总是使用所有行进行类型推断(不仅仅是由nrow, skip, header指定的行),即使它们以comment.char开头(这是一个错误)。

  1. 听起来像是滥用 CSV。你的注释行应该以 # 开头。
  2. 是的,fread() 需要修复/增强以忽略类型推断的注释行。
  3. 目前,您可以通过后处理数据表读取来解决 fread() 的问题。
  4. 有争议的是,是否应更改 fread() 以支持您想要的行为:使用 nrows 来限制暴露给类型推断的内容。这可能会解决您(相当独特的)情况并破坏其他一些情况。

我不明白为什么您(编辑:客户)不能将您的注释写入单独的 .txt/README/数据字典文件以配合 .csv 使用。使用单独的数据字典文件的做法已经非常成熟。 我从未见过有人这样做 CSV 文件。至少将注释移到标题中,而不是页脚。


正如@RichardScriven所说,这只是我的数据的一个小例子,并没有额外的标题或带注释字符的单元格。它在底部有附加信息,可以改变我的变量类别。通常我们将数据存储在不可写的目录中(虽然当然不可能获得权限),因此我更愿意不修改并保存新文件。 - user2957945
3
关于您的更新:1)数据是给我的,所以没有注释。3)是的,在底部添加额外信息并不有帮助,但我经常收到这样的数据,客户会在主要数据下方添加小概述或文本(不幸的是,这对我来说并不是一个独特的情况)。 - user2957945
4
我认为你的论点有些超出问题的范畴了。我们能不能先约定这是一种糟糕的数据格式,然后问问原帖作者应该如何处理它呢?(我知道这里有一个“归谬法”,比如说,“我的客户把数据嵌入到一个.mp4文件中作为屏幕截图:我该如何使用fread来读取它?”但这种情况似乎远非如此。) - Ben Bolker
PS https://tools.ietf.org/html/rfc4180 对于注释行/元数据没有任何说明(除了标题行)。 - Ben Bolker
如果您经常收到这样的文件,那么写一个脚本自动提取任何尾部注释行(特别是当任何字段为数字时),并将它们移动到单独的.comment文件怎么样? - smci
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接