2017-05-30 58 views
4

我想使用管道mutate語句使用自定義函數。我看起來有點類似SO post但徒勞無功。 說我有這樣一個數據幀(其中blob一些變量不涉及到具體的任務,而是整個數據的一部分):R:逐行dplyr ::使用函數獲取數據幀行並返回一個整數

df <- 
    data.frame(exclude=c('B','B','D'), 
      B=c(1,0,0), 
      C=c(3,4,9), 
      D=c(1,1,0), 
      blob=c('fd', 'fs', 'sa'), 
      stringsAsFactors = F) 

我有一個使用該變量名的功能,所以選擇一些基礎在exclude列中的值和例如計算未在exclude中指定的變量(總是單個字符)的總和。

FUN <- function(df){ 
    sum(df[c('B', 'C', 'D')] [!names(df[c('B', 'C', 'D')]) %in% df['exclude']]) 
} 

當我給出了一個單行(行1)FUN我得到的CD(那些沒有被exclude提及),即4的預期總和:

FUN(df[1,]) 

我如何在具有mutate的管道中做相似的操作(將結果添加到變量s)。這兩種嘗試不起作用:

df %>% mutate(s=FUN(.)) 
df %>% group_by(1:n()) %>% mutate(s=FUN(.)) 

UPDATE 如預期這也不起作用:

df %>% rowwise(.) %>% mutate(s=FUN(.)) 

這個工作的原因,但不是dplyr的mutate內(和管道):

df$s <- sapply(1:nrow(df), function(x) FUN(df[x,])) 
+0

你能舉個例子嗎?這不起作用:'df%>%rowwise(。)%>%mutate(s = FUN(。))''。 @konvas的答案看起來有些複雜(請參閱我對@ konvas的建議的評論) – user3375672

+1

對不起,我誤解了。您可以執行'df%>%rowwise(。)%>%mutate(s = FUN(data.frame(exclude =排除,B = B,C = C,D = D)))'或'df%>%rowwise()%>%nest(排除:D)%> mutate(s = map_dbl(data,FUN)) %>%unnest()'。問題是'dplyr'自然不能在子數據框架上運行,而在列(即向量)上運行。所以一些額外的技巧將是必要的。 – Axeman

+0

這就是我以後的更多! (記錄:它是'tidyr :: nest()'和'purrr :: map_dbl()')。 – user3375672

回答

5

如果你想使用dplyr你可以使用rowwise和你的功能FUN

df %>% 
    rowwise %>% 
    do({ 
     result = as_data_frame(.) 
     result$s = FUN(result) 
     result 
    }) 

同樣可以使用group_by代替rowwise(像你已經嘗試過),但與do代替mutate

df %>% 
    group_by(1:n()) %>% 
    do({ 
     result = as_data_frame(.) 
     result$s = FUN(result) 
     result 
    }) 

原因mutate不會在這種情況下工作,是你實現正在將整個過程傳遞給它,所以這就像調用FUN(df)

儘管做同樣的事情更有效的方法是隻是製作一個包含列的矩陣,然後使用rowSums

cols <- c('B', 'C', 'D') 
include_mat <- outer(function(x, y) x != y, X = df$exclude, Y = cols) 
# or outer(`!=`, X = df$exclude, Y = cols) if it's more readable to you 
df$s <- rowSums(df[cols] * include_mat) 
+0

是的,謝謝,但我上面的例子(總和)只是一個玩具的例子。我的目標是瞭解如何在dplyr管道('%>%')內使用dyprr :: mutate自定義函數(ny)。 do()語句中的建議看起來非常(如您所說)效率低下且有狀態 - 有很多簡單的方法。我也可以做得更有效率(但是這不是用dplyr mutate和pipes):'df $ s < - sapply(1:nrow(df),function(x)FUN(df [x,]))' 。 – user3375672

3

我想你現在解決了這一點,但對於其他人,在這個線程跌倒,我會建議在purrrlyr包使用by_row功能。

在你上面的例子中,我們將使用您的數據幀df並以下列方式用戶定義函數FUN

df %>% 
    by_row(..f = FUN, .to = "s", .collate = "cols") 

這就是它!給你:

# tibble [3 x 6] 
    exclude  B  C  D blob  s 
    <chr> <dbl> <dbl> <dbl> <chr> <dbl> 
1  B  1  3  1 fd  4 
2  B  0  4  1 fs  5 
3  D  0  9  0 sa  9 

誠然,語法是有點怪,但這裏是它的分解:

  • ..f =功能應用到每一行
  • .to =的名稱輸出列,在這種情況下s
  • .collate =結果按列表,行或列排序的方式。由於FUN只有一個輸出,我們將被罰款爲使用"cols""rows"

here的更多信息,使用purrrlyr ...


預警,而我喜歡的功能by_row,它並不總是表現的最佳方法!請參閱以下microbenchmark測試:

mbm <- microbenchmark(
    purrrlyr.test = df %>% by_row(..f = FUN, .to = "s", .collate = "cols"), 
    rowwise.test = df %>% 
    rowwise %>% 
    do({ 
     result = as_data_frame(.) 
     result$s = FUN(result) 
     result 
    }), 
    group_by.test = df %>% 
    group_by(1:n()) %>% 
    do({ 
     result = as_data_frame(.) 
     result$s = FUN(result) 
     result 
    }), 
    sapply.test = {df$s <- sapply(1:nrow(df), function(x) FUN(df[x,]))} 
) 
autoplot(mbm) 

enter image description here

你可以看到purrrlyr方法比使用do組合與rowwisegroup_by(1:n())(見@konvas答案)的方法快,但仍然沒有與sapply方法一樣高效。不同的用戶定義的功能可能會改變速度順序。

相關問題