0

Rでの「if」と「for」の使い方を学ぼうとしています。簡単な例として、データフレームを設定します。

V1<-c(3,2,2,4,5)
V2<-c(3,7,3,5,2)
V3<-c(5,2,5,7,5)
V4<-c(1,1,2,3,4)
V5<-c(1,2,6,7,5)
DF2<-data.frame(V1=V1,V2=V2,V3=V3,V4=V4,V5=V5)
DF2
  V1 V2 V3 V4 V5
1  3  3  5  1  1
2  2  7  2  1  2
3  2  3  5  2  6 
4  4  5  7  3  7
5  5  2  5  4  5

私の目標は、行の値に基づいて行の列を削除するifステートメントを設定することでした。例として:

If V1 = 5, drop column V5
If V1 = 4, drop column V4 & V5

したがって、これらのルールによれば、私のデータフレームは次のようになります。

  V1 V2 V3 V4 V5
1  3  3  5  1  1
2  2  7  2  1  2
3  2  3  5  2  6 
4  4  5  7  
5  5  2  5  4  

私の最初の考えは、これを行うための簡単なifステートメントを書くことができるということでした。

if(DF2$V1==5){
    DF2[-5]
}else if(DF2$V1==4){
    DF2[-4:5]
}

しかし、「if」条件ステートメントを1より大きくすることはできないというエラーが発生しました。したがって、「for」ループを作成すると、ifステートメントが行ごとに移動し、条件付きステートメントが==1になると思いました。

for(i in 1:length(DF2)){
if(DF2$V1==5){
    DF2[-5]
}else if(DF2$V1==4){
    DF2[-4]
}
} 

しかし、今は同じエラーが発生します。x10だけです。だから私は明らかに同じ木を吠えています。だから私の質問は-私の元のデータフレームの質問に対処するための最良の方法は何ですか?そして、ある種のifまたはforループの答えがない場合、なぜこのエラーが発生するのですか?

4

3 に答える 3

14

ifここでは使用すべきではなくif、ソリューションをベクトル化する必要があるため、これは使用に役立ちません。また、定義上、データフレームは等しい長さのベクトルのリストであるため、データフレームに単に穴を開けることはできません。それを文字ベクトルにして、必要な部分を「」に置き換えることができると思いますが、それはおそらく役に立たないでしょう。別のアプローチは、printそれをマトリックスとして使用および印刷し、NAまたは欠落値を表示しないように指示することです。

短編小説:

  1. ベクトル化
  2. 空白ではなくNAに置き換えます
  3. NAを空白として印刷するには、NAを行列に変換し、print関数の引数を使用します

ここにあります...

DF2[DF2$V1==5, 5] <- NA
DF2[DF2$V1==4, 4:5] <- NA
DF2

#If you want blanks printed.
M1 <- as.matrix(DF2)
rownames(M1) <- 1:nrow(M1)
print(M1, na.print="", quote=FALSE)
于 2012-09-16T18:23:25.043 に答える
5

@Tylerのアプローチの方が効率的だと正直に思います---通常のRユーザーにとっては確かにより一般的なアプローチです---しかし、使用ifに固執している場合は、何をしているのかを考えてみてください。

  • data.frame行ごとに処理しています。
  • apply()Rの関数を使用すると、 (行ごとに関数を適用する場合)または(列ごとに関数を適用する場合)のMARGINいずれかとしてを指定できます。12
  • apply()したがって、次のように、各行で使用するための「関数」として条件を設定できます。

    t(apply(DF2, 1, function(x) { if(x[1] == 5) x[5] <- NA;
                                  if(x[1] == 4) x[4:5] <- NA;
                                  x} ))
    #      V1 V2 V3 V4 V5
    # [1,]  3  3  5  1  1
    # [2,]  2  7  2  1  2
    # [3,]  2  3  5  2  6
    # [4,]  4  5  7 NA NA
    # [5,]  5  2  5  4 NA
    

これtは、最後のステップで出力を転置するだけです。

ベンチマーク

いくつかのコメントで効率性の問題が提起されています。小さなデータセットの場合、どの回答でも効率に大きな違いがあるとは思えないので、より大きな(ただし非常に小さな)データセットを使用していくつかのベンチマークを実行しました。

データセットは次のとおりです。

set.seed(1)
DF2 = data.frame(V1 = sample(5, 1000, replace = TRUE),
                 V2 = sample(5, 1000, replace = TRUE),
                 V3 = sample(5, 1000, replace = TRUE),
                 V4 = sample(5, 1000, replace = TRUE),
                 V5 = sample(5, 1000, replace = TRUE))

そして、これがベンチマークと結果を実行するために使用されるコードです。ここでは、タイラーのアプローチがを使用するよりもはるかに高速であることが簡単にわかりますif (...) else if (...)

library(rbenchmark)
benchmark(
  Barranka = {
    for(i in seq(1,nrow(DF2))) {
      if(DF2$V1[i] == 5) {
        DF2[i,5] <- NaN
      } else if(DF2$V1[i] == 4) {
        DF2[i,4] <- NaN
        DF2[i,5] <- NaN
      }
    }},
  Tyler = {
    DF2[DF2$V1==5, 5] <- NA
    DF2[DF2$V1==4, 4:5] <- NA },
  mrdwab = {
    t(apply(DF2, 1, function(x) { if(x[1] == 5) x[5] <- NA;
                                  if(x[1] == 4) x[4:5] <- NA;
                                   x })) },
columns = c("test", "replications", "elapsed", "relative"), 
order = "relative")
#       test replications elapsed relative
# 2    Tyler          100   0.378    1.000
# 3   mrdwab          100   2.072    5.481
# 1 Barranka          100  11.885   31.442

を使用して行数を100000に変更してみたところsystem.time()、タイラーのアプローチと私のアプローチで必要なことを問題なく実行できました。タイラーの経過時間は0.315秒、私の経過時間は2.773秒、バランカの経過時間は807.446秒(13分以上!)でした。それは大きな違いです。

誰かがベンチマークのより良い方法を知っているなら、この投稿を編集して更新してください。

:これは、誰かの特定のアプローチを批判するためではなく、コメントで行われたステートメントの一部を正当化するためにここにあります。私がRについて好き(そして嫌い)なことの1つは、ほとんどの場合、何かをする方法が複数あるということです。

于 2012-09-16T19:00:23.857 に答える
-4

さて、あまり詳細に掘り下げることなくfor、データフレームを反復処理するために、およびif削除を実行するためにを使用する必要があると思います。一方、行サイズが異なるデータフレームを作成することはできないため、目的のエントリをNaN次のように置き換える方が理にかなっています。

V1<-c(3,2,2,4,5)
V2<-c(3,7,3,5,2)
V3<-c(5,2,5,7,5)
V4<-c(1,1,2,3,4)
V5<-c(1,2,6,7,5)
DF2<-data.frame(V1=V1,V2=V2,V3=V3,V4=V4,V5=V5)

'The data frame, before replacing values:'; DF2
for(i in seq(1,nrow(DF2))) {
  if(DF2$V1[i] == 5) {
    DF2[i,5] <- NaN
  } else if(DF2$V1[i] == 4) {
    DF2[i,4] <- NaN
    DF2[i,5] <- NaN
  }
}

'The data frame, after replacing values:'; DF2

このスクリプトを実行すると、次の出力が得られます。

> V1<-c(3,2,2,4,5)
> V2<-c(3,7,3,5,2)
> V3<-c(5,2,5,7,5)
> V4<-c(1,1,2,3,4)
> V5<-c(1,2,6,7,5)
> DF2<-data.frame(V1=V1,V2=V2,V3=V3,V4=V4,V5=V5)
> 
> 'The data frame, before replacing values:'; DF2
[1] "The data frame, before replacing values:"
  V1 V2 V3 V4 V5
1  3  3  5  1  1
2  2  7  2  1  2
3  2  3  5  2  6
4  4  5  7  3  7
5  5  2  5  4  5
> for(i in seq(1,nrow(DF2))) {
+   if(DF2$V1[i] == 5) {
+     DF2[i,5] <- NaN
+   } else if(DF2$V1[i] == 4) {
+     DF2[i,4] <- NaN
+     DF2[i,5] <- NaN
+   }
+ }
> 
> 'The data frame, after replacing values:'; DF2
[1] "The data frame, after replacing values:"
  V1 V2 V3  V4  V5
1  3  3  5   1   1
2  2  7  2   1   2
3  2  3  5   2   6
4  4  5  7 NaN NaN
5  5  2  5   4 NaN

念のため、私が使用している優れたRリファレンスサイトがあります:statmethods.net

これがお役に立てば幸いです

于 2012-09-16T18:25:54.623 に答える