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

问题描述 投票:2回答:5

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

r data.table
5个回答
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      

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
投票

一个选项是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>

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

0
投票

另外一个选项:

DT[, paste0("v", 1:5) := tstrsplit(text, ";", keep = 1:5)]
DT[, v6 := stringi::stri_match(text, regex = "^(?:.*?;){5}(.*)$")[,2]][]
© www.soinside.com 2019 - 2024. All rights reserved.