작업 할 때 각 행에 적용해야하는 스칼라 함수 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
답변
dplyr 0.2 (내 생각에) rowwise()
가 구현 되었으므로이 문제에 대한 대답은 다음과 같습니다.
iris %>%
rowwise() %>%
mutate(Max.Len= max(Sepal.Length,Petal.Length))
비 rowwise
대안
5 년 (!) 후에도이 답변은 여전히 많은 트래픽을 얻습니다. 주어진 이후로 rowwise
많은 사람들이 직관적이라고 생각하지만 점점 권장되지 않습니다. 이 주제를 잘 다루기 위해 R에서 제니 브라이언의 행 중심 워크 플로우를 깔끔한 자료 와 함께 살펴보십시오.
내가 찾은 가장 간단한 방법은 다음을 사용하는 Hadley의 예제 중 하나를 기반으로합니다 pmap
.
iris %>%
mutate(Max.Len= purrr::pmap_dbl(list(Sepal.Length, Petal.Length), max))
이 접근 방식을 사용하면 .f
내부 함수 ( )에 임의의 수의 인수를 제공 할 수 있습니다 pmap
.
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
.
답변
업데이트 2017-08-03
이 글을 쓴 후 Hadley는 몇 가지 사항을 다시 변경했습니다. purrr에 있던 함수는 이제 purrrlyr 라는 새로운 혼합 패키지에 있습니다.
purrrlyr에는 purrr과 dplyr의 교차점에있는 일부 함수가 포함되어 있습니다. 패키지를 더 가볍게 만들고 tidyverse의 다른 솔루션으로 대체 되었기 때문에 purrr에서 제거되었습니다.
따라서 아래 코드가 작동하도록하려면 해당 패키지를 설치하고로드해야합니다.
원본 게시물
Hadley는 우리가 무엇을 사용해야하는지에 대해 자주 생각을 바꾸지 만, 행별 기능을 얻으려면 purrr 의 기능으로 전환해야한다고 생각 합니다. 적어도 그들은 동일한 기능을 제공하고 plyradply
에서 와 거의 동일한 인터페이스 를 가지고 있습니다.
두 가지 관련 기능은, 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
대신를 반환하면 s data.frame
가있는 목록을 얻습니다 data.frame
.
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
매개 변수에 의해 제어됩니다 . 목록, 행, 열의 세 가지 옵션이 있습니다. 출력의 길이가 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
두 번째는 호출 된 열이 .row
있고 첫 번째는 호출 되지 않는 것을 제외하고 .
마지막으로, 출력이 a vector
또는 a 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()
사용되어야한다. 그런 다음 다시 결합 rbind_all()
하려면 dplyr
패키지 에서 사용 하십시오 .
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)