dplyrを使用してテーブルのすべての行に関数を適用しますか?


121

で作業するとき、すべての行に適用する必要があるスカラー関数plyrに使用すると便利であることがよくありましたadply

例えば

data(iris)
library(plyr)
head(
     adply(iris, 1, transform , Max.Len= max(Sepal.Length,Petal.Length))
    )
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len
1          5.1         3.5          1.4         0.2  setosa     5.1
2          4.9         3.0          1.4         0.2  setosa     4.9
3          4.7         3.2          1.3         0.2  setosa     4.7
4          4.6         3.1          1.5         0.2  setosa     4.6
5          5.0         3.6          1.4         0.2  setosa     5.0
6          5.4         3.9          1.7         0.4  setosa     5.4

dplyr、私はもっと使っています、これを行うきちんとした/自然な方法があるかどうか疑問に思っていますか?これは私が欲しいものではないので:

library(dplyr)
head(
     mutate(iris, Max.Len= max(Sepal.Length,Petal.Length))
    )
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Max.Len
1          5.1         3.5          1.4         0.2  setosa     7.9
2          4.9         3.0          1.4         0.2  setosa     7.9
3          4.7         3.2          1.3         0.2  setosa     7.9
4          4.6         3.1          1.5         0.2  setosa     7.9
5          5.0         3.6          1.4         0.2  setosa     7.9
6          5.4         3.9          1.7         0.4  setosa     7.9

最近mdply、dplyrに相当するものがあるかどうか尋ねたところ、ハドリーはに基づいて何かを醸造しているのではないかと提案しましたdo。ここでも機能すると思います。
バプティスト

4
最終的に、dplyrはrowwise()、個々の行ごとにグループ化されるようなものになります
ハドリー

@hadley thx、adplyグループ化を使わないときと同じように振る舞うべきではありませんか?密接に統合された機能はgroup_byNOT と呼ばれるためsplit_by
スティーブンヘンダーソン

@StephenHendersonいいえ、テーブル全体を操作する方法も必要です。
ハドリー2014

1
@HowYaDoingはい、しかしその方法は一般化しません。たとえば、psum、pmean、pmedianはありません。
スティーブンヘンダーソン

回答:


202

dplyr 0.2(私は思う)の時点でrowwise()実装されているので、この問題の答えは次のようになります。

iris %>% 
  rowwise() %>% 
  mutate(Max.Len= max(Sepal.Length,Petal.Length))

rowwise代替

5年後(!)、この回答には依然として多くのトラフィックが含まれています。それが与えられたのでrowwise、多くの人がそれを直感的に感じるようですが、ますます推奨されません。自分に好意を示し、Rで Jenny BryanのRow指向のワークフローを実行し、整頓された資料を使用してこのトピックを適切に処理してください。

私が見つけた最も簡単な方法は、次を使用したHadleyの例の1つに基づいていpmapます。

iris %>% 
  mutate(Max.Len= purrr::pmap_dbl(list(Sepal.Length, Petal.Length), max))

このアプローチを使用すると、.f内部の関数()に任意の数の引数を与えることができますpmap

pmap これは、行ごとの操作を行っているときに、実際にベクトルのリスト(データフレームの列)からタプルを操作しているという事実を反映しているため、優れた概念的アプローチです。


私はこれを(上記から)理想的な答えに変更しました。
スティーブンヘンダーソン

1
動的に形成されたデータフレームの値を追加することは可能ですか?したがって、このデータフレームでは、列名は不明です。列名がわかっている場合は追加できます。
アルンラジャ

stackoverflow.com/questions/28807266/…ちょうど答えが見つかりました。これでは、合計ではなく相関を使用しています。しかし、同じ概念。
アルンラジャ

13
うまくいかない場合は、実際にdplyr :: mutateを使用していることを確認してください。plyr:: mutateではなく、問題を解決してください
jan-glx

YAKに感謝します。これも私です。plyrdplyrパッケージの両方を含める場合mutate、スコープを明示的に指定しない限り、ほぼ間違いなく間違って使用していますdplyr::mutate
Chris Warth、2015

22

慣用的なアプローチは、適切にベクトル化された関数を作成することです。

R提供pmaxしかし、それはまた提供し、ここに適しているVectorizeのラッパーとしてmapply使用すると、任意の関数のベクトル化任意のバージョンを作成できるようにします。

library(dplyr)
# use base R pmax (vectorized in C)
iris %>% mutate(max.len = pmax(Sepal.Length, Petal.Length))
# use vectorize to create your own function
# for example, a horribly inefficient get first non-Na value function
# a version that is not vectorized
coalesce <- function(a,b) {r <- c(a[1],b[1]); r[!is.na(r)][1]}
# a vectorized version
Coalesce <- Vectorize(coalesce, vectorize.args = c('a','b'))
# some example data
df <- data.frame(a = c(1:5,NA,7:10), b = c(1:3,NA,NA,6,NA,10:8))
df %>% mutate(ab =Coalesce(a,b))

C / C ++でのベクトル化の実装は高速にmagicPonyなりますが、関数を作成するパッケージがないことに注意してください。


THXは、これは、偉大な答えであるあなたが言うような優れた一般的なRスタイル-idiomaticですが、私はそのは本当に存在するかどうか私の質問に対処しないと思うdplyr、それはdplyrなどせずに単純なように...の方法with(df, Coalesce(a,b))だと、おそらく、答えのようなもの-それを使用しないdplyrでください?
スティーブンヘンダーソン

4
magicPonyパッケージがないことを再確認したことを認めなければなりません。
残念

21

行ごとにグループ化する必要があります。

iris %>% group_by(1:n()) %>% mutate(Max.Len= max(Sepal.Length,Petal.Length))

これはで何をし1たかadplyです。


より単純な、または「より良い」構文があるはずです。
スティーブンヘンダーソン

@StephenHenderson、私はdplyr専門家ではないかもしれません。うまくいけば、他の誰かがより良いものをもたらすでしょう。注:で少し整理しました1:n()
BrodieG 2014

私はあなたが正しいと思うが、グループ化のないデフォルトの振る舞いはgroup_by(1:n())ふるまいのようであるべきだと思う。午前中に誰も他のアイデアを持っていない場合は、私はあなたのアイデアをチェックします;)
スティーブンヘンダーソン

また、これはn「この関数はデータソースごとに特別に実装されており、要約内からのみ使用できます。」というドキュメントに多少違反していることに注意してください。
BrodieG 2014

Sepal.LengthとPetal.Lengthを何らかの方法でインデックス番号で参照できますか?変数がたくさんある場合は、便利です。Like ... Max.len = max([c(1,3)])?
Rasmus Larsen

19

2017-08-03を更新

これを書いた後、ハドリーはいくつかのことを再度変更しました。これまでpurrr含まれていた関数は、次に説明するpurrrlyrと呼ばれる新しい混合パッケージに含まれています。

purrrlyrには、purrrとdplyrの交点にあるいくつかの関数が含まれています。パッケージを軽量化するために、また、tidyverseで他のソリューションに置き換えられたため、それらはゴロゴロから削除されました。

したがって、以下のコードを機能させるには、そのパッケージをインストールしてロードする必要があります。

元の投稿

ハドリーは私たちが何を使うべきかについて彼の考えを頻繁に変えます、しかし私は行単位の機能を取得するために私たちはゴロゴロの関数に切り替えることになっていると思います。少なくとも、彼らは同じ機能を提供し、ほぼ同じインタフェース持つadplyからplyrを

関連する関数は2つby_rowありinvoke_rowsます。私の理解では、by_row行をループして結果をdata.frameに追加するときに使用します。invoke_rowsdata.frameの行をループして、各colを引数として関数に渡すときに使用されます。最初のものだけを使用します。

library(tidyverse)

iris %>% 
  by_row(..f = function(this_row) {
    browser()
  })

これにより、内部を確認できます(つまり、何を行っているかを確認できます)。これは、を使用した場合と同じadplyです。

Called from: ..f(.d[[i]], ...)
Browse[1]> this_row
# A tibble: 1 × 5
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
         <dbl>       <dbl>        <dbl>       <dbl>  <fctr>
1          5.1         3.5          1.4         0.2  setosa
Browse[1]> Q

デフォルトでby_rowは、出力に基づいてリスト列を追加します。

iris %>% 
  by_row(..f = function(this_row) {
      this_row[1:4] %>% unlist %>% mean
  })

与える:

# A tibble: 150 × 6
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species      .out
          <dbl>       <dbl>        <dbl>       <dbl>  <fctr>    <list>
1           5.1         3.5          1.4         0.2  setosa <dbl [1]>
2           4.9         3.0          1.4         0.2  setosa <dbl [1]>
3           4.7         3.2          1.3         0.2  setosa <dbl [1]>
4           4.6         3.1          1.5         0.2  setosa <dbl [1]>
5           5.0         3.6          1.4         0.2  setosa <dbl [1]>
6           5.4         3.9          1.7         0.4  setosa <dbl [1]>
7           4.6         3.4          1.4         0.3  setosa <dbl [1]>
8           5.0         3.4          1.5         0.2  setosa <dbl [1]>
9           4.4         2.9          1.4         0.2  setosa <dbl [1]>
10          4.9         3.1          1.5         0.1  setosa <dbl [1]>
# ... with 140 more rows

代わりにを返す場合data.framedata.framesを含むリストを取得します。

iris %>% 
  by_row( ..f = function(this_row) {
    data.frame(
      new_col_mean = this_row[1:4] %>% unlist %>% mean,
      new_col_median = this_row[1:4] %>% unlist %>% median
    )
  })

与える:

# A tibble: 150 × 6
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species                 .out
          <dbl>       <dbl>        <dbl>       <dbl>  <fctr>               <list>
1           5.1         3.5          1.4         0.2  setosa <data.frame [1 × 2]>
2           4.9         3.0          1.4         0.2  setosa <data.frame [1 × 2]>
3           4.7         3.2          1.3         0.2  setosa <data.frame [1 × 2]>
4           4.6         3.1          1.5         0.2  setosa <data.frame [1 × 2]>
5           5.0         3.6          1.4         0.2  setosa <data.frame [1 × 2]>
6           5.4         3.9          1.7         0.4  setosa <data.frame [1 × 2]>
7           4.6         3.4          1.4         0.3  setosa <data.frame [1 × 2]>
8           5.0         3.4          1.5         0.2  setosa <data.frame [1 × 2]>
9           4.4         2.9          1.4         0.2  setosa <data.frame [1 × 2]>
10          4.9         3.1          1.5         0.1  setosa <data.frame [1 × 2]>
# ... with 140 more rows

関数の出力を追加する方法は、.collateparam によって制御されます。リスト、行、列の3つのオプションがあります。出力の長さが1の場合、行と列のどちらを使用しても問題ありません。

iris %>% 
  by_row(.collate = "cols", ..f = function(this_row) {
    this_row[1:4] %>% unlist %>% mean
  })

iris %>% 
  by_row(.collate = "rows", ..f = function(this_row) {
    this_row[1:4] %>% unlist %>% mean
  })

両方が生成します:

# A tibble: 150 × 6
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species  .out
          <dbl>       <dbl>        <dbl>       <dbl>  <fctr> <dbl>
1           5.1         3.5          1.4         0.2  setosa 2.550
2           4.9         3.0          1.4         0.2  setosa 2.375
3           4.7         3.2          1.3         0.2  setosa 2.350
4           4.6         3.1          1.5         0.2  setosa 2.350
5           5.0         3.6          1.4         0.2  setosa 2.550
6           5.4         3.9          1.7         0.4  setosa 2.850
7           4.6         3.4          1.4         0.3  setosa 2.425
8           5.0         3.4          1.5         0.2  setosa 2.525
9           4.4         2.9          1.4         0.2  setosa 2.225
10          4.9         3.1          1.5         0.1  setosa 2.400
# ... with 140 more rows

1行のdata.frameを出力する場合、どちらを使用するかはほんのわずかに重要です。

iris %>% 
  by_row(.collate = "cols", ..f = function(this_row) {
    data.frame(
      new_col_mean = this_row[1:4] %>% unlist %>% mean,
      new_col_median = this_row[1:4] %>% unlist %>% median
      )
  })

iris %>% 
  by_row(.collate = "rows", ..f = function(this_row) {
    data.frame(
      new_col_mean = this_row[1:4] %>% unlist %>% mean,
      new_col_median = this_row[1:4] %>% unlist %>% median
    )
  })

両方が与える:

# A tibble: 150 × 8
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species  .row new_col_mean new_col_median
          <dbl>       <dbl>        <dbl>       <dbl>  <fctr> <int>        <dbl>          <dbl>
1           5.1         3.5          1.4         0.2  setosa     1        2.550           2.45
2           4.9         3.0          1.4         0.2  setosa     2        2.375           2.20
3           4.7         3.2          1.3         0.2  setosa     3        2.350           2.25
4           4.6         3.1          1.5         0.2  setosa     4        2.350           2.30
5           5.0         3.6          1.4         0.2  setosa     5        2.550           2.50
6           5.4         3.9          1.7         0.4  setosa     6        2.850           2.80
7           4.6         3.4          1.4         0.3  setosa     7        2.425           2.40
8           5.0         3.4          1.5         0.2  setosa     8        2.525           2.45
9           4.4         2.9          1.4         0.2  setosa     9        2.225           2.15
10          4.9         3.1          1.5         0.1  setosa    10        2.400           2.30
# ... with 140 more rows

2番目の列には呼び出された列が.rowあり、最初の列にはありません

最後に、出力がa vectorまたはa data.framewith a 行のいずれかとして長さ1より長い場合、行または列を使用するかどうかが重要です.collate

mtcars[1:2] %>% by_row(function(x) 1:5)
mtcars[1:2] %>% by_row(function(x) 1:5, .collate = "rows")
mtcars[1:2] %>% by_row(function(x) 1:5, .collate = "cols")

それぞれ生成します:

# A tibble: 32 × 3
     mpg   cyl      .out
   <dbl> <dbl>    <list>
1   21.0     6 <int [5]>
2   21.0     6 <int [5]>
3   22.8     4 <int [5]>
4   21.4     6 <int [5]>
5   18.7     8 <int [5]>
6   18.1     6 <int [5]>
7   14.3     8 <int [5]>
8   24.4     4 <int [5]>
9   22.8     4 <int [5]>
10  19.2     6 <int [5]>
# ... with 22 more rows

# A tibble: 160 × 4
     mpg   cyl  .row  .out
   <dbl> <dbl> <int> <int>
1     21     6     1     1
2     21     6     1     2
3     21     6     1     3
4     21     6     1     4
5     21     6     1     5
6     21     6     2     1
7     21     6     2     2
8     21     6     2     3
9     21     6     2     4
10    21     6     2     5
# ... with 150 more rows

# A tibble: 32 × 7
     mpg   cyl .out1 .out2 .out3 .out4 .out5
   <dbl> <dbl> <int> <int> <int> <int> <int>
1   21.0     6     1     2     3     4     5
2   21.0     6     1     2     3     4     5
3   22.8     4     1     2     3     4     5
4   21.4     6     1     2     3     4     5
5   18.7     8     1     2     3     4     5
6   18.1     6     1     2     3     4     5
7   14.3     8     1     2     3     4     5
8   24.4     4     1     2     3     4     5
9   22.8     4     1     2     3     4     5
10  19.2     6     1     2     3     4     5
# ... with 22 more rows

つまり、一番下の行。必要に応じてadply(.margins = 1, ...)機能を、あなたが使用することができますby_row


2
by_row:それは"; dplyr ::のmutate()purrr ::マップ()tidyr ::巣()の組み合わせを使用する"と言う呼び出して、推奨されませんgithub.com/hadley/purrrlyr/blob/...
momeara

それはたくさんのrです。
qwr

14

BrodieGの答えを拡張して、

関数が返す複数の行場合、代わりにmutate()do()使用しなければなりません。次に、それを元に戻すにrbind_all()は、dplyrパッケージから使用します。

dplyr、バージョンdplyr_0.1.2、使用1:n()中にgroup_by()句は、私のために動作しません。うまくいけば、ハドリーはrowwise()すぐに実装されます。

iris %>%
    group_by(1:nrow(iris)) %>%
    do(do_fn) %>%
    rbind_all()

パフォーマンスのテスト

library(plyr)    # plyr_1.8.4.9000
library(dplyr)   # dplyr_0.8.0.9000
library(purrr)   # purrr_0.2.99.9000
library(microbenchmark)

d1_count <- 1000
d2_count <- 10

d1 <- data.frame(a=runif(d1_count))

do_fn <- function(row){data.frame(a=row$a, b=runif(d2_count))}
do_fn2 <- function(a){data.frame(a=a, b=runif(d2_count))}

op <- microbenchmark(
        plyr_version = plyr::adply(d1, 1, do_fn),
        dplyr_version = d1 %>%
            dplyr::group_by(1:nrow(d1)) %>%
            dplyr::do(do_fn(.)) %>%
            dplyr::bind_rows(),
        purrr_version = d1 %>% purrr::pmap_dfr(do_fn2),
        times=50)

次の結果になります。

Unit: milliseconds
          expr       min        lq      mean    median        uq       max neval
  plyr_version 1227.2589 1275.1363 1317.3431 1293.5759 1314.4266 1616.5449    50
 dplyr_version  977.3025 1012.6340 1035.9436 1025.6267 1040.5882 1449.0978    50
 purrr_version  609.5790  629.7565  643.8498  644.2505  656.1959  686.8128    50

これは、新しい purrrバージョンが最速であるいます


1

このようなもの?

iris$Max.Len <- pmax(iris$Sepal.Length, iris$Petal.Length)

1
はいthx、それは非常に具体的な答えです。しかし、私の例と質問はdplyr、スカラー関数の一般的な解決策があるかどうかを明らかにしようとしています。
スティーブンヘンダーソン

一般に、関数はベクトル化する必要があります-風変わりな関数の場合は、を記述wacky.function <- function(col.1, col.2){...}してからiris.wacky <- wacky.function(iris$Sepal.Length, iris$Petal.Length)
colcarroll 2014

多くの場合、私は推測する必要がありますが、のようなものを使用しているときdplyrplyrまたはdata.tableそれらのイディオムを使用して、スタイルが混在するコードを共有することが困難にならないようにする必要があると思います。したがって問題。
スティーブンヘンダーソン

plyrドキュメントの最初の行は、「plyrは一般的な一連の問題を解決するツールのセットです。大きな問題を管理可能な部分に分割し、各部分を操作してから、すべての部分を元に戻す必要があります。」これは、基本的な列操作が最適なツールである非常に異なる問題のようです。これは、これを行うための「自然な」plyr/ dplyrコマンドがない理由も説明します。
colcarroll 2014

5
有名な引用を屠殺するために:「手持ちがすべてプライヤである場合、ハンマーとスクリュードライバーにもそれを使用することになります
thelatemail
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.