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 :
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
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 (...)))
.
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))
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) code > (pour certaines fonctions)
Pas correcte. purrr :: reduction
est similaire au Reduce
de la base R, alors que purrr :: invoke
est similaire au do.call code de R >. 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)
.
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 :
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; 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
En relation: R: retourne pmin ou pmax de données .frame avec plusieurs colonnes