运行 "apply" 非常大的数据框上的命令
Running "apply" command on a very large data frame
我在 R 中有一个尺寸为 15,000,000 x 140
的 tibble。大小约为 6 GB。
我想检查给定行的第 11-40 列中的任何列是否从特定列表开始。我想得到一个 1 和 0 的向量,然后是 15,000,000 长。
我可以使用以下方法做到这一点:
subResult <- apply(rawData[,11:40], c(1,2), function(x){substring(x,1,3) %in% c("295", "296", "297", "298", "299")})
result <- apply(subResult, 1, sum)
问题是这太慢了——仅第一行就需要 1 天多的时间。
有什么方法可以更快地做到这一点——也许直接通过 dplyr 或 data.table?
谢谢!
这里是数据的样本,只保留了第 11-40 列。
!> head(rawData)
# A tibble: 6 x 30
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 39402 39451 3fv3i 19593 fk20 14p4 59304 329fj2 NA NA NA NA NA
2 39422 f203ff vmio2 vo2493 19149 59833 13404 394034 43920 349304 59302 1934 34834
3 3432f32 fe493 43943 H2344 53049 V602 3124 K148 K13 NA NA NA NA
# ... with 17 more variables: X14 <chr>, X15 <chr>, X16 <chr>, X17 <chr>,
# X18 <chr>, X19 <chr>, X20 <chr>, X21 <chr>, X22 <chr>, X23 <chr>,
# X24 <chr>, X25 <chr>, X26 <chr>, X27 <chr>, X28 <chr>, X29 <chr>, X30 <chr>
根据描述,这可以通过 tidyverse
来完成
library(tidyverse)
rawData %>%
select(11:40) %>% #select the columns
#convert to logical columns
mutate_all(funs(substring(.,1,3) %in% c("295", "296", "297", "298", "299"))) %>%
reduce('+') %>% #get the rowwise sum
mutate(rawData, newcol = .) # assign a new column to the original data
或通过将 'data.frame' 转换为 'data.table' (setDT(rawData)
),使用 data.table
,在 .SDcols
中指定感兴趣的列,循环列,通过使用 OP 的条件将其转换为逻辑,Reduce
通过获取每一行的 sum
并将 (:=
) 分配给 'newcol'
library(data.table)
setDT(rawData)[, newCol := Reduce('+', lapply(.SD, function(x)
substring(x, 1, 3) %chin% c("295", "296", "297", "298", "299"))),
.SDcols = 11:40]
尝试使用 Rcpp
包。
这是一个简单的 C++ 程序,它接受两个字符串向量,并检查第一个元素的 3 个字符是否与第二个元素相等。因此它将输出大小为长度(第一个向量)x 长度(第二个向量)的逻辑矩阵。
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
LogicalMatrix IndicatorMatrix(std::vector<std::string> target, std::vector<std::string> tocheck) {
int nrows = target.size();
int ncols = tocheck.size();
LogicalMatrix ind(nrows, ncols);
for(int r=0; r<nrows; r++) {
for(int c=0; c<ncols; c++) {
bool found = target[r].substr(0,3) == tocheck[c];
ind(r,c) = found;
}
}
return ind;
}
之后,您可以将此程序源代码化为 R 并使用您的 IndicatorMatrix
函数,就好像它是一个 R 函数对象一样。
library(Rcpp)
sourceCpp("C:/Users/Desktop/indicatorMatrix.cpp")
rep("123456", 15000000) -> x
df <- data.frame(x,x,x,x,x,x,x,x, stringsAsFactors=FALSE)
y <- c("123", "124", "345", "231", "675", "344", "222")
t1 <- Sys.time()
out <- lapply(1:length(df), function(col) {
res <- IndicatorMatrix(unlist(df[,col]), y)
res
})
t2 <- Sys.time()
t2-t1
程序在大约 100 秒内在 1500 万行的 8 列数据框中搜索了 8 个 3 个字符的字符串。所以这对你来说可能是正确的方向。
我的评论:
- apply 将您的数据转换为矩阵
- 数据框首先是一个列表,而不是矩阵
substring()
是矢量化函数(%in%
也是)
所以,我会这样做:
sapply(rawData[11:40], function(var) {
substring(var, 1, 3) %in% c("295", "296", "297", "298", "299")
})
然后使用 rowSums()
而不是 apply(subResult, 1, sum)
。
我在 R 中有一个尺寸为 15,000,000 x 140
的 tibble。大小约为 6 GB。
我想检查给定行的第 11-40 列中的任何列是否从特定列表开始。我想得到一个 1 和 0 的向量,然后是 15,000,000 长。
我可以使用以下方法做到这一点:
subResult <- apply(rawData[,11:40], c(1,2), function(x){substring(x,1,3) %in% c("295", "296", "297", "298", "299")})
result <- apply(subResult, 1, sum)
问题是这太慢了——仅第一行就需要 1 天多的时间。
有什么方法可以更快地做到这一点——也许直接通过 dplyr 或 data.table?
谢谢!
这里是数据的样本,只保留了第 11-40 列。
!> head(rawData)
# A tibble: 6 x 30
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 39402 39451 3fv3i 19593 fk20 14p4 59304 329fj2 NA NA NA NA NA
2 39422 f203ff vmio2 vo2493 19149 59833 13404 394034 43920 349304 59302 1934 34834
3 3432f32 fe493 43943 H2344 53049 V602 3124 K148 K13 NA NA NA NA
# ... with 17 more variables: X14 <chr>, X15 <chr>, X16 <chr>, X17 <chr>,
# X18 <chr>, X19 <chr>, X20 <chr>, X21 <chr>, X22 <chr>, X23 <chr>,
# X24 <chr>, X25 <chr>, X26 <chr>, X27 <chr>, X28 <chr>, X29 <chr>, X30 <chr>
根据描述,这可以通过 tidyverse
library(tidyverse)
rawData %>%
select(11:40) %>% #select the columns
#convert to logical columns
mutate_all(funs(substring(.,1,3) %in% c("295", "296", "297", "298", "299"))) %>%
reduce('+') %>% #get the rowwise sum
mutate(rawData, newcol = .) # assign a new column to the original data
或通过将 'data.frame' 转换为 'data.table' (setDT(rawData)
),使用 data.table
,在 .SDcols
中指定感兴趣的列,循环列,通过使用 OP 的条件将其转换为逻辑,Reduce
通过获取每一行的 sum
并将 (:=
) 分配给 'newcol'
library(data.table)
setDT(rawData)[, newCol := Reduce('+', lapply(.SD, function(x)
substring(x, 1, 3) %chin% c("295", "296", "297", "298", "299"))),
.SDcols = 11:40]
尝试使用 Rcpp
包。
这是一个简单的 C++ 程序,它接受两个字符串向量,并检查第一个元素的 3 个字符是否与第二个元素相等。因此它将输出大小为长度(第一个向量)x 长度(第二个向量)的逻辑矩阵。
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
LogicalMatrix IndicatorMatrix(std::vector<std::string> target, std::vector<std::string> tocheck) {
int nrows = target.size();
int ncols = tocheck.size();
LogicalMatrix ind(nrows, ncols);
for(int r=0; r<nrows; r++) {
for(int c=0; c<ncols; c++) {
bool found = target[r].substr(0,3) == tocheck[c];
ind(r,c) = found;
}
}
return ind;
}
之后,您可以将此程序源代码化为 R 并使用您的 IndicatorMatrix
函数,就好像它是一个 R 函数对象一样。
library(Rcpp)
sourceCpp("C:/Users/Desktop/indicatorMatrix.cpp")
rep("123456", 15000000) -> x
df <- data.frame(x,x,x,x,x,x,x,x, stringsAsFactors=FALSE)
y <- c("123", "124", "345", "231", "675", "344", "222")
t1 <- Sys.time()
out <- lapply(1:length(df), function(col) {
res <- IndicatorMatrix(unlist(df[,col]), y)
res
})
t2 <- Sys.time()
t2-t1
程序在大约 100 秒内在 1500 万行的 8 列数据框中搜索了 8 个 3 个字符的字符串。所以这对你来说可能是正确的方向。
我的评论:
- apply 将您的数据转换为矩阵
- 数据框首先是一个列表,而不是矩阵
substring()
是矢量化函数(%in%
也是)
所以,我会这样做:
sapply(rawData[11:40], function(var) {
substring(var, 1, 3) %in% c("295", "296", "297", "298", "299")
})
然后使用 rowSums()
而不是 apply(subResult, 1, sum)
。