6

私は大規模なデータセットを扱っていxます。文字ベクトル で指定されているxの列セットの 1 つ以上の列で欠落しているの行を削除したいと考えています。xvarcols

これまでのところ、次のことを試しました。

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

これは、Matthewとの長い議論に基づいていくつかの変更を加えたc ++ソリューションの改訂版です(以下のコメントを参照)。私はcを初めて使用するので、誰かがこれを改善できる可能性があると確信しています。

その後library("RcppArmadillo")、を使用してベンチマークを含むファイル全体を実行できるようになりますsourceCpp('cleanmat.cpp')。c++ファイルには2つの関数が含まれています。cleanmat2つの引数(Xおよび列のインデックス)を取り、値が欠落している列のない行列を返します。keep引数を1つ取りX、論理ベクトルを返します。

オブジェクトの受け渡しに関する注意:これらの関数は引数としてdata.tableaを受け入れません。関数は、引数data.tableを取るように変更する必要があります(ここを参照してください) 。DataFrame

cleanmat.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再利用できます。これは、未使用とマークされ、各反復が完了すると再利用できるようになるためです。IIUC。

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]

これには、(C または R での) 単一の割り当てが必要でありkeep、C ループはkeep、NA が検出されるたびに列の更新をループします。C は、Rcpp、RStudio、インライン パッケージ、または古い学校で実行できます。キャッシュ効率のために、2 つのループがそのように回っていることが重要です。keep[i] &&一部の行に多くの NA がある場合、各行の最初の NA の後に後の列の値をフェッチすることさえも節約するために、この部分は速度を向上させると考えられています。

于 2012-12-07T09:56:43.077 に答える
2

さらに2つのアプローチ

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 に答える