将元素x粘贴到列表中的y

时间:2019-03-28 17:11:54

标签: r data.table

我正在使用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万行,因此解决方案越快越好。

5 个答案:

答案 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.tablestringr的选项。不确定是否比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 lapplypaste0和您想要的东西。虽然不确定如何与其他基准进行比较。

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]][]