R:在大型数据集的矢量元素上使用 STRSPLIT 和 GREP 花费的时间太长
R: Using STRSPLIT and GREP on vector elements on large dataset takes too long
(我的第一个 StackFlow 问题)
我的目标是改进用于识别哪些 NetApp 文件共享与哪些 AD 权限分配组相关的 ETL 过程。当前名为 'TreeSize' 的应用程序扫描大量卷并输出大量大型 .CSV 文件 (35mb+)。我想合并此数据并删除每个组(或命名用户)不以大写 G 或 D('^[GD]')开头的所有权限信息。由于要处理超过 700,000 行,目前我需要 24 小时才能完成 运行。我希望有更好的方法来更有效地处理这些数据,从而大大缩短时间。
这是所有文件合并后的测试数据,与实际数据相似。使用 rownum 调整数据大小。 (真实数据700000+)
测试数据
set.seed(42)
rownum <- 2000 #Real number over 700000
i <- 1
datalist <- list()
while (i <= rownum) {
randomStr1 <- paste(sample(c(0:9, letters, LETTERS[4:7], "-"),10, replace=TRUE),collapse="")
randomStr2 <- paste(sample(c(0:9, letters, LETTERS[4:7], " "),10, replace=TRUE),collapse="")
randomStr3 <- paste(sample(c(0:9, letters, LETTERS[4:7], " & "),10, replace=TRUE),collapse="")
randomStr4 <- sample(c("full", "+r+w+x", "+r+x"),3)
datalist$volume[i] <- rep(sample(LETTERS[1:6]))[1]
datalist$permissions[i] <- paste(c(randomStr1,randomStr2,randomStr3),randomStr4,sep = ': ',collapse = ' | ')
i = i+1
}
dat <- data.frame(datalist)
View(dat)
我创建了一个循环遍历合并数据的 WHILE 循环。我首先使用 STRSPLIT 创建一个向量,其中包含“ | 之间的每个向量元素” “每根管子。然后我在 GREP 命令中传递每个矢量元素,搜索 (‘^[GD]’) 的 RegExp。如果找到它,它会保留向量元素,如果找到多个元素,它会在分号和 space (“; “)
之间将数据合并在一起
这是我目前的做法。
i <- 1
while (i <= length(dat$permissions)) {
df <- strsplit(dat$permissions, " \| |: ")[[i]] #create a vector containing each vector element
dat$permissions[i] <- paste(df[grep('^[GD]', df)], collapse = "; ") #Only keep where starts with G or D then Paste together
print(paste(i, " of ", length(dat$permissions), " ", dat$permissions[i]))
i = i + 1 }
View(dat)
完成后,我导出到一个 .CSV 文件以完成转换。
处理这些数据以大幅减少处理时间的更好方法是什么?
我认为加快速度的关键是避免循环每一行,当它可以在 strsplit
和最终 paste
操作的单个矢量化操作中完成时。
paste(
seq_along(dat$permissions), "of", nrow(dat),
lapply(strsplit(dat$permissions, " \| |: "),
\(x) paste(x[grepl("^[GD]", x)], collapse="; "))
)
应该会导致大约 250 倍的加速:
system.time({
paste(
seq_along(dat$permissions), "of", nrow(dat),
lapply(strsplit(dat$permissions, " \| |: "), \(x) paste(x[grepl("^[GD]", x)], collapse="; "))
)
})
## user system elapsed
## 0.03 0.00 0.03
system.time({
i <- 1
while (i <= length(dat$permissions)) {
df <- strsplit(dat$permissions, " \| |: ")[[i]] #create a vector containing each vector element
dat$permissions[i] <- paste(df[grep('^[GD]', df)], collapse = "; ") #Only keep where starts with G or D then Paste together
print(paste(i, " of ", length(dat$permissions), " ", dat$permissions[i]))
i = i + 1 }
})
## user system elapsed
## 8.11 0.06 8.17
加快速度的一个选择是避免拆分字符串并使用 stringr::str_extract_all()
:
直接提取匹配项
library(stringr)
library(purrr)
map_chr(str_extract_all(dat$permissions, "(?<=^|\| )[GD].*?(?=:)"), str_c, collapse = "; ")
这进一步改进了 thelatemail 已经很快的替代方案:
microbenchmark::microbenchmark(
extract = map_chr(str_extract_all(dat$permissions, "(?<=^|\| )[GD].*?(?=:)"), str_c, collapse = "; "),
splitmatch = sapply(strsplit(dat$permissions, " \| |: "), \(x) paste(x[grepl("^[GD]", x)], collapse = "; ")),
check = "equal"
)
Unit: milliseconds
expr min lq mean median uq max neval
extract 5.8841 6.14375 6.681234 6.3134 6.41655 15.1264 100
splitmatch 23.6010 24.00005 25.501808 24.2499 24.78320 40.4140 100
(我的第一个 StackFlow 问题)
我的目标是改进用于识别哪些 NetApp 文件共享与哪些 AD 权限分配组相关的 ETL 过程。当前名为 'TreeSize' 的应用程序扫描大量卷并输出大量大型 .CSV 文件 (35mb+)。我想合并此数据并删除每个组(或命名用户)不以大写 G 或 D('^[GD]')开头的所有权限信息。由于要处理超过 700,000 行,目前我需要 24 小时才能完成 运行。我希望有更好的方法来更有效地处理这些数据,从而大大缩短时间。
这是所有文件合并后的测试数据,与实际数据相似。使用 rownum 调整数据大小。 (真实数据700000+)
测试数据
set.seed(42)
rownum <- 2000 #Real number over 700000
i <- 1
datalist <- list()
while (i <= rownum) {
randomStr1 <- paste(sample(c(0:9, letters, LETTERS[4:7], "-"),10, replace=TRUE),collapse="")
randomStr2 <- paste(sample(c(0:9, letters, LETTERS[4:7], " "),10, replace=TRUE),collapse="")
randomStr3 <- paste(sample(c(0:9, letters, LETTERS[4:7], " & "),10, replace=TRUE),collapse="")
randomStr4 <- sample(c("full", "+r+w+x", "+r+x"),3)
datalist$volume[i] <- rep(sample(LETTERS[1:6]))[1]
datalist$permissions[i] <- paste(c(randomStr1,randomStr2,randomStr3),randomStr4,sep = ': ',collapse = ' | ')
i = i+1
}
dat <- data.frame(datalist)
View(dat)
我创建了一个循环遍历合并数据的 WHILE 循环。我首先使用 STRSPLIT 创建一个向量,其中包含“ | 之间的每个向量元素” “每根管子。然后我在 GREP 命令中传递每个矢量元素,搜索 (‘^[GD]’) 的 RegExp。如果找到它,它会保留向量元素,如果找到多个元素,它会在分号和 space (“; “)
之间将数据合并在一起这是我目前的做法。
i <- 1
while (i <= length(dat$permissions)) {
df <- strsplit(dat$permissions, " \| |: ")[[i]] #create a vector containing each vector element
dat$permissions[i] <- paste(df[grep('^[GD]', df)], collapse = "; ") #Only keep where starts with G or D then Paste together
print(paste(i, " of ", length(dat$permissions), " ", dat$permissions[i]))
i = i + 1 }
View(dat)
完成后,我导出到一个 .CSV 文件以完成转换。
处理这些数据以大幅减少处理时间的更好方法是什么?
我认为加快速度的关键是避免循环每一行,当它可以在 strsplit
和最终 paste
操作的单个矢量化操作中完成时。
paste(
seq_along(dat$permissions), "of", nrow(dat),
lapply(strsplit(dat$permissions, " \| |: "),
\(x) paste(x[grepl("^[GD]", x)], collapse="; "))
)
应该会导致大约 250 倍的加速:
system.time({
paste(
seq_along(dat$permissions), "of", nrow(dat),
lapply(strsplit(dat$permissions, " \| |: "), \(x) paste(x[grepl("^[GD]", x)], collapse="; "))
)
})
## user system elapsed
## 0.03 0.00 0.03
system.time({
i <- 1
while (i <= length(dat$permissions)) {
df <- strsplit(dat$permissions, " \| |: ")[[i]] #create a vector containing each vector element
dat$permissions[i] <- paste(df[grep('^[GD]', df)], collapse = "; ") #Only keep where starts with G or D then Paste together
print(paste(i, " of ", length(dat$permissions), " ", dat$permissions[i]))
i = i + 1 }
})
## user system elapsed
## 8.11 0.06 8.17
加快速度的一个选择是避免拆分字符串并使用 stringr::str_extract_all()
:
library(stringr)
library(purrr)
map_chr(str_extract_all(dat$permissions, "(?<=^|\| )[GD].*?(?=:)"), str_c, collapse = "; ")
这进一步改进了 thelatemail 已经很快的替代方案:
microbenchmark::microbenchmark(
extract = map_chr(str_extract_all(dat$permissions, "(?<=^|\| )[GD].*?(?=:)"), str_c, collapse = "; "),
splitmatch = sapply(strsplit(dat$permissions, " \| |: "), \(x) paste(x[grepl("^[GD]", x)], collapse = "; ")),
check = "equal"
)
Unit: milliseconds
expr min lq mean median uq max neval
extract 5.8841 6.14375 6.681234 6.3134 6.41655 15.1264 100
splitmatch 23.6010 24.00005 25.501808 24.2499 24.78320 40.4140 100