我正在使用data.table中的fread()
阅读一个极大的数据集。问题是字段的nuber(separator = ;
)在每一行上都有所不同。我主要对前5列感兴趣,但也希望看到第6列到第n列的内容。
样本数据
我用data.table::fread()
和sep = ""
读了数据,读完整行。
DT <- data.table::fread("1;2;3;4;5;6
1;2;3;4;5;6;7;8
1;2;3;4;5", sep = "", header = FALSE, col.names = "text" )
# text
#1: 1;2;3;4;5;6
#2: 1;2;3;4;5;6;7;8
#3: 1;2;3;4;5
代码到目前为止
前五列出现在所有行中,我可以使用tstrsplit()
轻松地将它们取出:
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit( text , ";")[1:5] ][]
# text v1 v2 v3 v4 v5
# 1: 1;2;3;4;5;6 1 2 3 4 5
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5
# 3: 1;2;3;4;5 1 2 3 4 5
我的问题
我想将第五个(或第五个分号后面的所有内容)之后的所有字段放到名为v6
的列中,以便结果如下所示:
desired_output <- DT[, v6 := c( "6", "6;7;8", NA_character_) ]
# text v1 v2 v3 v4 v5 v6
# 1: 1;2;3;4;5;6 1 2 3 4 5 6
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
# 3: 1;2;3;4;5 1 2 3 4 5 <NA>
注意:文本之间的长度; ;可以变化,因此并不总是一个,也不总是数字。
我的生产数据超过1M行,因此解决方案越快越好。
这是data.table
和stringr
的选项。不确定它是否比separate
更快
library(stringr)
DT[, paste0('col', 1:5) := tstrsplit(text, ';')[1:5]] # or tstrsplit(str_extract(text, '(\\d+;){4}\\d+'), ';')
DT[, col6 := str_remove(text, '(\\d+;){5}|(\\d+;){4}\\d+')]
DT
# text col1 col2 col3 col4 col5 col6
# 1: 1;2;3;4;5;6 1 2 3 4 5 6
# 2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
# 3: 1;2;3;4;5 1 2 3 4 5
问题是在201行有9列,但此时fread
已经确定最多有8列。您可以使用以下命令破解它以读取所有9列:
x <- fread("test.txt",fill=TRUE, sep="\t", colClasses=rep("logical",9))
如果9不够,请将该数字增加,直到您再也看不到该错误为止。这实际上不应该将任何列强制转换为逻辑列(当指定colClasses
参数时,data.table::fread
拒绝以导致信息丢失的方式强制列类)。我不确定这种方法会导致什么样的惩罚,但我想它比其他方法更快(至少在你建立最大列数后更快)。
如果您仍然希望将6+列粘贴到一个列中,则有很多方法可以执行此操作。
对于后代,请参阅问题评论中列出的链接(https://github.com/Rdatatable/data.table/issues/2727)以查看是否已解决此问题。
一个选项是separate
,参数extra
指定为'merge'
library(tidyverse)
n <- 6
DT %>%
separate(text, into = paste0("v", seq_len(n)), extra = "merge",
convert = TRUE, remove = FALSE)
# text v1 v2 v3 v4 v5 v6
#1: 1;2;3;4;5;6 1 2 3 4 5 6
#2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
#3: 1;2;3;4;5 1 2 3 4 5 <NA>
我接近你想要的append
transpose
lapply
和paste0
。不知道它如何与其他人进行对比。
DT[, c("v1", "v2", "v3", "v4", "v5", "v6") := append(tstrsplit(text , ";")[1:5],
transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';')))][]
这也可以使用链接概念进行修改,以便更好地阅读
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit(text , ";")[1:5]
][, v6 := transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';'))][]
两者都产生以下结果
text v1 v2 v3 v4 v5 v6
1: 1;2;3;4;5;6 1 2 3 4 5 6;NA;NA
2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
3: 1;2;3;4;5 1 2 3 4 5 NA;NA;NA
生成NA
以保持列表元素的长度相同。但在链中进一步添加[, v6 := gsub(";NA", "", v6)]
会移除NA
DT[, c("v1", "v2", "v3", "v4", "v5") := tstrsplit(text , ";")[1:5]
][, v6 := transpose(lapply(transpose(tstrsplit(text, ";")[-c(1:5)]), paste0, collapse=';'))
][, v6 := gsub(";NA", "", v6)][]
最后给予
text v1 v2 v3 v4 v5 v6
1: 1;2;3;4;5;6 1 2 3 4 5 6
2: 1;2;3;4;5;6;7;8 1 2 3 4 5 6;7;8
3: 1;2;3;4;5 1 2 3 4 5 NA
另外一个选项:
DT[, paste0("v", 1:5) := tstrsplit(text, ";", keep = 1:5)]
DT[, v6 := stringi::stri_match(text, regex = "^(?:.*?;){5}(.*)$")[,2]][]