2
votes

Appliquer `dplyr :: rowwise` dans toutes les variables

J'ai une donnée:

Source: local data frame [30 x 5]
Groups: <by row>

# A tibble: 30 x 5
     x.1   x.2   x.3   x.4   var
   <dbl> <dbl> <dbl> <dbl> <dbl>
 1  32.7  42.7  50.1  20.8 7091.
 2  75.9  71.3  83.6  77.6 7091.
 3  49.6  28.7  97.0  59.7 7091.
 4  47.4  96.1  31.9  79.7 7091.
 5  54.2  47.1  81.7  41.6 7091.
 6  27.9  58.1  97.4  25.9 7091.
 7  61.8  78.3  52.6  67.7 7091.
 8  85.4  51.3  38.8  82.0 7091.
 9  27.9  72.6  68.9  25.2 7091.
10  87.2  42.1  27.6  73.9 7091.
# ... with 20 more rows

La fonction suivante fonctionne:

df_1 %>% 
  select(-y) %>% 
  rowwise() %>% 
  mutate(var = sum(select_if(., is.numeric)))

Mais, les fonctions suivantes (pour toutes les variables) ne fonctionnent pas ne fonctionne pas:

avec .:

df_1 %>% 
  select(-y) %>% 
  rowwise() %>% 
  mutate(var = sum(.))

avec select_if :

library(tidyverse)

df_1 %>% 
  select(-y) %>% 
  rowwise() %>% 
  mutate(var = sum(c(x.1, x.3)))

Les deux méthodes renvoient:

df_1 <- data.frame(
  x = replicate(4, runif(30, 20, 100)), 
  y = sample(1:3, 30, replace = TRUE)
)

7091 est une somme incorrecte.

Comment l'ajustement fonctionne-t-il?


7 commentaires

Vous pouvez utiliser rowSums : df_1%>% mutate (var = rowSums (select (., Starts_with ('x.'))))


@IceCreamToucan, j'ai besoin d'utiliser rowwise .


cette répond à votre question?


Ça ne marche pas. J'ai besoin d'une somme pour chaque cas (ligne) pour toutes les variables.


Peut être fait en utilisant le package purrr : df_1%>% select (-y)%>% mutate (var = pmap (., Lift (sum)))


Et signifie ou sd ?


@GiovaniNeves: C'est la même chose pour mean et sd . Utilisez simplement un autre lifter de domaine: df_1%>% select (-y)%>% mutate (var = pmap (., Lift_vd (mean)))


4 Réponses :


1
votes

Je pense que c'est délicat parce que les variantes de portée de mutate ( mutate_at , mutate_all , mutate_if ) visent généralement à exécuter une fonction sur une colonne spécifique, au lieu de créer une opération qui utilise toutes les colonnes.

La solution la plus simple que je puisse trouver consiste essentiellement à créer un vecteur ( cols ) qui est ensuite utilisé pour exécuter l'opération récapitulative :

library(dplyr)
library(purrr)

df_1 <- data.frame(
  x = replicate(4, runif(30, 20, 100)), 
  y = sample(1:3, 30, replace = TRUE)
)

# create vector of columns to operate on
cols <- names(df_1)
cols <- cols[map_lgl(df_1, is.numeric)]
cols <- cols[! cols %in% c("y")]

cols
#> [1] "x.1" "x.2" "x.3" "x.4"

df_1 %>% 
  select(-y) %>% 
  rowwise() %>% 
  mutate(
    var = sum(!!!map(cols, as.name), na.rm = TRUE)
  )
#> Source: local data frame [30 x 5]
#> Groups: <by row>
#> 
#> # A tibble: 30 x 5
#>      x.1   x.2   x.3   x.4   var
#>    <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1  46.1  28.9  28.9  50.7  155.
#>  2  26.8  68.0  67.1  26.5  188.
#>  3  35.2  63.8  62.5  28.5  190.
#>  4  31.3  44.9  67.3  68.2  212.
#>  5  52.6  23.9  83.2  43.4  203.
#>  6  55.7  92.8  86.3  57.2  292.
#>  7  56.9  50.0  77.6  25.6  210.
#>  8  95.0  82.6  86.1  22.7  286.
#>  9  62.7  26.5  61.0  88.9  239.
#> 10  65.2  23.1  25.5  51.0  165.
#> # … with 20 more rows

Créé le 30/04/2019 par package reprex (v0.2.1)

REMARQUE: si vous n'êtes pas familier avec purrr , vous pouvez également utiliser quelque chose comme lapply code>, etc.

Vous pouvez en savoir plus sur ces types d'opérations dplyr plus délicates ( !! , !!! code>, etc.) ici:

https: //dplyr.tidyverse .org / articles / programmation.html


0 commentaires

2
votes

Quelques approches que j'ai adoptées dans le passé:

  • utiliser une fonction préexistante par ligne (par exemple, rowSums )
  • en utilisant réduire (qui ne s'applique pas à toutes les fonctions)
  • transposition compliquée
  • fonction personnalisée avec pmap

Utilisation de fonctions préexistantes au niveau des lignes

my_var <- function(...){
  vec <-  c(...)
  var(vec)
}

df_1 %>%
  mutate(var = select(., -y) %>% pmap(my_var)) %>%
  head()
#>        x.1      x.2      x.3      x.4 y      var
#> 1 41.24069 58.56641 93.03007 39.17035 3 620.9523
#> 2 49.76991 67.96527 43.48827 24.71475 2 318.3722
#> 3 65.82827 59.48330 56.72526 71.38306 2 43.17011
#> 4 92.65662 34.89741 46.59157 90.10154 1 878.5009
#> 5 36.13455 86.18987 72.06964 82.31317 3 520.7224
#> 6 91.87117 73.47734 40.64134 83.78471 2 506.1679

Utilisation de Reduce

df_1 %>%
  mutate(var = select(., -y) %>% as.matrix %>% t %>% as.data.frame %>% map_dbl(var)) %>%
  head()
#>        x.1      x.2      x.3      x.4 y       var
#> 1 41.24069 58.56641 93.03007 39.17035 3 620.95228
#> 2 49.76991 67.96527 43.48827 24.71475 2 318.37221
#> 3 65.82827 59.48330 56.72526 71.38306 2  43.17011
#> 4 92.65662 34.89741 46.59157 90.10154 1 878.50087
#> 5 36.13455 86.18987 72.06964 82.31317 3 520.72241
#> 6 91.87117 73.47734 40.64134 83.78471 2 506.16785

transposition et conversion matrice / data.frame

df_1 %>%
  mutate(var = reduce(select(., -y),`+`))  %>%
  head()
#>        x.1      x.2      x.3      x.4 y      var
#> 1 41.24069 58.56641 93.03007 39.17035 3 232.0075
#> 2 49.76991 67.96527 43.48827 24.71475 2 185.9382
#> 3 65.82827 59.48330 56.72526 71.38306 2 253.4199
#> 4 92.65662 34.89741 46.59157 90.10154 1 264.2471
#> 5 36.13455 86.18987 72.06964 82.31317 3 276.7072
#> 6 91.87117 73.47734 40.64134 83.78471 2 289.7746

Fonction personnalisée avec pmap

set.seed(1)
df_1 <- data.frame(
  x = replicate(4, runif(30, 20, 100)), 
  y = sample(1:3, 30, replace = TRUE)
)

library(tidyverse)

# rowSums
df_1 %>%
  mutate(var = rowSums(select(., -y))) %>%
  head()
#>        x.1      x.2      x.3      x.4 y      var
#> 1 41.24069 58.56641 93.03007 39.17035 3 232.0075
#> 2 49.76991 67.96527 43.48827 24.71475 2 185.9382
#> 3 65.82827 59.48330 56.72526 71.38306 2 253.4199
#> 4 92.65662 34.89741 46.59157 90.10154 1 264.2471
#> 5 36.13455 86.18987 72.06964 82.31317 3 276.7072
#> 6 91.87117 73.47734 40.64134 83.78471 2 289.7746

Créé le 2019-04-30 par le package reprex (v0.2.1)


2 commentaires

Au lieu de + , je peux mettre mean , var (répondre avec réduire )? Comment puis-je faire ceci?


Je l'ai mis à jour avec var , en utilisant une stratégie différente. Ce n'est pas particulièrement élégant (je suppose qu'il y a des fonctions personnalisées par ligne pour beaucoup de choses), mais cette approche fonctionnerait généralement tant que toutes les colonnes -y sont du même type.



4
votes

Cela peut être fait en utilisant purrr :: pmap a >, qui passe une liste d'arguments à une fonction qui accepte les "points". Étant donné que la plupart des fonctions telles que mean , sd , etc. fonctionnent avec des vecteurs, vous devez associer l'appel avec un domain lifter :

df_1 %>% select(-y) %>% 
  mutate( v1 = pmap(., lift_vd(mean)),
          v2 = pmap(., lift_vd(sd)),
          v3 = pmap(., sum) )
#         x.1      x.2      x.3      x.4       v1       v2       v3
# 1  70.12072 62.99024 54.00672 86.81358 68.48282 13.88555 273.9313
# 2  49.40462 47.00752 21.99248 78.87789 49.32063 23.27958 197.2825

La fonction sum accepte directement les points, donc vous ne t besoin de lever son domaine:

df_1 %>% select(-y) %>% mutate( var = pmap(., sum) )
#         x.1      x.2      x.3      x.4      var
# 1  70.12072 62.99024 54.00672 86.81358 273.9313
# 2  49.40462 47.00752 21.99248 78.87789 197.2825

Tout est conforme au traitement de données standard dplyr , donc les trois peuvent être combinés comme arguments séparés pour muter :

df_1 %>% select(-y) %>% mutate( var = pmap(., lift_vd(mean)) )
#         x.1      x.2      x.3      x.4      var
# 1  70.12072 62.99024 54.00672 86.81358 68.48282
# 2  49.40462 47.00752 21.99248 78.87789 49.32063

df_1 %>% select(-y) %>% mutate( var = pmap(., lift_vd(sd)) )
#         x.1      x.2      x.3      x.4      var
# 1  70.12072 62.99024 54.00672 86.81358 13.88555
# 2  49.40462 47.00752 21.99248 78.87789 23.27958


3 commentaires

Merci. Mais, et pour plus que la fonction? Exemple, pour mean , sd et var (3 nouvelles colonnes)? Voir: mutate (var = pmap (., Lift_vd (mean, sd, var))) ne fonctionne pas.


@GiovaniNeves: Combinez simplement ceux à l'intérieur de mutate comme vous le feriez normalement. Voir la modification ci-dessus.


Excellente solution! Merci, @Artem Sokolov!



1
votes

C'est un problème délicat car dplyr opère par colonne pour de nombreuses opérations. J'ai utilisé à l'origine apply à partir de la base R pour appliquer sur les lignes, mais apply est problématique lors de la gestion des caractères et des types numériques .

Au lieu de cela, nous pouvons utiliser (le vieillissement) plyr strong> et adply a > pour faire cela simplement, puisque plyr nous permet de traiter un bloc de données à une ligne comme un vecteur:

df_1 %>% select(-y) %>% adply(1, function(df) c(v1 = sd(df[1, ])))

Notez certaines fonctions comme var won ' t travailler sur une trame de données à une ligne, nous devons donc convertir en vecteur en utilisant as.numeric .


0 commentaires