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
を使用していますが、これを行うための整頓された自然な方法があるかどうか疑問に思っていますか?これはNOT私が欲しいものです:
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
Dplyr 0.2(と思う)rowwise()
が実装されているため、この問題に対する答えは次のようになります。
iris %>%
rowwise() %>%
mutate(Max.Len= max(Sepal.Length,Petal.Length))
rowwise
代替5年(!)後も、この回答にはまだ多くのトラフィックがあります。多くの人が直感的に理解しているようですが、rowwise
は推奨されていません。このトピックをうまく理解するには、Jenny Bryanの 整頓されたRでの行指向のワークフロー 資料を参考にしてください。
私が見つけた最も簡単な方法は、pmap
を使用したHadleyの例の1つに基づいています。
iris %>%
mutate(Max.Len= purrr::pmap_dbl(list(Sepal.Length, Petal.Length), max))
このアプローチを使用すると、pmap
内の関数(.f
)に任意の数の引数を与えることができます。
pmap
は、行単位の操作を実行しているとき、ベクトルのリスト(データフレーム内の列)から実際にタプルを操作しているという事実を反映しているため、優れた概念的アプローチです。
慣用的なアプローチは、適切にベクトル化された関数を作成することです。
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
パッケージはありません。
行ごとにグループ化する必要があります。
iris %>% group_by(1:n()) %>% mutate(Max.Len= max(Sepal.Length,Petal.Length))
これは、1
がadply
で行ったことです。
これを書いた後、ハドリーは再びいくつかを変更しました。 purrrにあった関数は、 と呼ばれる新しい混合パッケージpurrrlyr にあります。 :
purrrlyrには、purrrとdplyrの交差点にあるいくつかの関数が含まれています。パッケージを軽くするために、また整頓された他のソリューションに置き換えられたため、これらはpurrrから削除されました。
そのため、以下のコードを機能させるには、そのパッケージをインストールしてロードする必要があります。
ハドリーは使用するものについて頻繁に考えを変えますが、行ごとの機能を取得するには、purrrの関数に切り替える必要があると思います。少なくとも、これらは同じ機能を提供し、plyrのadply
とほぼ同じインターフェースを備えています。
by_row
とinvoke_rows
の2つの関連関数があります。私の理解では、行をループして結果をdata.frameに追加する場合は、by_row
を使用します。 invoke_rows
は、data.frameの行をループし、各列を関数の引数として渡すときに使用されます。最初のもののみを使用します。
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.frame
を返す場合、data.frame
sのリストを取得します。
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
関数の出力を追加する方法は、.collate
パラメーターによって制御されます。リスト、行、列の3つのオプションがあります。出力の長さが1の場合、rowsまたはcolsのどちらを使用してもかまいません。
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
という列があり、最初の列にはありません。
最後に、vector
または行を含むdata.frame
として出力が長さ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
を使用できます。
BrodieGの答えを拡張し、
関数が複数の行を返す場合、mutate()
の代わりに、do()
を使用する必要があります。それを元に戻すには、dplyr
パッケージのrbind_all()
を使用します。
dplyr
バージョンdplyr_0.1.2
では、1:n()
句でgroup_by()
を使用しても機能しません。うまくいけば Hadleyは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
バージョンが最速であることを示しています
このようなもの?
iris$Max.Len <- pmax(iris$Sepal.Length, iris$Petal.Length)