Ancho constante para geom_bar en caso de falta de datos

¿Hay alguna forma de establecer un ancho constante para geom_bar() en caso de que falten datos en el ejemplo de series de tiempo a continuación? Intenté configurar el width en aes() sin suerte. Compare el ancho de las barras entre mayo de 2011 y junio de 2011 en el diagtwig debajo del ejemplo de código.

 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) 

trama

La forma más fácil es complementar su conjunto de datos para que cada combinación esté presente, incluso si tiene NA como valor. Tomando un ejemplo más simple (ya que el suyo tiene muchas características innecesarias):

 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") 

enter image description here

Esto muestra el comportamiento que intenta evitar: en el grupo "B", no hay un grupo "a", por lo que las barras son más anchas. Suplemento dat con un dataframe con todas las combinaciones de 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") 

enter image description here

Tuve el mismo problema pero estaba buscando una solución que funcione con la tubería ( %>% ). El uso de tidyr::spread y tidyr::gather de tidyverse es el truco. Utilizo los mismos datos que @Brian Diggs, pero con nombres de mayúsculas variables para no terminar con nombres de doble variable cuando se transforma en ancho:

 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()) 

Editar:

En realidad, existe una solución aún más simple para ese problema en combinación con la tubería. Usar tidyr::complete da el mismo resultado en una línea:

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

Algunas nuevas opciones para position_dodge() y el nuevo position_dodge2() , introducido en ggplot2 3.0.0 pueden ayudar.

Puede usar preserve = "single" en position_dodge() para basar los anchos de un solo elemento, por lo que el ancho de todas las barras será el mismo.

 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) 

Usar position_dodge2() cambia la forma en que se centran las cosas, centrando cada conjunto de barras en cada ubicación del eje x. Tiene algo de padding incorporado, por lo tanto, use padding = 0 para eliminarlo.

 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)