GEOM_BAR (Stacked Bar)

333 views
Skip to first unread message

pradip...@samhsa.hhs.gov

unread,
Aug 28, 2011, 5:50:58 PM8/28/11
to ggplot2, pradip...@samhsa.hhs.gov

Dear Rexpert,

Although I was able to produce the plot (ggplot GEOM_BAR - stacked), I
could not resolve three issues for which I am requesting your help.

1) For some of the levels of x (), the values of y () with respect to
fill () are not in the correct segment of the bar (I can send you the
plot that I have produced and the data set that I have used, if
needed).

2) The opts () element “axis.title.y = theme_text(size = 16,
vjust=2,face = "bold")” did not produce the desired results. I could
not figure out why the ylab () is not bold in the plot.

3) The graph, which I have produced, is based on one of the eight
subsets of the main data set. May I request tips to write a function
with two arguments [datasets, levels of fill ()] that would produce
plots for 8 subsets of data in a single application?

Thanks in advance,

Pradip Muhuri




###### Below is the code for one subset

setwd ("C:/Documents and Settings/pradip.muhuri/My Documents")
library (ggplot2)

load("dfdescript.rdata")

# create 8 subsets of data
pop_mar <- subset(dfdescript,flag=="mar")
pop_edu <- subset(dfdescript,flag=="edu")
pop_inc <- subset(dfdescript,flag=="inc")
pop_ins <- subset(dfdescript,flag=="ins")
pop_emp <- subset(dfdescript,flag=="emp")
pop_fsp <- subset(dfdescript,flag=="fsp")
pop_pua <- subset(dfdescript,flag=="pua")
pop_wel <- subset(dfdescript,flag=="wel")

# The following code is used to plot geom_bar (the percent
distribution (y) with respect to
# the fill () factor for each level of x() using the "pop_mar" data
subset created above


pop_mar$race <- ordered (factor(pop_mar$race,levels=c
(5,2,4,7,1,6,3),
labels=c("Asian","Black","HI/
OPI","Hispanic","White",
"Multi Race","AI/AN")))

pop_mar$fill_var <- ordered (factor(pop_mar$fill_var, levels=c
(2,3,1),
labels=c("Never Married","Div/Wid/Sep",
"Married")))

p <- ggplot(pop_mar, aes(x=race, y=percentage, fill=fill_var))+
geom_bar(stat="identity")+
geom_text(aes(label=percentage,
y=percentage, angle=90, vjust =0,
face="bold")) +
scale_fill_manual(values=c("Married"="red",
"Never
Married"="skyblue",
"Div/Wid/Sep"=
"yellow"))+
coord_flip()+
xlab (" ") +
ylab("Percentage")+
labs (fill=" ")+


opts(legend.position="bottom",legend.direction="horizontal",
legend.text = theme_text(colour="black", size =
16, face = "bold"),
panel.grid.major=theme_blank(),
panel.grid.minor=theme_blank(),
title="Marital Status by Race/Ethnicity",
axis.text.y=theme_text(size=16, hjust=1,
face="bold"),
axis.text.x=theme_text(size=16, hjust=1,
face="bold"),
axis.title.y = theme_text(size = 16, vjust=2,face
= "bold"),
panel.background=theme_rect(),
plot.title = theme_text(size=24, face="bold")
)

print (p)
ggsave(file="marital.png", width=7, height=7)

Dennis Murphy

unread,
Aug 29, 2011, 4:11:54 AM8/29/11
to pradip...@samhsa.hhs.gov, ggplot2
Hi:

It appears you need a reminder of the concept of a reproducible
example, since neither of your first two questions can really be
answered directly without one.
http://gist.github.com/270442

I used the UCBAdmissions data set from the autoloaded datasets()
package, trying to adapt the core of your opts to the example (with
adjustments to make it look halfway presentable to me - e.g., dropping
the x-axis label).

u <- as.data.frame.table(UCBAdmissions)
ggplot(u, aes(x = Gender, y = Freq)) +
theme_bw() +
geom_bar(aes(fill = Admit), stat = 'identity') +
facet_wrap(~ Dept) +
scale_fill_manual(values = c('Admitted' = 'red',
'Rejected' = 'skyblue')) +
labs(x = '', y = 'Frequency', fill = 'Decision') +
opts(legend.position="bottom",


legend.direction="horizontal",
legend.text = theme_text(colour="black",

size = 14,
face = "bold"),
legend.title = theme_text(size = 20, face = 'bold'),
panel.grid.major=theme_blank(),
panel.grid.minor=theme_blank(),
title="Admission status by gender/department",
axis.text.y=theme_text(size=14, face="bold"),
axis.text.x = theme_text(size = 14, face = 'bold'),
axis.title.y = theme_text(size = 20, angle = 90, vjust = 0.2,
face = "bold"),
# panel.background=theme_rect(),


plot.title = theme_text(size=24, face="bold")
)

More inline.

On Sun, Aug 28, 2011 at 2:50 PM, pradip...@samhsa.hhs.gov
<pradip...@samhsa.hhs.gov> wrote:
>
> Dear Rexpert,
>
> Although I was able to produce the plot (ggplot GEOM_BAR - stacked), I
> could not resolve three issues for which I am requesting your help.
>
> 1)      For some of the levels of x (), the values of y () with respect to
> fill () are not in the correct segment of the bar (I can send you the
> plot that I have produced and the data set that I have used, if
> needed).

This cannot be answered fully without data and some statement about
your expectations in relation to the plot rendered.


>
> 2)      The opts () element “axis.title.y = theme_text(size = 16,
> vjust=2,face = "bold")” did not produce the desired results. I could
> not figure out why the ylab () is not bold in the plot.

Your combination of vertical justification and angle pulled the title
off the graphics page; it's there, but outside the plotting region.
Notice the value of vjust and angle used in the example.

>
> 3)      The graph, which I have produced, is based on one of the eight
> subsets of the main data set. May I request tips to write a function
> with two arguments [datasets, levels of fill ()] that would produce
> plots for 8 subsets of data in a single application?

facet_wrap(~ race). See the example above to illustrate its use.

Dennis

> --
> You received this message because you are subscribed to the ggplot2 mailing list.
> Please provide a reproducible example: http://gist.github.com/270442
>
> To post: email ggp...@googlegroups.com
> To unsubscribe: email ggplot2+u...@googlegroups.com
> More options: http://groups.google.com/group/ggplot2
>

Muhuri, Pradip (SAMHSA/CBHSQ)

unread,
Aug 29, 2011, 9:56:33 AM8/29/11
to Dennis Murphy, ggplot2

Dear Dennis,

1) To further explain the issues, I am attaching the R Script, the R data and the .png file. Sorry for any inconvenience due to my sending these files. In my application, I have used the geom_text, which is not in your adapted example script. The issue is that the printed y-values are not in the right segment (fill) of the bar and that the % is not pasted to the y-value (e.g., 40%). Would please show me the code in the geom_text ?

2) I don't need the facet wrap. I would like to produce similar plots from 7 other subsets (data). Each of the 8 data files has a fill () that is different (2,3, and 4 levels, with unique labels). Would I not need to write a user-defined function with two arguments (data set, and flag - a variable in the data set)?

Thank you for your continued support,

Pradip


Pradip K. Muhuri, PhD
Statistician
Substance Abuse & Mental Health Services Administration
The Center for Behavioral Health Statistics and Quality
Division of Population Surveys
1 Choke Cherry Road, Room 7-1023
Rockville, MD 20857

Tel: 240-276-1070
Fax: 240-276-1260
e-mail: Pradip...@samhsa.hhs.gov

The Center for Behavioral Health Statistics and Quality your feedback. Please click on the following link to complete a brief customer survey: http://cbhsqsurvey.samhsa.gov

marital.png
ByRaceDescriptiveStat_all.R
dfdescript.rdata

Dennis Murphy

unread,
Aug 29, 2011, 4:29:23 PM8/29/11
to Muhuri, Pradip (SAMHSA/CBHSQ), ggplot2
Hi:

Thank you for the reproducible example. Hopefully the code below will be of use.

I was hoping to say that one could write a single plot function and
then apply it across all subsets of the data in one shot, but after
looking at the data briefly, it doesn't make sense to do so since the
subsets are qualitatively different and the number of levels of
fill_var change across the different data subsets. Evidently the
various subsets (flag) are qualitatively different tables. It appears
that you'll have to do some work by hand to arrange the sub-data
frames such that they can be called within the plot function defined
below. In particular, you probably want to make the plot title an
argument of the function, which I show below.

I'll illustrate how I modified the data and plot code for the first
subset and leave the rest to you. Since race appears to be consistent
throughout the data, you may want to do this first: By doing this up
front, you won't have to repeat it for each data subset.

# library('ggplot2')
# library('plyr')
dfdescript <- within(dfdescript, {
race <- factor(race, levels=c (5,2,4,7,1,6,3),
labels=c('Asian', 'Black', 'HI/OPI', 'Hispanic',
'White', 'Multi Race', 'AI/AN')) } )

# Step 1: Define the data subset and redefine race and fill_var to be factors.

pmar <- subset(dfdescript, flag == 'mar')
pmar <- within(pmar, {
# race <- factor(race, levels=c (5,2,4,7,1,6,3),
# labels=c('Asian', 'Black', 'HI/OPI', 'Hispanic',
# 'White', 'Multi Race', 'AI/AN'))
fill_var <- factor(fill_var, levels = c(2, 3, 1),
labels=c('Never
Married','Div/Wid/Sep', 'Married'))
} )
# Sort data by race and fill_var [uses arrange() from plyr]
pmar <- arrange(pmar, race, fill_var)
# Cumulate the percentages to use as a plotting position in the plot function
pmar <- ddply(pmar, .(race), transform, cpct = cumsum(percentage))
# Generate a plot title
martitle <- 'Marital Status by Race/Ethnicity'

plotfun <- function(dd, plotTitle, outFile)
{
print(ggplot(pmar, aes(x = race, y = percentage, fill = fill_var)) +
geom_bar(stat = 'identity') +
geom_text(aes(y = cpct - 4, label = percentage), size = 5) +


scale_fill_manual(values=c('Married'='red',
'Never Married'='skyblue',

'Div/Wid/Sep'= 'yellow')) +
coord_flip()+
labs (x = "", y = "Percentage", fill=" ") +


opts(legend.position="bottom",
legend.direction="horizontal",

legend.text = theme_text(colour="black", size = 16,


face = "bold"),
panel.grid.major=theme_blank(),
panel.grid.minor=theme_blank(),

title=plotTitle,


axis.text.y=theme_text(size=16, hjust=1, face="bold"),

axis.text.x=theme_text(size=16, face="bold"),
axis.title.y = theme_text(size = 20, face = "bold"),


# panel.background=theme_rect(),
plot.title = theme_text(size=24, face="bold")
)

)
ggsave(outFile)
}

plotfun(pmar, martitle, 'mar.png')

In plotfun, I offset the cumulative percentages by -4; it appears to
be a value that works for all the table subsets and keeps the
percentages from abutting the boundaries of the stacked bars. I think
you should do something with the percentage label, but I don't have
any firm recommendations. The label in the current version of the plot
function is neither bold nor enlarged and looks out of place IMO.

You should just be able to redefine the levels of fill_var for each
subset table and then call plotfun() with a suitable main title and
output file name. If you don't want to see the plot on-screen, make
this the last line of plotfun():

invisible()

HTH,
Dennis

Muhuri, Pradip (SAMHSA/CBHSQ)

unread,
Aug 30, 2011, 10:35:03 AM8/30/11
to Dennis Murphy, ggplot2
Dennis,

Thank you so much for extending extraordinary help with the code. ... very helpful.

Stuart Luppescu

unread,
Aug 30, 2011, 4:56:52 PM8/30/11
to ggp...@googlegroups.com
On Mon, 2011-08-29 at 13:29 -0700, Dennis Murphy wrote:
[snip]
Dennis, I was just looking for a way to do this when I noticed your
message. It helped me tremendously. Thank you. However, there are still
two things I need help with.

I have a data frame produced by ddply that looks like this:

tab3.1
ext.obs comp Var1 Freq Rating
1 HC\nObserver Student\n Interaction 1 6.557377 1
2 HC\nObserver Student\n Interaction 2 21.639344 2
3 HC\nObserver Student\n Interaction 3 47.868852 3
4 HC\nObserver Student\n Interaction 4 23.934426 4
5 Prin\n/AP Student\n Interaction 1 4.774536 1
6 Prin\n/AP Student\n Interaction 2 19.098143 2
7 Prin\n/AP Student\n Interaction 3 68.169761 3
8 Prin\n/AP Student\n Interaction 4 7.957560 4
9 HC\nObserver Culture \nfor Learning 1 8.802817 1
10 HC\nObserver Culture \nfor Learning 2 23.239437 2
11 HC\nObserver Culture \nfor Learning 3 48.943662 3
12 HC\nObserver Culture \nfor Learning 4 19.014085 4
13 Prin\n/AP Culture \nfor Learning 1 1.944444 1
14 Prin\n/AP Culture \nfor Learning 2 28.333333 2
15 Prin\n/AP Culture \nfor Learning 3 61.666667 3
16 Prin\n/AP Culture \nfor Learning 4 8.055556 4

and so on for 34 more rows.

I'm plotting 5 pairs of stacked bars one for each comp. In each pair,
there is one set of stacked bars for HC Observer and one set for
Prin/AP. This is the code I'm using:

plotTitle <- "TfL Learning Environment"

ggplot(tab3.1, aes(x = ext.obs, y = Freq, fill = Var1)) +


geom_bar(stat = 'identity') +

labs (x = "", y = "Percentage", fill=" ") +

scale_fill_brewer(name="Rating", palette="BrBG") +
geom_text(aes(data=tab3.1, ext.obs, y=Freq, label=paste(round(Freq,
1), "%", sep='')), size=3) +

facet_grid(. ~ comp)

opts(title=plotTitle)

Here are the problems:
1) The overall plot title doesn't appear. When I did this graph in
traditional R graphics I used par(mfrow=c(1, 5)) and mtext.
2) I would like the legend to appear reversed, with 1 at the bottom and
4 at the top to correspond to the vertical order of the stacked bars.

Thanks for any help you can give me.

--
Stuart Luppescu -=- slu .at. ccsr.uchicago.edu
University of Chicago -=- CCSR
才文と智奈美の父 -=- Kernel 2.6.39-gentoo-r3
I want a budget. -- Peter Dalgaard (in a talk
about Tcl/Tk, meaning to say 'I want a button
widget') gR 2003, Aalborg (September 2003)






Dennis Murphy

unread,
Aug 30, 2011, 7:50:00 PM8/30/11
to Stuart Luppescu, ggp...@googlegroups.com
Hi:

How about this:

library('ggplot2')
library('plyr')
library('RColorBrewer')
tab3.1 <- read.table(textConnection("


ext.obs comp Var1 Freq Rating
1 'HC\nObserver' 'Student\n Interaction' 1 6.557377 1
2 'HC\nObserver' 'Student\n Interaction' 2 21.639344 2
3 'HC\nObserver' 'Student\n Interaction' 3 47.868852 3
4 'HC\nObserver' 'Student\n Interaction' 4 23.934426 4
5 'Prin\n/AP' 'Student\n Interaction' 1 4.774536 1
6 'Prin\n/AP' 'Student\n Interaction' 2 19.098143 2
7 'Prin\n/AP' 'Student\n Interaction' 3 68.169761 3
8 'Prin\n/AP' 'Student\n Interaction' 4 7.957560 4
9 'HC\nObserver' 'Culture \nfor Learning' 1 8.802817 1
10 'HC\nObserver' 'Culture \nfor Learning' 2 23.239437 2
11 'HC\nObserver' 'Culture \nfor Learning' 3 48.943662 3
12 'HC\nObserver' 'Culture \nfor Learning' 4 19.014085 4
13 'Prin\n/AP' 'Culture \nfor Learning' 1 1.944444 1
14 'Prin\n/AP' 'Culture \nfor Learning' 2 28.333333 2
15 'Prin\n/AP' 'Culture \nfor Learning' 3 61.666667 3

16 'Prin\n/AP' 'Culture \nfor Learning' 4 8.055556 4"),
header = TRUE, stringsAsFactors = FALSE)
closeAllConnections()


plotTitle <- "TfL Learning Environment"

# Use the new mutate() function, a faster version of transform()
# cfrq computes cumulative percent frequencies by
# ext.obs/comp combinations
tab3.1m <- mutate(tab3.1, Var1 = factor(Var1, levels = 1:4),
cfrq = ave(Freq, list(ext.obs, comp), FUN =
cumsum))

ggplot(tab3.1m, aes(x = ext.obs, y = Freq, fill = Var1)) +


geom_bar(stat = 'identity') +
labs (x = "", y = "Percentage", fill=" ") +

scale_fill_brewer(name="Rating", palette="BrBG",
breaks = rev(levels(tab3.1m$Var1))) +
geom_text(aes(y = cfrq - 1,


label=paste(round(Freq, 1), "%", sep='')),
size=3) +

facet_grid(. ~ comp) +
opts(title = plotTitle)

HTH,
Dennis

Stuart Luppescu

unread,
Aug 30, 2011, 7:58:59 PM8/30/11
to ggp...@googlegroups.com
On Tue, 2011-08-30 at 16:50 -0700, Dennis Murphy wrote:
> plotTitle <- "TfL Learning Environment"
>
> # Use the new mutate() function, a faster version of transform()
> # cfrq computes cumulative percent frequencies by
> # ext.obs/comp combinations
> tab3.1m <- mutate(tab3.1, Var1 = factor(Var1, levels = 1:4),
> cfrq = ave(Freq, list(ext.obs, comp), FUN =
> cumsum))
>
> ggplot(tab3.1m, aes(x = ext.obs, y = Freq, fill = Var1)) +
> geom_bar(stat = 'identity') +
> labs (x = "", y = "Percentage", fill=" ") +
> scale_fill_brewer(name="Rating", palette="BrBG",
> breaks = rev(levels(tab3.1m$Var1))) +
> geom_text(aes(y = cfrq - 1,
> label=paste(round(Freq, 1), "%", sep='')),
> size=3) +
> facet_grid(. ~ comp) +
> opts(title = plotTitle)

Dennis, That's awesome. I didn't even ask about locating the text
annotations, and did a cumbersome thing with a for() loop, but your
solution is far more elegant. Thanks!!


--
Stuart Luppescu -=- slu .at. ccsr.uchicago.edu
University of Chicago -=- CCSR
才文と智奈美の父 -=- Kernel 2.6.39-gentoo-r3

Spencer Graves: What do you think about adding a
"No RTFM" policy to the R mailing lists? Michael
Dewey: You raise an interesting point but the
responses to your post remind us that people (and
indeed whole cultures) are not all situated at the
same point on the continuum of directness between
"It's a cow, stupid" and "From this side it looks
not unlike a cow". -- Spencer Graves and
Michael Dewey (reply after a long discussion of a

Reply all
Reply to author
Forward
0 new messages