6

我正在处理一个大型数据集x。我想删除x一组列中的一个或多个列中缺少的行x,该组由字符向量指定varcols

到目前为止,我已经尝试了以下方法:

require(data.table)
x <- CJ(var1=c(1,0,NA),var2=c(1,0,NA))
x[, textcol := letters[1:nrow(x)]]
varcols <- c("var1","var2")

x[, missing := apply(sapply(.SD,is.na),1,any),.SDcols=varcols]
x <- x[!missing]

有没有更快的方法来做到这一点?谢谢。

4

4 回答 4

9

这应该比使用更快apply

x[rowSums(is.na(x[, ..varcols])) == 0, ]
#    var1 var2 textcol
# 1:    0    0       e
# 2:    0    1       f
# 3:    1    0       h
# 4:    1    1       i
于 2012-12-07T01:54:22.030 回答
4

这是一个 c++ 解决方案的修订版本,基于与 Matthew 的长时间讨论进行了许多修改(请参阅下面的评论)。我是 c 的新手,所以我相信有人仍然可以改进这一点。

library("RcppArmadillo")您应该能够运行整个文件之后,包括使用sourceCpp('cleanmat.cpp'). c++ 文件包含两个函数。cleanmat接受两个参数(X和列的索引)并返回没有缺失值的列的矩阵。keep只接受一个参数X并返回一个逻辑向量。

关于传递data.table对象的注意事项:这些函数不接受 adata.table作为参数。必须修改函数以DataFrame作为参数(请参见此处.

清洁垫.cpp

#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]

using namespace Rcpp;
using namespace arma;


// [[Rcpp::export]]
mat cleanmat(mat X, uvec idx) {
    // remove colums
    X = X.cols(idx - 1);
    // get dimensions
    int n = X.n_rows,k = X.n_cols;
    // create keep vector
    vec keep = ones<vec>(n);
    for (int j = 0; j < k; j++) 
        for (int i = 0; i < n; i++) 
            if (keep[i] && !is_finite(X(i,j))) keep[i] = 0;
    // alternative with view for each row (slightly slower)
    /*vec keep = zeros<vec>(n);
    for (int i = 0; i < n; i++) {
         keep(i) = is_finite(X.row(i));
    }*/  
    return (X.rows(find(keep==1)));
}


// [[Rcpp::export]]
LogicalVector keep(NumericMatrix X) {
    int n = X.nrow(), k = X.ncol();
    // create keep vector
    LogicalVector keep(n, true);
    for (int j = 0; j < k; j++) 
        for (int i = 0; i < n; i++) 
            if (keep[i] && NumericVector::is_na(X(i,j))) keep[i] = false;

    return (keep);
}


/*** R
require("Rcpp")
require("RcppArmadillo")
require("data.table")
require("microbenchmark")

# create matrix
X = matrix(rnorm(1e+07),ncol=100)
X[sample(nrow(X),1000,replace = TRUE),sample(ncol(X),1000,replace = TRUE)]=NA
colnames(X)=paste("c",1:ncol(X),sep="")

idx=sample(ncol(X),90)
microbenchmark(
  X[!apply(X[,idx],1,function(X) any(is.na(X))),idx],
  X[rowSums(is.na(X[,idx])) == 0, idx],
  cleanmat(X,idx),
  X[keep(X[,idx]),idx],
times=3)

# output
# Unit: milliseconds
#                                                     expr       min        lq    median        uq       max
# 1                                       cleanmat(X, idx)  253.2596  259.7738  266.2880  272.0900  277.8921
# 2 X[!apply(X[, idx], 1, function(X) any(is.na(X))), idx] 1729.5200 1805.3255 1881.1309 1913.7580 1946.3851
# 3                                 X[keep(X[, idx]), idx]  360.8254  361.5165  362.2077  371.2061  380.2045
# 4                  X[rowSums(is.na(X[, idx])) == 0, idx]  358.4772  367.5698  376.6625  379.6093  382.5561

*/
于 2012-12-08T02:39:10.417 回答
3

为了速度,有大量的varcols,也许看按列迭代。像这样的东西(未经测试):

keep = rep(TRUE,nrow(x))
for (j in varcols) keep[is.na(x[[j]])] = FALSE
x[keep]

问题is.na在于它创建了一个新的逻辑向量来保存其结果,然后必须由 R 循环以找到 TRUE,以便它知道要设置 FALSE 的哪个保持。但是,在上面的 for 循环中,R 可以为 的结果重用(大小相同的)先前的临时内存is.na,因为它被标记为未使用并且在每次迭代完成后可供重用。国际大学联合会。

1. is.na(x[, ..varcols])
这没关系,但会创建一个大副本来保存与 一样大的逻辑矩阵length(varcols)。并且==0on 结果rowSums也需要一个新的向量。

2. !is.na(var1) & !is.na(var2)
也可以,但是!会再次创建一个新向量,&. 的每个结果is.na都必须由 R 单独保存,直到表达式完成。length(varcols)直到增加很多,或者很大,可能没有什么区别ncol(x)

3. CJ(c(0,1),c(0,1))
迄今为止最好的,但不确定这将如何随着length(varcols)增加而扩展。CJ需要分配新内存,并且在连接开始之前,它会循环使用所有组合填充该内存。

因此,最快的(我猜)将是这样的 C 版本(伪代码):

keep = rep(TRUE,nrow(x))
for (j=0; j<varcols; j++)
   for (i=0; i<nrow(x); i++)
       if (keep[i] && ISNA(x[i,j])) keep[i] = FALSE;
x[keep]

这将需要一个单一的分配keep(在 C 或 R 中),然后 C 循环将keep在看到 NA 时循环更新列。C 可以在 Rcpp、RStudio、内联包或老派中完成。重要的是这两个循环是这样循环的,以提高缓存效率。想法是,keep[i] &&当某些行中有很多 NA 时,该部分有助于加快速度,甚至可以在每行中的第一个 NA 之后完全保存后面的列值。

于 2012-12-07T09:56:43.077 回答
2

还有两种方法

两个矢量扫描

x[!is.na(var1) & !is.na(var2)]

加入非 NA 值的唯一组合

如果您提前知道可能的唯一值,这将是最快的

system.time(x[CJ(c(0,1),c(0,1)), nomatch=0])

一些时间

x <-data.table(var1 = sample(c(1,0,NA), 1e6, T, prob = c(0.45,0.45,0.1)),
                var2= sample(c(1,0,NA), 1e6, T, prob = c(0.45,0.45,0.1)),
                key = c('var1','var2'))

system.time(x[rowSums(is.na(x[, ..varcols])) == 0, ])
   user  system elapsed 
   0.09    0.02    0.11 

 system.time(x[!is.na(var1) & !is.na(var2)])
   user  system elapsed 
   0.06    0.02    0.07 


 system.time(x[CJ(c(0,1),c(0,1)), nomatch=0])
   user  system elapsed 
   0.03    0.00    0.04 
于 2012-12-07T04:09:19.250 回答