data.table::fread函数的stringsAsFactors=TRUE参数不能将字符列转换为因子类型 - 有什么解决方法?

9

我知道这个问题已经在多个地方提出,我已经尝试了数小时来寻找一个可能的好解决方案,但都失败了。这就是为什么我要问这个问题。

所以,我有一个巨大的数据文件(~5GB),我使用fread()来读取它。

library(data.table)
df<- fread('output.txt', sep = "|", stringsAsFactors = TRUE)
head(df, 5)
       age            income homeowner_status_desc marital_status_cd gender
1:         $35,000 - $49,999                                               
2: 35 - 44 $35,000 - $49,999                  Rent            Single      F
3:         $35,000 - $49,999                                               
4:                                                                         
5:         $50,000 - $74,999 
str(df)
Classes ‘data.table’ and 'data.frame':  999 obs. of  5 variables:
 $ age                  : chr  "" "35 - 44" "" "" ...
 $ income               : chr  "$35,000 - $49,999" "$35,000 - $49,999" "$35,000 - $49,999" "" ...
 $ homeowner_status_desc: chr  "" "Rent" "" "" ...
 $ marital_status_cd    : chr  "" "Single" "" "" ...
 $ gender               : chr  "" "F" "" "" ...
 - attr(*, ".internal.selfref")=<externalptr> 

数据存在缺失值(即空白处)。在原始数据中,有很多列,因此我需要找到一种方法,使得包含字符串的列能够变成因子。有没有人能建议一下最佳实践来完成这个任务?我考虑将其转换为数据框并执行此操作。但是,在它仍是数据表的情况下是否可能实现呢?


2
据我所知,data.table默认不会将任何内容存储为因子,以减少存储量。您需要自己将所有内容更改为因子。我有一行代码可以帮助我完成这个过程:df[,(names(df)):=lapply(.SD, as.factor),.SDcols=names(df)]。编辑:如果您只想要字符列,请使用以下代码:types <- data.frame(sapply(df, class)); char_list <- row.names(types)[types[[1]] == 'character'],然后将names(df)替换为char_list - Michal
@Michal 但是因子占用更少的内存来存储。 - rawr
你尝试在freadcolClasses参数中必要的地方指定"factor"了吗? - MichaelChirico
@MichaelChirico,对于我的情况来说,那不是最好的方法,因为在原始数据中我有约70列。 - hmi2015
1
我创建了一个小的csv文件,我可以确认相同的行为,其中stringsAsFactors=TRUE不会导致因子列。此外,指定colClasses为因子似乎也不起作用。 - Dean MacGregor
显示剩余3条评论
4个回答

11

刚刚在版本1.9.6+中为实现了stringsAsFactors参数

NEWS中得知:

  1. fread()实现了stringsAsFactors参数。当TRUE时,字符列被转换为因子。默认值为FALSE。感谢Artem Klevtsov的反馈#501,以及@hmi2015的这篇SO帖子

0
这基本上是一条注释,但很长,所以让我们开始吧。
您可能想使用colClasses指定哪些列是因子。
如果您有很多列,我为简化操作所做的事情是使用我编写的以下函数:
abbr_to_colClass<-function(inits,counts){
  x<-substring(inits,1:nchar(inits),1:nchar(inits))
  types<-ifelse(x=="c","character",
                ifelse(x=="f","factor",
                       ifelse(x=="i","integer",
                              "numeric")))
  rep(types,substring(counts,1:nchar(counts),1:nchar(counts)))
}

假设你有一个包含类别列的.csv文件:

character 3
factor    2
integer   1
numeric   5
character 6

然后您可以使用我的函数进行设置

colClasses=abbr_to_colClass("cfinc","32156")

如果您有连续的一种类型的长字符串,这将特别节省空间。

(我知道这不是最强大的函数,但在需要读取许多字段时,它已经为我服务了很多次)


0

我创建了一个小的csv文件,可以确认当stringsAsFactors=TRUE时不会生成因子列。此外,指定colClasses为factor似乎也不起作用。

如果在fread之后运行此代码,它将把所有字符列转换为因子列。

for (j in which(sapply(df, class)=='character')) set(df, i=NULL, j=j, value=as.factor(df[[j]]))

0

尝试使用新的readr包,它已经被优化为快10倍且不会泄漏内存。现在可以使用col_types参数来指定collector(自定义解析器函数),而不是使用stringsAsFactors。请查看文档,特别是col_factor/parse_factor

require(readr)
read_csv(..., col_types=...)

@Arun,也许你需要先试一次通过编译层次列表的方法。可能要使用“select”列参数。 - smci
@Arun:看起来性能相当不错,Hadley 进行了大量重写,人们广泛使用它,让我们听听 OP 它的表现如何... 是的,需要一点努力来指定您的级别。 - smci
@Arun:通常你只需要加载前n行来捕获所有的因子水平,比如说n~100,000。最好让OP发布readrfread的头对头性能数据。 - smci
@Arun 我们没有必要在抽象的层面上继续讨论;问题的提出者可以提供他们的因子水平的详细信息。 推断所有因子水平可能是一个从琐碎(婚姻状况,年龄阶段,邮政编码,工资范围)到难以不可能完成的任务 - 这完全取决于他们的数据集。 至于性能,我们等待问题的提出者(或任何其他人)发布一些数字。 base :: read.csv 会泄漏内存,现在我们可以避免它。 - smci

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