我正在使用data.table中的fread()
读取一个庞大的数据集。问题在于字段的数量(分隔符= ;
)在每一行上都不同。我主要对前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>
注意:之间的文字长度; ;可以有所不同,因此不总是一个,也不总是数字。
我的生产数据超过100万行,因此解决方案越快越好。
答案 0 :(得分:2)
问题在于,在第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)中列出的链接,以查看是否已解决该问题。
答案 1 :(得分:1)
选项为separate
,且参数extra
指定为“合并”
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>
答案 2 :(得分:1)
这是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
答案 3 :(得分:1)
我完全使用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
答案 4 :(得分:0)
另一个选择:
DT[, paste0("v", 1:5) := tstrsplit(text, ";", keep = 1:5)]
DT[, v6 := stringi::stri_match(text, regex = "^(?:.*?;){5}(.*)$")[,2]][]