2012-07-04 94 views
1

我經常發現自己需要基於某些條件將少量基於規則的轉換應用於數據幀,通常是具有特定值的固定數量的字段。轉換可以修改任意數量的列,通常是一到三個。與數據幀中的總行數相比,這些轉換涉及的行數很少。目前我正在使用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 

硬編碼的條件句insted的我使用的條件和轉換功能成對列表,例如,下面的代碼,但是這不是一個有意義的改善。

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

有什麼更好的方法來解決這個問題?

回答

2

我不認爲你需要使用plyr來解決這個問題。我想你可以簡單地使用ifelse()並利用R矢量化並獲得相同結果的事實。

由於您的函數直接修改Sales列,所以在運行plyr之前,我製作了它的副本: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 

,並比較了矢量版本,避免兩個節目之間的系統時間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 

所以,除非我失蹤somethi ng - 你可以在這裏一起避免plyr,並獲得一些不錯的速度提升。如果ifelse()證明速度太慢,你可以寫出一些布爾函數來更快,但我懷疑這是必要的。

+0

Chase,修改Sales列的例子就是這樣 - 一個例子。實際上,我需要時常修改幾列。我已經更新了這個問題來反映這一點。你會建議重複ifelse()條件嗎? – Sim