J'essaie de transférer ma compréhension de plyr dans dplyr, mais je ne vois pas comment grouper par plusieurs colonnes.
# make data with weird column names that can't be hard coded
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
# plyr - works
ddply(data, columns, summarize, value=mean(value))
# dplyr - raises error
data %.%
group_by(columns) %.%
summarise(Value = mean(value))
#> Error in eval(expr, envir, enclos) : index out of bounds
Que manque-t-il pour traduire l'exemple plyr en une syntaxe dplyr-esque?
Edit 2017: Dplyr a été mis à jour, une solution plus simple est donc disponible. Voir la réponse actuellement sélectionnée.
Depuis que cette question a été postée, dplyr a ajouté des versions étendues de group_by
( documentation ici ). Cela vous permet d'utiliser les mêmes fonctions que vous utiliseriez avec select
, comme ceci:
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
library(dplyr)
df1 <- data %>%
group_by_at(vars(one_of(columns))) %>%
summarize(Value = mean(value))
#compare plyr for reference
df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value))
table(df1 == df2, useNA = 'ifany')
## TRUE
## 27
Le résultat de votre exemple de question est celui attendu (voir la comparaison avec plyr ci-dessus et le résultat ci-dessous):
# A tibble: 9 x 3
# Groups: asihckhdoydkhxiydfgfTgdsx [?]
asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value
<fctr> <fctr> <dbl>
1 A A 0.04095002
2 A B 0.24943935
3 A C -0.25783892
4 B A 0.15161805
5 B B 0.27189974
6 B C 0.20858897
7 C A 0.19502221
8 C B 0.56837548
9 C C -0.22682998
Notez que, puisque dplyr::summarize
ne supprime qu'une couche de groupe à la fois, il reste encore un groupe dans le tibble résultant (qui peut parfois surprendre des personnes par la suite). Si vous voulez être absolument à l'abri d'un comportement de regroupement inattendu, vous pouvez toujours ajouter %>% ungroup
à votre pipeline après la synthèse.
Juste pour écrire le code en entier, voici une mise à jour de la réponse de Hadley avec la nouvelle syntaxe:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# Columns you want to group by
grp_cols <- names(df)[-3]
# Convert character vector to list of symbols
dots <- lapply(grp_cols, as.symbol)
# Perform frequency counts
df %>%
group_by_(.dots=dots) %>%
summarise(n = n())
sortie:
Source: local data frame [9 x 3]
Groups: asihckhdoydk
asihckhdoydk a30mvxigxkgh n
1 A A 10
2 A B 10
3 A C 13
4 B A 14
5 B B 10
6 B C 12
7 C A 9
8 C B 12
9 C C 10
Le support pour cela dans dplyr est actuellement assez faible, je pense que la syntaxe ressemblera à quelque chose comme:
df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))
Mais ce ne sera probablement pas là pendant un moment (parce que je dois réfléchir à toutes les conséquences).
En attendant, vous pouvez utiliser regroup()
, qui prend une liste de symboles:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
df %.%
regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.%
summarise(n = n())
Si vous avez un vecteur de caractère composé de noms de colonnes, vous pouvez les convertir en structure correcte avec lapply()
et as.symbol()
:
vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)
df %.% regroup(vars2) %.% summarise(n = n())
La spécification de chaîne des colonnes dans dplyr
est maintenant prise en charge via des variantes des fonctions dplyr
avec des noms se terminant par un trait de soulignement. Par exemple, correspondant à la fonction group_by
, il existe une fonction group_by_
pouvant accepter des arguments de chaîne. Cette vignette décrit la syntaxe de ces fonctions en détail.
Le fragment de code suivant résout proprement le problème que @sharoz a posé à l'origine (notez la nécessité d'écrire l'argument .dots
):
# Given data and columns from the OP
data %>%
group_by_(.dots = columns) %>%
summarise(Value = mean(value))
(Notez que dplyr utilise maintenant l'opérateur %>%
et que %.%
est obsolète).
Jusqu'à ce que dplyr prenne en charge les arguments de chaîne, peut-être que ce Gist est utile:
https://Gist.github.com/skranz/9681509
Il contient un tas de fonctions d'encapsulage telles que s_group_by, s_mutate, s_filter, etc. qui utilisent des arguments de chaîne. Vous pouvez les mélanger avec les fonctions normales de Dplyr. Par exemple
cols = c("cyl","gear")
mtcars %.%
s_group_by(cols) %.%
s_summarise("avdisp=mean(disp), max(disp)") %.%
arrange(avdisp)
Cela fonctionne si vous transmettez les objets (enfin, vous ne l'êtes pas, mais ...) plutôt que sous forme de vecteur de caractère:
df %.%
group_by(asdfgfTgdsx, asdfk30v0ja) %.%
summarise(Value = mean(value))
> df %.%
+ group_by(asdfgfTgdsx, asdfk30v0ja) %.%
+ summarise(Value = mean(value))
Source: local data frame [9 x 3]
Groups: asdfgfTgdsx
asdfgfTgdsx asdfk30v0ja Value
1 A C 0.046538002
2 C B -0.286359899
3 B A -0.305159419
4 C A -0.004741504
5 B B 0.520126476
6 C C 0.086805492
7 B C -0.052613078
8 A A 0.368410146
9 A B 0.088462212
où df
était votre data
.
?group_by
dit:
...: variables to group by. All tbls accept variable names, some
will also accept functons of variables. Duplicated groups
will be silently dropped.
ce que j’interprète comme signifiant non pas les versions de caractères des noms, mais la façon dont vous les désigneriez dans foo$bar
; bar
n'est pas cité ici. Ou comment vous feriez référence à des variables dans une formule: foo ~ bar
.
@Arun mentionne également que vous pouvez faire:
df %.%
group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
summarise(Value = mean(value))
Mais vous ne pouvez pas transmettre quelque chose qui non évalué n'est pas le nom d'une variable dans l'objet de données.
Je suppose que cela est dû aux méthodes internes que Hadley utilise pour rechercher les éléments que vous transmettez via l'argument ...
.
data = data.frame(
my.a = sample(LETTERS[1:3], 100, replace=TRUE),
my.b = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value))
Un cas (minuscule) qui manque dans les réponses ici, que je voulais expliciter, est lorsque les variables à grouper sont générées de manière dynamique à mi-chemin dans un pipeline:
library(wakefield)
df_foo = r_series(rnorm, 10, 1000)
df_foo %>%
# 1. create quantized versions of base variables
mutate_each(
funs(Quantized = . > 0)
) %>%
# 2. group_by the indicator variables
group_by_(
.dots = grep("Quantized", names(.), value = TRUE)
) %>%
# 3. summarize the base variables
summarize_each(
funs(sum(., na.rm = TRUE)), contains("X_")
)
Cela montre essentiellement comment utiliser grep
conjointement avec group_by_(.dots = ...)
pour y parvenir.
Exemple général d'utilisation de l'argument .dots
comme entrée de vecteur de caractères dans la fonction dplyr::group_by
:
iris %>%
group_by(.dots ="Species") %>%
summarise(meanpetallength = mean(Petal.Length))
Ou sans nom codé en dur pour la variable de regroupement (comme demandé par l'OP):
iris %>%
group_by(.dots = names(iris)[5]) %>%
summarise_at("Petal.Length", mean)
Avec l'exemple du PO:
data %>%
group_by(.dots =names(data)[-3]) %>%
summarise_at("value", mean)
Voir aussi la vignette de dplyr sur la programmation qui explique les pronoms, la quasiquotation, les quosures et le bon ordre.