我有一个data.table DT,其中包含一个字符串列和一个数字列,该数字列指示应从字符串开头提取多少个单词。
> require(data.table)
> DT <- data.table(string_col = c("A BB CCC", "DD EEE FFFF GDG", "AB DFD EFGD ABC DBC", "ABC DEF")
, first_n_words = c(2, 3, 3, 1))
> DT
string_col first_n_words
1: A BB CCC 2
2: DD EEE FFFF GDG 3
3: AB DFD EFGD ABC DBC 3
4: ABC DEF 1
我想用string_col的前n个单词添加一个新列,如下所示:
> output_DT
string_col first_n_words output_string_col
1: A BB CCC 2 A BB
2: DD EEE FFFF GDG 3 DD EEE FFFF
3: AB DFD EFGD ABC DBC 3 AB DFD EFGD
4: ABC DEF 1 ABC
这是可以使用的gsub语法:
> gsub(paste0("^((\\w+\\W+){", first_n_words - 1, "}\\w+).*$"),"\\1", string_col)
我基本上需要为每一行创建该gsub函数,使用该行的first_n_words,然后再将其应用于该行的string_col。 我只对data.table语法解决方案感兴趣,因为这是一个非常大的数据集。最好使用gsub解决方案。
编辑:我已经尝试了以下方法,但没有用
> DT[, output_string_col := gsub(paste0("^((\\w+\\W+){", first_n_words - 1, "}\\w+).*$"),"\\1", string_col)]
Warning message:
In gsub(paste0("^((\\w+\\W+){", first_n_words - 1, "}\\w+).*$"), :
argument 'pattern' has length > 1 and only the first element will be used
>## This is not the desired output
> DT
string_col first_n_words output_string_col
1: A BB CCC 2 A BB
2: DD EEE FFFF GDG 3 DD EEE
3: AB DFD EFGD ABC DBC 3 AB DFD
4: ABC DEF 1 ABC DEF
这不是所需的输出
答案 0 :(得分:3)
一种可能的方法是:
stringr::word(DT$string_col, end = DT$first_n_words)
#output
[1] "A BB" "DD EEE FFFF" "AB DFD EFGD" "ABC"
以下是此小型数据集的速度比较:
library(microbenchmark)
denis <- function(x){
x[,line := .I]
x[, output_string_col := gsub(paste0("^((\\w+\\W+){", first_n_words - 1, "}\\w+).*$"),"\\1", string_col),
by = line]
x[,("line") := NULL]
}
Tim <- function(x){
x[, output_string_col := apply(x, 1, function(x) {
gsub(paste0("^((\\w+\\W+){", as.numeric(x[2]) - 1, "}\\w+).*$"), "\\1", x[1])
})]
}
miss <- function(x){
x[, output_string_col := stringr::word(string_col, end = first_n_words)]
}
microbenchmark(
Tim(DT),
miss(DT),
denis(DT)
)
Unit: milliseconds
expr min lq mean median uq max neval cld
Tim(DT) 1.875036 1.926662 2.174488 1.971941 2.181196 12.83158 100 a
miss(DT) 1.452720 1.484245 1.710604 1.510905 1.592787 15.27196 100 a
denis(DT) 2.780183 2.864604 3.255014 2.948813 3.126542 18.78252 100 b
在更大的数据集上:
DT <- DT[sample(1:4, 100000, replace = TRUE),]
Unit: seconds
expr min lq mean median uq max neval cld
Tim(DT) 13.924312 14.628571 15.030614 14.810397 15.840749 15.949039 5 b
miss(DT) 3.571372 3.939229 4.150258 4.237873 4.492383 4.510435 5 a
denis(DT) 11.291374 11.728155 13.362248 12.738197 13.478435 17.575077 5 b
G。Grothendieck的微基准测试可能不是衡量数据表性能的最正确方法,因为DT从一次迭代更改为另一次迭代而未将其重置为初始值。
因此在接下来的几行中,将在创建数据表后仅对性能进行一次测量
DT <- data.table(string_col = c("A BB CCC",
"DD EEE FFFF GDG",
"AB DFD EFGD ABC DBC",
"ABC DEF"),
first_n_words = c(2, 3, 3, 1))
set.seed(1)
ind <- sample(1:4, 100000, replace = TRUE)
DT1 <- DT[ind,]
system.time(Tim(DT1))
#output
user system elapsed
14.06 0.02 15.01
DT2 <- DT[ind,]
system.time(miss(DT2))
#output
user system elapsed
2.82 0.00 2.87
DT3 <- DT[ind,]
system.time(denis(DT3))
#output
user system elapsed
11.56 0.03 11.98
all.equal(DT1, DT2)
all.equal(DT2, DT3)
答案 1 :(得分:3)
继续使用data.table的一个答案是使用分组操作,因为您需要gsub中的值而不是向量:
DT[,line := .I]
DT[, output_string_col := gsub(paste0("^((\\w+\\W+){", first_n_words - 1, "}\\w+).*$"),"\\1", string_col),by = line]
> DT
string_col first_n_words line output_string_col
1: A BB CCC 2 1 A BB
2: DD EEE FFFF GDG 3 2 DD EEE FFFF
3: AB DFD EFGD ABC DBC 3 3 AB DFD EFGD
4: ABC DEF 1 4 ABC
@Franck重新标记后,分组应该放在first_n_words
上以提高效率
DT[, output_string_col := gsub(paste0("^((\\w+\\W+){", first_n_words[1] - 1, "}\\w+).*$"),"\\1", string_col),by = first_n_words]
此修改版本的基准测试结果更快:
library(microbenchmark)
denis <- function(x){
x[, output_string_col := gsub(paste0("^((\\w+\\W+){", first_n_words[1] - 1, "}\\w+).*$"),"\\1", string_col),by = first_n_words]
}
Tim <- function(x){
x[, output_string_col := apply(x, 1, function(x) {
gsub(paste0("^((\\w+\\W+){", as.numeric(x[2]) - 1, "}\\w+).*$"), "\\1", x[1])
})]
}
miss <- function(x){
x[, output_string_col := stringr::word(string_col, end = first_n_words)]
}
DT <- DT[sample(1:4, 1000, replace = TRUE),]
microbenchmark(
Tim(DT),
miss(DT),
denis(DT)
)
Unit: milliseconds
expr min lq mean median uq
Tim(DT) 56.851716 57.836126 60.435164 58.714486 60.753051
miss(DT) 11.042056 11.516928 12.427029 11.871800 12.617031
denis(DT) 1.993437 2.355283 2.555936 2.615181 2.680001
max neval
111.169277 100
20.916932 100
3.530668 100
答案 2 :(得分:0)
尝试在行模式下使用apply
:
apply(DT[, c('string_col', 'first_n_words')], 1, function(x) {
gsub(paste0("^((\\w+\\W+){", x[1] - 1, "}\\w+).*$"), "\\1", x[0])
})