1
votes

Comment utiliser une plage pour les colonnes au lieu de noms pour pmax / pmin

Je souhaite utiliser une plage de colonnes dans pmax / pmin au lieu de taper les noms de toutes les colonnes.

bar <- foo %>% 
    mutate_at(a:z = pmax(a:z))

En fin de compte, je veux aussi quelque chose comme ça

#sample data
foo <- data.frame(sapply(letters, function(x) x = sample(1:10,5)))

#this works
bar <- foo %>% 
    mutate(maxcol = pmax(a,b,c))

# this does not work
bar <- foo %>% 
    mutate(maxcol = pmax(a:z))


3 Réponses :


2
votes

Vous pouvez utiliser rowwise et c_across (dépend de dplyr > = 1.0.0):

library(dplyr)

foo %>% 
  rowwise() %>% 
  summarise(max= max(c_across(a:z)))

`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 5 x 1
    max
  <int>
1    10
2    10
3    10
4    10
5    10


5 commentaires

Rowwise reste-t-il comme group_by, la prochaine opération serait-elle effectuée rowwise? Comment supprimer Rowwise s'il reste?


Il reste groupé par ligne s'il est utilisé avec mutate . Ajoutez simplement un %>% ungroup () pour vous débarrasser du regroupement par ligne.


Bien que cette réponse soit canonique à dplyr :: c_across , Je trouve intéressant que la documentation suggère rowwise comme premier moyen de le faire, malgré ses faibles performances de mise à l'échelle par rapport à la vectorisation par colonne (akrun) ou tout-à-une (ma réponse).


@ r2evans quels documents? pouvez-vous s'il vous plaît ajouter une référence à cela?


"les documents" faisait référence au lien "canonique" sept mots auparavant. Plus précisément, il indique que c_across a été conçu pour être utilisé avec rowwise , et que le dernier morceau de code de la section "Exemples" utilise %>% rowwise ()% >% mutate (... sum (c_across (...))) .



2
votes

Nous sélectionnons les colonnes de a à z ( sélectionnons (., a: z) ), et les réduisons à un seul vecteur / colonne après avoir appliqué le pmax sur chaque ligne correspondante des colonnes

foo$maxcol <- do.call(pmax, foo)

Ou une autre option est d'épisser ( !!! qui force -pliquer une liste d'objets.

foo %>% 
     mutate(maxcol = pmax(!!! .))

Nous pouvons également utiliser pmax avec do.call dans base R

library(dplyr)
library(purrr)
foo %>%
     mutate(maxcol = reduce(select(., a:z), pmax))


6 commentaires

Pouvez-vous s'il vous plaît ajouter quelques informations sur ce que fait réduire et ce qu'est !!! ? Merci!


@AshishSinghal a ajouté quelques informations supplémentaires. La réduction est similaire dans son action à do.call depuis base R , do.call (pmax, foo) (pour certaines fonctions)


Pas correcte. purrr :: reduction est similaire au Reduce de la base R, alors que purrr :: invoke est similaire au do.call . La plus grande différence est que invoke / do.call fait un appel à la fonction, et réduit / Réduisez faites des appels n-1 , où n est le nombre d'arguments dans sa liste; a: z fait 26 colonnes, donc pmax est appelé 25 fois.


Votre utilisation de do.call (pmax, foo) fonctionne pour toutes les colonnes mais ne fonctionne pas aussi bien dans un tube dplyr sur un sous-ensemble de colonnes, ce que j'ai déduit de l'utilisation de < code> pmax (a, b, c) . Peut-être ai-je mal interprété le désir de sous-ensembles de colonnes.


@ r2evans Je pensais à l'OP # cela ne fonctionne pas avec a: z avec des colonnes pleines. Bref, ça va. Vous avez des explications claires avec des repères. Bon à connaître les efficiences. Merci


J'avais besoin de sous-ensembles de certaines colonnes `mutate (maxcol = do.call (pmax, subset (., Select = a: e)))` fait ce que je veux. qui, je pense, est similaire à foo $ maxcol <- do.call (pmax, foo) .



3
votes

Voici une option qui fait un appel de fonction sur toutes les lignes, toutes les colonnes à la fois.

library(purrr)
foo %>%
  mutate(maxcol = invoke(pmax, subset(., select = a:z)))

### microbenchmark(...)
# Unit: milliseconds
#     expr    min      lq    mean  median      uq      max neval
#      Dom 7.8292 8.40275 9.02813 8.97345 9.38500  12.4368   100
#      akr 4.9622 5.28855 8.78909 5.60090 6.11790 309.2607   100
#   r2base 2.5521 2.74635 3.01949 2.90415 3.21060   4.6512   100
#  r2purrr 2.5063 2.77510 3.11206 2.93415 3.33015   5.2403   100

Vous pouvez en sélectionner ou toutes les colonnes utilisant la notation deux-points, même les colonnes arbitraires:

set.seed(42)
foo <- data.frame(sapply(letters, function(x) x = sample(1:10,5000,replace=TRUE)))
microbenchmark::microbenchmark(
  Dom = {
    foo %>% 
      rowwise() %>% 
      summarise(max= max(c_across(a:z)))
  },
  akr = {
    foo %>%
       mutate(maxcol = reduce(select(., a:z), pmax))
  },
  r2 = {
    foo %>%
      mutate(maxcol = do.call(pmax, subset(., select = a:z)))
  }
)
# Unit: milliseconds
#  expr      min       lq      mean    median        uq       max neval
#   Dom 515.6437 563.6060 763.97348 811.45815 883.00115 1775.2366   100
#   akr   4.6660   5.1619  11.92847   5.74050   6.50625  293.7444   100
#    r2   2.9253   3.4371   4.24548   3.71845   4.27380   14.0958   100

La raison pour laquelle cela devrait être préféré aux autres réponses (qui utilisent généralement des méthodes prétendument idiomatiques) est que :

  • dans la réponse de Dom, la fonction max est appelée une fois pour chaque ligne de la trame; Les opérations vectorisées de R ne sont pas utilisées, ceci est inefficace et doit être évité si possible;
  • dans la réponse d'Akrun, pmax est appelé une fois pour chaque colonne de l'image, ce qui dans ce cas peut sembler pire mais en fait plus proche du mieux que l'on puisse faire. Ma réponse est la plus proche de celle d'Akrun en ce sens que nous sélectionnons des données dans le mutate .

Si vous préférez utiliser dplyr :: select plutôt que base :: subset , il doit être décomposé en

XXX

Je pense que cela est un peu mieux démontré avec des benchmarks. En utilisant le cadre 5x26 fourni, nous constatons une nette amélioration:

set.seed(42)
foo <- data.frame(sapply(letters, function(x) x = sample(1:10,5)))
microbenchmark::microbenchmark(
  Dom = {
    foo %>% 
      rowwise() %>% 
      summarise(max= max(c_across(a:z)))
  },
  akr = {
    foo %>%
       mutate(maxcol = reduce(select(., a:z), pmax))
  },
  r2 = {
    foo %>%
      mutate(maxcol = do.call(pmax, subset(., select = a:z)))
  }
)
# Unit: milliseconds
#  expr    min      lq    mean  median      uq     max neval
#   Dom 6.6561 7.15260 7.61574 7.38345 7.90375 11.0387   100
#   akr 4.2849 4.69920 4.96278 4.86110 5.18130  7.0908   100
#    r2 2.3290 2.49285 2.68671 2.59180 2.78960  4.7086   100

Essayons avec un 5000x26 légèrement plus grand:

foo %>%
  mutate(maxcol = select(., a:e, g) %>% do.call(pmax, .))

Ce dernier montre clairement une conséquence de l'utilisation de rowwise . La performance relative entre la réponse d'Akrun et celle-ci est presque identique à 5 lignes, ce qui renforce la prémisse que la colonne est meilleure que la ligne (et tout-en-une est plus rapide que les deux).

( Cela peut également être fait avec purrr :: invoke , si vous le souhaitez, bien que cela ne l'accélère pas:

foo %>%
  mutate(maxcol = do.call(pmax, subset(., select = c(a:e,g))))
#    a  b c d e  f g  h  i j  k l m  n  o p q  r  s t u  v w  x  y z maxcol
# 1  1  4 9 2 4  4 1 10  2 3 10 4 7  1 10 9 8  2  8 9 5  1 9  1 10 9      9
# 2  5  2 5 3 5  2 8  8  5 8  2 3 6 10  9 3 5  8  7 4 6  9 8  5  8 3      8
# 3 10  9 6 1 7 10 6  4  4 7  6 6 2  7  5 5 4  1 10 7 3 10 5 10  1 7     10
# 4  8  1 4 8 9  3 3  9 10 1  8 5 8  4  4 8 6 10  5 2 9  5 7  7  3 1      9
# 5  2 10 2 9 8  9 9  6  7 5  9 2 5  5  7 4 2  5  4 8 4  6 6  2  9 6     10


0 commentaires