我有一个非常大的数据库(超过 3000 万条记录),其结构如下:
test <- as.data.table(list(v1 = c("150,10001,11,Bien", "151,10002,11,Bien",
"152,10003,11,Bien", "153,10004,11,Mal",
"154,10005,11,Regular")))
我将该数据库下载为每行一个字符串,因为我在尝试使用
fread
直接阅读时错过了很多信息。例如,我收到与此类似的错误:"Stopped early on line 101. Expected 1099 fields but found 1100. Consider fill=TRUE and comment.char=. First discarded non-empty line"
因此我无法按预期下载整个数据库。
我想要实现的是从这些字符串创建四个变量。此代码在我的虚拟数据库中完美运行:
test <- test[, `:=`(id = lapply(strsplit(test$v1, split=","), "[", 1),
question_id = lapply(strsplit(test$v1, split=","), "[", 2),
answer_id = lapply(strsplit(test$v1, split=","), "[", 3),
answer = lapply(strsplit(test$v1, split=","), "[", 4))]
在我的实际数据库中,这段代码花费了很多时间。我等了两个多小时,没有得到任何结果。我想这种方法可能是错误的,因为我不是自己创建变量而是列表,这样使得过程比应有的速度慢:
Classes ‘data.table’ and 'data.frame': 5 obs. of 5 variables:
$ v1 : chr "150,10001,11,Bien" "151,10002,11,Bien" "152,10003,11,Bien" "153,10004,11,Mal" ...
$ id :List of 5
..$ : chr "150"
..$ : chr "151"
..$ : chr "152"
..$ : chr "153"
..$ : chr "154"
$ question_id:List of 5
..$ : chr "10001"
..$ : chr "10002"
..$ : chr "10003"
..$ : chr "10004"
..$ : chr "10005"
$ answer_id :List of 5
..$ : chr "11"
..$ : chr "11"
..$ : chr "11"
..$ : chr "11"
..$ : chr "11"
$ answer :List of 5
..$ : chr "Bien"
..$ : chr "Bien"
..$ : chr "Bien"
..$ : chr "Mal"
..$ : chr "Regular"
- attr(*, ".internal.selfref")=<externalptr>
我读到,使用 Windows 时,
parLapply
(来自 parallel
库的函数)往往比 lapply
慢,所以我放弃了该解决方案。
任何建议将不胜感激。
首先,可以避免在同一字符串上多次调用
tstrsplit
来获取特定列:
test <- as.data.table(list(v1 = c("150,10001,11,Bien", "151,10002,11,Bien","152,10003,11,Bien", "153,10004,11,Mal","154,10005,11,Regular")))
test[, c("id","question_id","answer_id","answer") := tstrsplit(v1, ",")][]
# v1 id question_id answer_id answer
# <char> <char> <char> <char> <char>
# 1: 150,10001,11,Bien 150 10001 11 Bien
# 2: 151,10002,11,Bien 151 10002 11 Bien
# 3: 152,10003,11,Bien 152 10003 11 Bien
# 4: 153,10004,11,Mal 153 10004 11 Mal
# 5: 154,10005,11,Regular 154 10005 11 Regular
既然你说你的行中有太多逗号,那么这仍然会失败。对于这些,从您当前(太慢)的方法来看,您只需要每个字符串的前四个元素,因此我们可以先将其删除。我将修改示例数据,以便我们可以在这里看到效果。
test <- as.data.table(list(v1 = c("150,10001,11,Bien", "151,10002,11,Bien","152,10003,11,Bien", "153,10004,11,Mal","154,10005,11,Regular")))
test[4, v1 := paste0(v1, ",quux")]
test[, c("id","question_id","answer_id","answer") := tstrsplit(v1, ",")][]
# Error in `[.data.table`(test, , `:=`(c("id", "question_id", "answer_id", :
# Supplied 4 columns to be assigned 5 items. Please see NEWS for v1.12.2.
您当前(太慢)的方法表明您只想要前四个,并且愿意放弃后面的所有内容,所以我建议使用条件
sub
。
test[nchar(gsub("[^,]+", "", v1)) > 3, v1 := sub(",[^,]*$", "", v1)]
test[, c("id","question_id","answer_id","answer") := tstrsplit(v1, ",")][]
# v1 id question_id answer_id answer
# <char> <char> <char> <char> <char>
# 1: 150,10001,11,Bien 150 10001 11 Bien
# 2: 151,10002,11,Bien 151 10002 11 Bien
# 3: 152,10003,11,Bien 152 10003 11 Bien
# 4: 153,10004,11,Mal 153 10004 11 Mal
# 5: 154,10005,11,Regular 154 10005 11 Regular
但是,我认为所有这些都是不必要的,因为您可以指示
fread
在发现提取列时进行填充(如警告/错误消息所示)。
fread("quux.csv")
# Warning in fread("quux.csv") :
# Stopped early on line 4. Expected 4 fields but found 5. Consider fill=TRUE and comment.char=. First discarded non-empty line: <<153,10004,11,Mal,quux>>
# V1 V2 V3 V4
# <int> <int> <int> <char>
# 1: 150 10001 11 Bien
# 2: 151 10002 11 Bien
# 3: 152 10003 11 Bien
fread("quux.csv", fill = TRUE)
# V1 V2 V3 V4 V5
# <int> <int> <int> <char> <char>
# 1: 150 10001 11 Bien
# 2: 151 10002 11 Bien
# 3: 152 10003 11 Bien
# 4: 153 10004 11 Mal quux
# 5: 154 10005 11 Regular
这当然是一条更安全的路径,因为它允许您评估额外的列是否可以丢弃,或者其他列之一是否应该在带引号的字符串中嵌入逗号。