2017-05-30 6 views
4

カスタム関数を使用してパイプのmutate文を使用しようとしています。私はこれを幾分似たように見たが、無駄にSO postを見た。R:データフレームの行をとり、整数を返す関数を使用して行ごとdplyr :: mutate

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) 

私は変数名を使用する機能は非常に一部基づいて選択した: は、私は(blobが特定のタスクに関連しないいくつかの変数であるが、全体データの一部である)、このようなデータフレームがあるとexclude列の値と、たとえばexcludeで指定されていない変数(常に1文字)の合計を計算します。どのように私を行う

FUN(df[1,]) 

:私はFUNに単一の行(行1)を与えるとき

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

は、私は、すなわち4 CDexcludeで言及されていないもの)は、予想される合計を取得します同様に、mutateを持つパイプで行います(結果を変数sに追加します)。これらの二つの試みは動作しません:意図したとおりこれも動作しません

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

UPDATE:

:これが原因で動作しますが、dplyrさんのmutate内にない(パイプ)

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

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

例を挙げてください。これはうまくいかない: 'df%>%rowwise(。)%>%mutate(s = FUN(。))'。 @konvasからの答えは、幾分かっこいいです(@ konvasの提案を参考にしてください) – user3375672

+1

申し訳ありませんが、誤解されています。%d>%rowwise(。)%>%mutate(s = FUN(data.frame(exclude = %nest(除外:D)%>%変異(s = map_dbl(データ、FUN)) %>%unnest() '問題は、「dplyr」はサブデータフレームではなく、列(すなわちベクトル)上で自然に動作することである。したがって、いくつかの追加のトリッキーが必要になります。 – Axeman

+0

これは私が後にしているものです! (記録のためには 'tidyr :: nest()'と 'purrr :: map_dbl()')です。 – user3375672

答えて

5

dplyrを使用する場合は、rowwiseとその機能を使用してください。

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

同じことがgroup_by代わりのrowwiseを(あなたはすでに試したように)使用して達成することができるが、代わりにmutate

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

doと理由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

はい、ありがとうございます。しかし、私の例(sum)は単なるおもちゃの例でした。私の目的は、dplyrパイプ( '%>%')内のdplyr :: mutateで(ny)カスタム関数を使用する方法を理解することです。 do()ステートメント内の提案は、非常に(あなたが言うように)非効率的でステートフルなようです - もっと簡単な方法があります。 '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"

は、purrrlyrの使用方法の詳細についてhere ...


事前警告を参照してください。 by_row、それは常にパフォーマンスのための最良のアプローチではありません!あなたは(@konvas答えを参照)purrrlyrアプローチはrowwiseまたはgroup_by(1:n())doの組み合わせを使用してのアプローチよりも高速であることがわかり、まだできません

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

:以下microbenchmarkのテストを参照してください。 sapplyアプローチと同じくらい効率的です。さまざまなユーザー定義関数が速度の順序を変更することがあります。

関連する問題