1

私はしばしば、特定の条件、通常は特定の値を持つ固定数のフィールドに基づいて、少数のルールベースの変換をデータフレームに適用する必要があることに気付きます。変換により、任意の数の列(通常は1〜3)を変更できます。これらの変換に関係する行の数は、データフレームの行の総数と比較して少ないです。現在使用していますが、すべての行を変更するddplyため、パフォーマンスが不足しています。ddply

変更する必要のある行の数が少ないという事実を利用して、この問題をエレガントで一般的な方法で解決する方法を探しています。以下は、私が扱っている変換のタイプの簡単な例です。

df <- data.frame(Product=gl(4,10,labels=c("A","B", "C", "D")), 
                 Year=sort(rep(2002:2011,4)), 
                 Quarter=rep(c("Q1","Q2", "Q3", "Q4"), 10), 
                 Sales=1:40)           
> head(df)
  Product Year Quarter Sales
1       A 2002      Q1     1
2       A 2002      Q2     2
3       A 2002      Q3     3
4       A 2002      Q4     4
5       A 2003      Q1     5
6       A 2003      Q2     6
> 
transformations <- function(df) {
    if (df$Year == 2002 && df$Product == 'A') {
        df$Sales <- df$Sales + 3
    } else if (df$Year == 2009 && df$Product == 'C') {
        df$Sales <- df$Sales - 10
        df$Product <- 'E'
    }
    df
}

library(plyr)
df <- ddply(df, .(Product, Year), transformations)

> head(df)
  Product Year Quarter Sales
1       A 2002      Q1     4
2       A 2002      Q2     5
3       A 2002      Q3     6
4       A 2002      Q4     7
5       A 2003      Q1     5
6       A 2003      Q2     6

ハードコードされた条件式の代わりに、条件付き関数と変換関数のペアリストを使用しています。たとえば、以下のコードですが、これは意味のある改善ではありません。

transformation_rules <- list(
  list(
    condition = function(df) df$Year == 2002 && df$Product == 'A',
    transformation = function(df) {
      df$Sales <- df$Sales + 3
      df
    }
  )
)

この問題に取り組むためのより良い方法は何ですか?

4

1 に答える 1

2

plyrこの問題に使う必要はまったくないと思います。ifelse()Rがベクトル化されているという事実を簡単に利用して、同じ結果を得ることができると思います。

関数が列を直接変更するので、Salesplyrを実行する前にそのようにコピーしましたdf2 <- df。また、私の例では、列Sales2を上書きする代わりに新しい列を作成しましたSales

そして、次のように関数を書き直しました。

df2$Sales2 <- with(df2, ifelse(Year == 2002 & Product == "A", Sales + 3,
                        ifelse(Year == 2009 & Product == "C", Sales - 10, Sales)))

そして、出力が等しいかどうかをテストします。

> all.equal(df$Sales, df2$Sales2)
[1] TRUE

また、2つのシステムのタイミングを比較すると、ddplyを回避するベクトル化バージョンの方がはるかに高速であることがわかります。

> system.time(df <- ddply(df, .(Product, Year), transformations))
   user  system elapsed 
  0.012   0.000   0.012 
> system.time(df2$Sales2 <- with(df2, ifelse(Year == 2002 & Product == "A", Sales + 3,
+                         ifelse(Year == 2009 & Product == "C", Sales - 10, Sales))))
   user  system elapsed 
      0       0       0 

したがって、何かが足りない場合を除いて、plyrここですべてを一緒に回避して、速度を大幅に向上させることができます。ifelse()遅すぎることが判明した場合は、ブール関数を記述してさらに高速化することができますが、それが必要になるかどうかは疑問です。

于 2012-07-04T06:21:49.657 に答える