Back-to-back/population plot in ggplot2 [duplicate] - r

This question already has answers here:
Simpler population pyramid in ggplot2
(4 answers)
Closed 1 year ago.
I am attempting to produce a back-to-back bar plot (aka population pyramid plot) using ggplot2. Here is an example:
There are multiple examples of creating these plots on S/O (e.g., here, here and here), but none of the solutions offered in these examples are working for me. I wonder whether this is due to these questions being asked a while ago with multiple ggplot updates in-between.
Here is an example of the data I am working with:
dt = structure(list(age_grp = structure(c(1L, 1L, 2L, 2L, 3L, 3L,
4L, 4L, 5L, 5L, 6L, 6L, 7L, 7L, 8L, 8L, 9L, 9L, 10L, 10L), .Label = c("13",
"14", "15", "16", "17", "18", "19", "20", "21", "22"), class = "factor"),
sex = structure(c(2L, 1L, 2L, 1L, 2L, 1L, 1L, 2L, 1L, 2L,
2L, 1L, 2L, 1L, 2L, 1L, 1L, 2L, 1L, 2L), .Label = c("Female",
"Male"), class = "factor"), percent = c(0.0407730571638261,
0.0536480686695279, 0.0652368914621218, 0.0268240343347639,
1.22319171491478, 1.07296137339056, 4.02360515021459, 4.89276685965914,
16.4967811158798, 19.5710674386366, 24.4638342982957, 20.118025751073,
17.9401451520835, 17.1673819742489, 13.0473782924244, 14.7532188841202,
13.412017167382, 10.6009948625948, 12.8755364806867, 8.15461143276523
)), row.names = c(NA, -20L), class = c("data.table", "data.frame"
))
My first attempt at making a pyramid plot used this code:
ggplot(dt, aes(x = age_grp, y = percent, fill = sex)) +
geom_bar(data = subset(dt, sex == "Female"), stat = "identity") +
geom_bar(data = subset(dt, sex == "Male"), stat = "identity") +
coord_flip()
And produced this plot:
You can see that the fill for male has just overlapped the fill for female, rather than the female fill being flipped.
To fix this, I used the following work-around:
ggplot(dt, aes(x = age_grp, y = percent, fill = sex)) +
geom_bar(data = subset(dt, sex == "Female"), aes(y = -percent), stat = "identity") +
geom_bar(data = subset(dt, sex == "Male"), stat = "identity") +
coord_flip()
And this produced a plot close to what I was after:
However, the problem now is that the "percent" y-axis is now negative for females. How do I make it so that the y-axis is positive for both males and females? I'm sure this is an easy fix and I am missing something very simple!
Any help is greatly appreciated

I missed a solution in this post!
Here is the solution:
ggplot(dt, aes(x = age_grp, y = percent, fill = sex)) +
geom_bar(data = subset(dt, sex == "Female"), aes(y = -percent), stat = "identity") +
geom_bar(data = subset(dt, sex == "Male"), stat = "identity") +
scale_y_continuous(breaks=seq(-20,20,5),labels=abs(seq(-20,20,5))) +
coord_flip()
Just needed to re-format the labels of the y-axis using scale_y_continuous

Related

why the order of factor does not change

I am trying to make the order from small to big when I plot a figure .
This is the data
df <- structure(list(label = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 4L,
4L, 5L, 5L, 6L, 6L), .Label = c(" data1", " data10", " data15",
" data20", " data5", " data8"), class = "factor"), variable = structure(c(1L,
2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L), .Label = c("boys",
"girls"), class = "factor"), N = structure(c(1L, 1L, 2L, 2L,
3L, 3L, 4L, 4L, 5L, 5L, 6L, 6L), .Label = c("1", "10", "15",
"20", "5", "8"), class = "factor"), value = c(93987048.3466667,
54815262.22, 2050259639.66667, 2154175512.5, 3101847435.66667,
2881192196, 6625604632, 3739286226.5, 270853386.15, 430516498,
675222758.633333, 717219589.4), sd = c(129474855.769856, 48756976.8630242,
777498079.458271, 301433971.74375, 1109031077.05936, 45164769.8052193,
1060915464.90521, 1147931493.65351, 239740705.411582, 38453433.9415797,
123930253.814345, 112112765.581137), se = c(74752342.8320143,
34476388.97, 448888725.46965, 213146005.5, 640299390.87988, 31936315,
612519829.250459, 811710143.5, 138414360.805088, 27190683.9,
71551165.4004505, 79275696.8), ci = c(321633371.941334, 438064056.816815,
1931412299.99575, 2708276784.58082, 2754985922.02623, 405789356.908967,
2635460115.35016, 10313755269.7407, 595548927.354538, 345490396.550009,
307859817.127003, 1007293234.14371)), .Names = c("label", "variable",
"N", "value", "sd", "se", "ci"), row.names = c(NA, -12L), class = "data.frame")
based on this answer, I should be able to convert the label to character and then factor and then sort it. How do you specifically order ggplot2 x axis instead of alphabetical order?
So I did like this
df$N <- as.character(df$N)
#Then turn it back into an ordered factor
df$N <- factor(df$N, levels=unique(df$N))
If I str the df, I can see that it change the character to factor but the level does not change
I even tried to manually change the level
df$N <- factor(df$N, levels=c("1","5","8","10","15","20"))
but I am still unable to change the order in plot
so if i do df[order(df$N),] nothing happens
library(ggplot2)
pd <- position_dodge(0.1)
ggplot(df, aes(x=N, y=value, colour=variable)) +
geom_errorbar(aes(ymin=value-se, ymax=value+se), width=.1, position=pd) +
geom_line(position=pd) +
geom_point(position=pd)
Convert N to character then to numeric and sort it.
# Sort N (Using OPs data)
df$N2 <- factor(df$N, levels = sort(unique(as.numeric(as.character(df$N)))))
library(ggplot2)
ggplot(df, aes(N2, value, colour = variable)) +
geom_errorbar(aes(ymin = value - se, ymax = value + se),
width = 0.1, position = pd) +
geom_line(position = pd) +
geom_point(position = pd)
So it seems that there's a distinction between the problem you're proposing, and the question you linked to. In the question linked, they were using X, Y, and Z as representations of groups. They wanted those groups ordered in a specific way.
Here, you are using numbers, and you want them in ascending order. You shouldn't categorize them as factor. Rather, they should be categorized as numeric.
You may still load your numeric data in as a factor, but you'll need to convert it to character first, and then numeric.
df$N <- as.numeric(as.character(df$N))
pd <- position_dodge(0.1)
ggplot(df, aes(x=N, y=value, colour=variable)) +
geom_errorbar(aes(ymin=value-se, ymax=value+se), width=.1, position=pd) +
geom_line(position=pd) +
geom_point(position=pd)

Add geom_hline legend to existing geom bar legend

I want to add a legend under the existing legend that represents the dashed line, such that the dashed line could be labeled "avg tx effect" and be placed under study 3.
library(ggplot2)
library(ggthemes)
#dput(df)
df=structure(list(study = structure(c(1L, 2L, 3L, 1L, 2L, 3L, 1L,
2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), .Label = c("study1", "study2",
"study3"), class = "factor"), d = c(-0.205, 0.1075, 0.3525, -0.37,
0.3, 0.42, -0.28, 0.09, 0.59, 0.11, -0.05, 0.25, 0, 0.25, 0.49
), Outcome = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L,
5L, 5L, 5L), Outcome2 = structure(c(1L, 1L, 1L, 4L, 4L, 4L, 7L,
7L, 7L, 10L, 10L, 10L, 13L, 13L, 13L), .Label = c("1", "1", "1",
"2", "2", "2", "3", "3", "3", "4", "4", "4", "5", "5", "5"), class = "factor")), .Names = c("study",
"d", "Outcome", "Outcome2"), row.names = c(NA, -15L), class = "data.frame")
ggplot(df, aes(x=Outcome2, y=d, fill=study)) +
geom_bar(position=position_dodge(), aes(x=Outcome2),stat="identity",
colour="black", # Use black outlines,
size=.3) + # Thinner lines
xlab("Outcome") +
ylab("Cohen's D Effect Size") +
scale_fill_grey(name="Study",
labels=c("study1","study2", "study3"))+
theme_bw()+
geom_hline(yintercept=.15,linetype=2)
A general feature of ggplot is that to generate a legend, you need to map your aesthetics (e.g. linetype) to a variable in the data, instead of setting it to constant. In the case of geom_hline, this may be achieved by putting the intercept in a separate data frame. Also note show_guide = TRUE.
Then customize the legend using scale_linetype_manual. The black lines in the fill legend are removed using override.aes.
Here's a stripped down version of your code to show only the most necessary steps:
df2 <- data.frame(yi = 0.15)
ggplot(data = df, aes(x = Outcome2, y = d, fill = study)) +
geom_bar(position = "dodge", stat = "identity") +
geom_hline(data = df2, aes(yintercept = yi, linetype = factor(yi)), show_guide = TRUE) +
scale_linetype_manual(name = "avg tx effect", values = "dashed", labels = "") +
guides(fill = guide_legend(override.aes = list(linetype = "blank")))
As #Gregor suggested, you could use a direct label for this line by adding annotate() as shown below:
ggplot(df, aes(x=Outcome2, y=d, fill=study)) +
geom_bar(position=position_dodge(), aes(x=Outcome2),stat="identity",
colour="black", # Use black outlines,
size=.3) + # Thinner lines
xlab("Outcome") +
ylab("Cohen's D Effect Size") +
scale_fill_grey(name="Study",
labels=c("study1","study2", "study3"))+
theme_bw()+
geom_hline(yintercept=.15,linetype=2) +annotate("text",x=.7,y=.17,size=3,label=c('avg tx ef'))
If space is an issue you can use the wrapper described here to wrap the text. Just run wrapper <- function(x, ...) paste(strwrap(x, ...), collapse = "\n") then add +annotate("text",x=.7,y=.18,size=3,label=wrapper('avg tx effect',10)). Which produces:

How to organize percentage values on top of a stacked bar chart ggplot2

I have a very skewed bar chart in ggplot2.
Here's the dput text output:
structure(list(Name = structure(c(1L, 3L, 4L, 5L, 6L, 2L, 1L,
3L, 4L, 5L, 6L, 2L), .Label = c("A", "Average", "B", "C", "D",
"E"), class = "factor"), variable = structure(c(1L, 1L, 1L, 1L,
1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("Undiscounted", "Discounted"
), class = "factor"), value = c(18453601.4400001, 11941354.11,
10748756.04, 6488253.74000001, 6078914.73000002, 2509377.50173653,
1157538776.56, 833907589.89, 674006380.96, 574466340.26, 534854603.27,
13191411.5509581)), row.names = c(NA, -12L), .Names = c("Name",
"variable", "value"), class = "data.frame")
Here's the code I use to plot it:
library(ggplot2)
text_size= 18
label_bottom_size=18
plot1<- ggplot(df1, aes(x = Name, y = value, fill = variable)) +
geom_bar(stat = "identity")+
ggtitle(sprintf("Bar chart of Stuff" ))+
theme(axis.title=element_text(size=text_size))+
theme(plot.title=element_text(size=text_size+20))+
theme(axis.text.x= element_text(size=label_bottom_size))+
theme(axis.text.y= element_text(size=text_size))+
theme(legend.text = element_text(size=text_size))+
theme(legend.title = element_text(size=text_size))
As some of the bar charts are so small and text doesn't fit, what I want to do is just have a (X%/Y%) above each bar that shows the percentage breakout. The values shown are in dollars.
Thank you!
This is some hack using data.table for aggregating the data and then displaying it with geom_text (there are probably better ways though)
library(data.table)
temp <- data.table(df1)[, per := (value/sum(value))*100, by = Name]
temp <- temp[, list(value = sum(value),
per = paste(sprintf("%.02f%%", per), collapse = " / "),
variable = variable), by = Name]
library(ggplot2)
text_size= 18
label_bottom_size=18
ggplot(df1, aes(x = Name, y = value, fill = variable)) +
geom_bar(stat = "identity")+
ggtitle(sprintf("Bar chart of Stuff" ))+
theme(axis.title=element_text(size=text_size),
plot.title=element_text(size=text_size+20),
axis.text.x= element_text(size=label_bottom_size),
axis.text.y= element_text(size=text_size),
legend.text = element_text(size=text_size),
legend.title = element_text(size=text_size)) +
geom_text(data = temp, aes(x = Name, y = value, label = per), vjust=-0.3)

Controlling bar width with ggplot2 [duplicate]

This question is motivated by further exploring this question. The problem with the accepted solution becomes more obvious when there is a greater disparity in the number of bars per facet. Take a look at this data and the resultant plot using that solution:
# create slightly contrived data to better highlight width problems
data <- data.frame(ID=factor(c(rep(1,9), rep(2,6), rep(3,6), rep(4,3), rep(5,3))),
TYPE=factor(rep(1:3,length(ID)/3)),
TIME=factor(c(1,1,1,2,2,2,3,3,3,1,1,1,2,2,2,1,1,1,2,2,2,1,1,1,1,1,1)),
VAL=runif(27))
# implement previously suggested solution
base.width <- 0.9
data$w <- base.width
# facet two has 3 bars compared to facet one's 5 bars
data$w[data$TIME==2] <- base.width * 3/5
# facet 3 has 1 bar compared to facet one's 5 bars
data$w[data$TIME==3] <- base.width * 1/5
ggplot(data, aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~TIME, ncol=1, scale="free") +
geom_bar(position="stack", aes(width = w),stat = "identity") +
coord_flip()
You'll notice the widths look exactly right, but the whitespace in facet 3 is quite glaring. There is no easy way to fix this in ggplot2 that I have seen yet (facet_wrap does not have a space option).
Next step is to try to solve this using gridExtra:
# create each of the three plots, don't worry about legend for now
p1 <- ggplot(data[data$TIME==1,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
p2 <- ggplot(data[data$TIME==2,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
p3 <- ggplot(data[data$TIME==3,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
# use similar arithmetic to try and get layout correct
require(gridExtra)
heights <- c(5, 3, 1) / sum(5, 3, 1)
print(arrangeGrob(p1 ,p2, p3, ncol=1,
heights=heights))
You'll notice I used the same arithmetic previously suggested based off the number of bars per facet, but in this case it ends up horribly wrong. This seems to be because there are extra "constant height" elements that I need to take into consideration in the math.
Another complication (I believe) is that the final output (and whether or not the widths match) will also depend on the width and height of where I'm outputting the final grob to, whether its in a R/RStudio environment, or to a PNG file.
How can I accomplish this?
Something like this appear to work, but it doesn't - not completely. It has the appearance of working because the levels of the ID factor are sequential. Anything else, and scale = "free" fails. But it might be possible to develop further. The method uses facet_grid, and thus space = "free" can be used. The method uses geom_rect to layer differently coloured rectangles on top of each other. It needs cumulative sums to be calculated so that the right-hand edge of each rectangle can be positioned.
data <- data.frame(ID=factor(c(rep(1,9), rep(2,6), rep(3,6), rep(4,3), rep(5,3))),
TYPE=factor(rep(1:3,3)),
TIME=factor(c(1,1,1,2,2,2,3,3,3,1,1,1,2,2,2,1,1,1,2,2,2,1,1,1,1,1,1)),
VAL=runif(27))
library(ggplot2)
library(plyr)
# Get the cumulative sums
data = ddply(data, .(ID, TIME), mutate, CUMSUMVAL = cumsum(VAL))
ggplot(data, aes(x=VAL, y = as.numeric(ID), fill=TYPE)) +
geom_rect(data = subset(data, TYPE == 3), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
geom_rect(data = subset(data, TYPE == 2), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
geom_rect(data = subset(data, TYPE == 1), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
facet_grid(TIME~., space = "free", scale="free") +
scale_y_continuous(breaks = c(1:5), expand = c(0, 0.2))
EDIT: OR really thick lines work a little better (I think)
ggplot(data, aes(x=VAL, y = ID, colour=TYPE)) +
geom_segment(data = subset(data, TYPE == 3), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 2), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 1), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
facet_grid(TIME~., space = "free", scale="free")
Additional Edit Taking the data from your earleir post, and modifying it a little.
Updated opts is deprecated; using theme instead.
df <- structure(list(ID = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L,
2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 5L,
5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L), .Label = c("a",
"b", "c", "d", "e", "f", "g"), class = "factor"), TYPE = structure(c(1L,
2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L,
1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L,
5L, 6L, 1L, 2L, 3L), .Label = c("1", "2", "3", "4", "5", "6",
"7", "8"), class = "factor"), TIME = structure(c(2L, 2L, 2L,
2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L,
2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L,
1L, 1L, 1L), .Label = c("One", "Five", "Fifteen"), class = "factor"), VAL = c(0.937377670081332,
0.522220720537007, 0.278690102742985, 0.967633064137772, 0.116124767344445,
0.0544306698720902, 0.470229141646996, 0.62017166428268, 0.195459847105667,
0.732876230962574, 0.996336271753535, 0.983087373664603, 0.666449476964772,
0.291554537601769, 0.167933790013194, 0.860138458199799, 0.172361251665279,
0.833266809117049, 0.620465772924945, 0.786503327777609, 0.761877260869369,
0.425386636285111, 0.612077651312575, 0.178726130630821, 0.528709076810628,
0.492527724476531, 0.472576208412647, 0.0702785139437765, 0.696220921119675,
0.230852259788662, 0.359884874196723, 0.518227979075164, 0.259466265095398,
0.149970305617899, 0.00682218233123422, 0.463400925742462, 0.924704828299582,
0.229068386601284)), .Names = c("ID", "TYPE", "TIME", "VAL"), row.names = c(NA,
-38L), class = "data.frame")
library(ggplot2)
library(plyr)
data = ddply(df, .(ID, TIME), mutate, CUMSUMVAL = cumsum(VAL))
ggplot(data, aes(x=VAL, y = ID, colour=TYPE)) +
geom_segment(data = subset(data, TYPE == 6), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 5), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 4), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 3), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 2), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 1), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
facet_grid(TIME~., space = "free", scale="free") +
theme(strip.text.y = element_text(angle = 0))
Changing the gtable doesn't help, unfortunately, as the bar width is in relative units,
g = ggplot_gtable(ggplot_build(p))
panels = which(sapply(g$heights, attr, "unit") == "null")
g$heights[[panels[1]]] <- unit(5, "null")
g$heights[[panels[2]]] <- unit(3, "null")
g$heights[[panels[3]]] <- unit(1, "null")
grid.draw(g)

ggplot2 + gridExtra: how to ensure geom_bar in different size plot grobs result in exact same bar width

This question is motivated by further exploring this question. The problem with the accepted solution becomes more obvious when there is a greater disparity in the number of bars per facet. Take a look at this data and the resultant plot using that solution:
# create slightly contrived data to better highlight width problems
data <- data.frame(ID=factor(c(rep(1,9), rep(2,6), rep(3,6), rep(4,3), rep(5,3))),
TYPE=factor(rep(1:3,length(ID)/3)),
TIME=factor(c(1,1,1,2,2,2,3,3,3,1,1,1,2,2,2,1,1,1,2,2,2,1,1,1,1,1,1)),
VAL=runif(27))
# implement previously suggested solution
base.width <- 0.9
data$w <- base.width
# facet two has 3 bars compared to facet one's 5 bars
data$w[data$TIME==2] <- base.width * 3/5
# facet 3 has 1 bar compared to facet one's 5 bars
data$w[data$TIME==3] <- base.width * 1/5
ggplot(data, aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~TIME, ncol=1, scale="free") +
geom_bar(position="stack", aes(width = w),stat = "identity") +
coord_flip()
You'll notice the widths look exactly right, but the whitespace in facet 3 is quite glaring. There is no easy way to fix this in ggplot2 that I have seen yet (facet_wrap does not have a space option).
Next step is to try to solve this using gridExtra:
# create each of the three plots, don't worry about legend for now
p1 <- ggplot(data[data$TIME==1,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
p2 <- ggplot(data[data$TIME==2,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
p3 <- ggplot(data[data$TIME==3,], aes(x=ID, y=VAL, fill=TYPE)) +
facet_wrap(~ TIME, ncol=1) +
geom_bar(position="stack", show_guide=FALSE) +
coord_flip()
# use similar arithmetic to try and get layout correct
require(gridExtra)
heights <- c(5, 3, 1) / sum(5, 3, 1)
print(arrangeGrob(p1 ,p2, p3, ncol=1,
heights=heights))
You'll notice I used the same arithmetic previously suggested based off the number of bars per facet, but in this case it ends up horribly wrong. This seems to be because there are extra "constant height" elements that I need to take into consideration in the math.
Another complication (I believe) is that the final output (and whether or not the widths match) will also depend on the width and height of where I'm outputting the final grob to, whether its in a R/RStudio environment, or to a PNG file.
How can I accomplish this?
Something like this appear to work, but it doesn't - not completely. It has the appearance of working because the levels of the ID factor are sequential. Anything else, and scale = "free" fails. But it might be possible to develop further. The method uses facet_grid, and thus space = "free" can be used. The method uses geom_rect to layer differently coloured rectangles on top of each other. It needs cumulative sums to be calculated so that the right-hand edge of each rectangle can be positioned.
data <- data.frame(ID=factor(c(rep(1,9), rep(2,6), rep(3,6), rep(4,3), rep(5,3))),
TYPE=factor(rep(1:3,3)),
TIME=factor(c(1,1,1,2,2,2,3,3,3,1,1,1,2,2,2,1,1,1,2,2,2,1,1,1,1,1,1)),
VAL=runif(27))
library(ggplot2)
library(plyr)
# Get the cumulative sums
data = ddply(data, .(ID, TIME), mutate, CUMSUMVAL = cumsum(VAL))
ggplot(data, aes(x=VAL, y = as.numeric(ID), fill=TYPE)) +
geom_rect(data = subset(data, TYPE == 3), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
geom_rect(data = subset(data, TYPE == 2), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
geom_rect(data = subset(data, TYPE == 1), aes(xmin = 0, xmax = CUMSUMVAL, ymin = as.numeric(ID)-.2, ymax = as.numeric(ID)+.2)) +
facet_grid(TIME~., space = "free", scale="free") +
scale_y_continuous(breaks = c(1:5), expand = c(0, 0.2))
EDIT: OR really thick lines work a little better (I think)
ggplot(data, aes(x=VAL, y = ID, colour=TYPE)) +
geom_segment(data = subset(data, TYPE == 3), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 2), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 1), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
facet_grid(TIME~., space = "free", scale="free")
Additional Edit Taking the data from your earleir post, and modifying it a little.
Updated opts is deprecated; using theme instead.
df <- structure(list(ID = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L,
2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 5L,
5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L), .Label = c("a",
"b", "c", "d", "e", "f", "g"), class = "factor"), TYPE = structure(c(1L,
2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L,
1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L,
5L, 6L, 1L, 2L, 3L), .Label = c("1", "2", "3", "4", "5", "6",
"7", "8"), class = "factor"), TIME = structure(c(2L, 2L, 2L,
2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L,
2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L,
1L, 1L, 1L), .Label = c("One", "Five", "Fifteen"), class = "factor"), VAL = c(0.937377670081332,
0.522220720537007, 0.278690102742985, 0.967633064137772, 0.116124767344445,
0.0544306698720902, 0.470229141646996, 0.62017166428268, 0.195459847105667,
0.732876230962574, 0.996336271753535, 0.983087373664603, 0.666449476964772,
0.291554537601769, 0.167933790013194, 0.860138458199799, 0.172361251665279,
0.833266809117049, 0.620465772924945, 0.786503327777609, 0.761877260869369,
0.425386636285111, 0.612077651312575, 0.178726130630821, 0.528709076810628,
0.492527724476531, 0.472576208412647, 0.0702785139437765, 0.696220921119675,
0.230852259788662, 0.359884874196723, 0.518227979075164, 0.259466265095398,
0.149970305617899, 0.00682218233123422, 0.463400925742462, 0.924704828299582,
0.229068386601284)), .Names = c("ID", "TYPE", "TIME", "VAL"), row.names = c(NA,
-38L), class = "data.frame")
library(ggplot2)
library(plyr)
data = ddply(df, .(ID, TIME), mutate, CUMSUMVAL = cumsum(VAL))
ggplot(data, aes(x=VAL, y = ID, colour=TYPE)) +
geom_segment(data = subset(data, TYPE == 6), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 5), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 4), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 3), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 2), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
geom_segment(data = subset(data, TYPE == 1), aes(x = 0, xend = CUMSUMVAL, y = ID, yend = ID), size = 10) +
facet_grid(TIME~., space = "free", scale="free") +
theme(strip.text.y = element_text(angle = 0))
Changing the gtable doesn't help, unfortunately, as the bar width is in relative units,
g = ggplot_gtable(ggplot_build(p))
panels = which(sapply(g$heights, attr, "unit") == "null")
g$heights[[panels[1]]] <- unit(5, "null")
g$heights[[panels[2]]] <- unit(3, "null")
g$heights[[panels[3]]] <- unit(1, "null")
grid.draw(g)

Resources