web-dev-qa-db-fra.com

Itération en ligne comme appliquer avec purrr

Comment réaliser une itération ligne par ligne en utilisant purrr :: map?

Voici comment je le ferais avec une application standard par ligne.

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))
          })

Cependant, ce n'est pas trop élégant, et je préfère le faire avec du ronronnement. Peut (ou non) être plus rapide aussi.

29
matsuo_basho

Vous pouvez utiliser pmap pour une itération ligne par ligne. Les colonnes sont utilisées comme arguments de la fonction que vous utilisez. Dans votre exemple, vous auriez une fonction à trois arguments.

Par exemple, voici pmap utilisant une fonction anonyme pour le travail que vous faites. Les colonnes sont transmises à la fonction dans l'ordre dans lequel elles se trouvent dans l'ensemble de données.

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

Vous pouvez utiliser le purrr tilde "short-hand" pour une fonction anonyme en vous référant aux colonnes dans l'ordre avec des nombres précédés de deux points.

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

Si vous souhaitez obtenir ces résultats particuliers en tant que data.frame au lieu d'une liste, vous pouvez utiliser pmap_dfr.

36
aosmith

Notez que vous n'utilisez que des opérations vectorisées dans votre exemple, vous pouvez donc très bien faire:

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

(ou en base R: transform(df,var1 = a+b,var2 = c/2)[4:5])

Si vous utilisez des fonctions non vectorisées telles que la médiane, vous pouvez utiliser pmap comme dans la réponse de @aosmith, ou utiliser dplyr::rowwise.

rowwise est plus lent et les responsables du paquet conseillent d'utiliser la famille map à la place, mais c'est sans doute plus agréable à l'œil que pmap dans certains cas. Personnellement, je l'utilise toujours lorsque la vitesse n'est pas un problème:

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

(pour revenir à une sortie de liste sans nom stricte: res %>% split(seq(nrow(.))) %>% unname)

7
Moody_Mudskipper

Vous êtes libre de toujours créer un wrapper autour d'une fonction que vous "aimez".

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,...)
}

appliquer la nouvelle fonction rmap (r owwise map)

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

Informations supplémentaires: (eval de haut en bas)

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