Обновление 2017-08-03
Написав это, Хэдли снова кое-что изменил. Функции, которые раньше находились в purrr, теперь находятся в новом смешанном пакете под названием purrrlyr , описанном как:
purrrlyr содержит некоторые функции, которые лежат на пересечении purrr и dplyr. Они были удалены из purrr, чтобы облегчить упаковку, и потому, что они были заменены другими решениями в tidyverse.
Итак, вам нужно будет установить + загрузить этот пакет, чтобы приведенный ниже код работал.
Исходный пост
Хэдли часто меняет свое мнение о том, что нам следует использовать, но я думаю, что мы должны переключиться на функции в purrr, чтобы получить функциональность по строкам. По крайней мере, они предлагают тот же функционал и почти такой же интерфейс, что и adply
у plyr .
Есть две связанные функции by_row
и invoke_rows
. Насколько я понимаю, вы используете, by_row
когда хотите перебрать строки и добавить результаты в data.frame. 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
если вместо этого мы вернем a 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
param. Есть три варианта: список, строки, столбцы. Когда наш вывод имеет длину 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
Если мы выводим data.frame с 1 строкой, неважно, что мы используем:
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
за исключением того, что во втором столбце .row
вызывается, а в первом нет.
Наконец, если наш вывод длиннее 1 либо как, vector
либо как data.frame
со строками, то имеет значение, используем ли мы строки или столбцы для .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
.
mdply
in dplyr, и Хэдли предположил, что они могут варить что-то на основеdo
. Думаю, здесь тоже сработает.