web-dev-qa-db-ja.com

purrrで適用するような行ごとの反復

Purrr :: mapを使用して行ごとの反復を実現するにはどうすればよいですか?

標準的な行単位の適用でこれを行う方法は次のとおりです。

df <- data.frame(a = 1:10, b = 11:20, c = 21:30)

lst_result <- apply(df, 1, function(x){
            var1 <- (x[['a']] + x[['b']])
            var2 <- x[['c']]/2
            return(data.frame(var1 = var1, var2 = var2))
          })

しかし、これはあまりにもエレガントではなく、私はむしろpurrrでやりたいです。より速くなる場合もあれば、そうでない場合もあります。

29
matsuo_basho

行ごとの反復にはpmapを使用できます。列は、使用している関数の引数として使用されます。この例では、引数が3つの関数を使用します。

たとえば、あなたがしている作業に匿名関数を使用しているpmapは次のとおりです。列は、データセットにある順序で関数に渡されます。

pmap(df, function(a, b, c) {
     data.frame(var1 = a + b,
                var2 = c/2) 
     }  ) 

purrrチルダ「ショートハンド」を使用して、2つのドットが前に付いた数字で順番に列を参照することにより、匿名関数を使用できます。

pmap(df, ~data.frame(var1 = ..1 + ..2,
                var2 = ..3/2)  ) 

これらの特定の結果をリストではなくdata.frameとして取得する場合は、pmap_dfrを使用できます。

36
aosmith

あなたの例ではベクトル化された操作のみを使用していることに注意してください。

_df %>% dplyr::transmute(var1 = a+b,var2 = c/2)
_

(またはベースR:transform(df,var1 = a+b,var2 = c/2)[4:5]

中央値などの非ベクトル化関数を使用する場合は、@ aosmithの答えのようにpmapを使用するか、_dplyr::rowwise_を使用できます。

rowwiseは低速であり、パッケージメンテナは代わりにmapファミリを使用することを推奨しますが、場合によってはpmapよりも間違いなく簡単です。私は個人的には速度が問題にならないときにそれをまだ使用しています:

_library(dplyr)
df %>% transmute(var3 = pmap(.,~median(c(..1,..2,..3))))
df %>% rowwise %>% transmute(var3 = median(c(a,b,c)))
_

(厳密な名前のないリスト出力に戻るには、res %>% split(seq(nrow(.))) %>% unname

7

「好きな」関数のラッパーをいつでも自由に作成できます。

rmap <- function (.x, .f, ...) {
    if(is.null(dim(.x))) stop("dim(X) must have a positive length")
    .x <- t(.x) %>% as.data.frame(.,stringsAsFactors=F)
    purrr::map(.x=.x,.f=.f,...)
}

新しい関数rmapを適用する(r owwise map

rmap(df1,~{
    var1 <- (.x[[1]] + .x[[2]])
    var2 <- .x[[3]]/2
    return(data.frame(var1 = var1, var2 = var2))
    })

追加情報:(上から下に評価)

df1 <- data.frame(a=1:3,b=1:3,c=1:3)
m   <- matrix(1:9,ncol=3)

apply(df1,1,sum)
rmap(df1,sum)

apply(m,1,sum)
rmap(m,sum)

apply(1:10,1,sum)  # intentionally throws an error
rmap(1:10,sum)     # intentionally throws an error
3
Andre Elrico