Largeur cohérente pour geom_bar en cas de données manquantes

Est-il possible de définir une largeur constante pour geom_bar() en cas de données manquantes dans l’exemple de série temporelle ci-dessous? J’ai essayé de définir la width en aes() sans succès. Comparez la largeur des barres de mai ’11 à juin ’11 dans le tracé sous l’exemple de code.

 colours <- c("#FF0000", "#33CC33", "#CCCCCC", "#FFA500", "#000000" ) iris$Month <- rep(seq(from=as.Date("2011-01-01"), to=as.Date("2011-10-01"), by="month"), 15) colours <- c("#FF0000", "#33CC33", "#CCCCCC", "#FFA500", "#000000" ) iris$Month <- rep(seq(from=as.Date("2011-01-01"), to=as.Date("2011-10-01"), by="month"), 15) d<-aggregate(iris$Sepal.Length, by=list(iris$Month, iris$Species), sum) d$quota<-seq(from=2000, to=60000, by=2000) colnames(d) <- c("Month", "Species", "Sepal.Width", "Quota") d$Sepal.Width<-d$Sepal.Width * 1000 g1 <- ggplot(data=d, aes(x=Month, y=Quota, color="Quota")) + geom_line(size=1) g1 + geom_bar(data=d[c(-1:-5),], aes(x=Month, y=Sepal.Width, width=10, group=Species, fill=Species), stat="identity", position="dodge") + scale_fill_manual(values=colours) 

terrain

Le moyen le plus simple est de compléter votre dataset afin que chaque combinaison soit présente, même si elle a pour valeur NA . Prenons un exemple plus simple (car le vôtre comporte de nombreuses fonctionnalités inutiles):

 dat <- data.frame(a=rep(LETTERS[1:3],3), b=rep(letters[1:3],each=3), v=1:9)[-2,] ggplot(dat, aes(x=a, y=v, colour=b)) + geom_bar(aes(fill=b), stat="identity", position="dodge") 

entrer la description de l'image ici

Cela montre le comportement que vous essayez d'éviter: dans le groupe "B", il n'y a pas de groupe "a", donc les barres sont plus larges. Complétez dat avec un dataframe avec toutes les combinaisons de a et b :

 dat.all <- rbind(dat, cbind(expand.grid(a=levels(dat$a), b=levels(dat$b)), v=NA)) ggplot(dat.all, aes(x=a, y=v, colour=b)) + geom_bar(aes(fill=b), stat="identity", position="dodge") 

entrer la description de l'image ici

J’ai eu le même problème mais je cherchais une solution qui fonctionne avec le tuyau ( %>% ). Utiliser tidyr::spread et tidyr::gather du tidyverse fait l’affaire. J’utilise les mêmes données que @Brian Diggs, mais avec des noms de variables en majuscules pour ne pas avoir de noms de variables doubles lors de la transformation en larges:

 library(tidyverse) dat <- data.frame(A = rep(LETTERS[1:3], 3), B = rep(letters[1:3], each = 3), V = 1:9)[-2, ] dat %>% spread(key = B, value = V, fill = NA) %>% # turn data to wide, using fill = NA to generate missing values gather(key = B, value = V, -A) %>% # go back to long, with the missings ggplot(aes(x = A, y = V, fill = B)) + geom_col(position = position_dodge()) 

Modifier:

Il existe en fait une solution encore plus simple à ce problème en combinaison avec le tuyau. Utiliser tidyr::complete donne le même résultat sur une ligne:

 dat %>% complete(A, B) %>% ggplot(aes(x = A, y = V, fill = B)) + geom_col(position = position_dodge()) 

Certaines nouvelles options pour position_dodge() et le nouveau position_dodge2() , introduit dans ggplot2 3.0.0, peuvent vous aider.

Vous pouvez utiliser preserve = "single" dans position_dodge() pour baser les largeurs sur un seul élément, afin que les largeurs de toutes les barres soient les mêmes.

 ggplot(data = d, aes(x = Month, y = Quota, color = "Quota")) + geom_line(size = 1) + geom_col(data = d[c(-1:-5),], aes(y = Sepal.Width, fill = Species), position = position_dodge(preserve = "single") ) + scale_fill_manual(values = colours) 

L’utilisation de position_dodge2() modifie la façon dont les choses sont centrées, en centrant chaque ensemble de barres sur chaque emplacement d’axe x. Il y a un padding intégré, utilisez donc padding = 0 pour le supprimer.

 ggplot(data = d, aes(x = Month, y = Quota, color = "Quota")) + geom_line(size = 1) + geom_col(data = d[c(-1:-5),], aes(y = Sepal.Width, fill = Species), position = position_dodge2(preserve = "single", padding = 0) ) + scale_fill_manual(values = colours)