Height of bars when conditioning on one and two factors using geom_bar not what expected

60 views
Skip to first unread message

Alan

unread,
Feb 9, 2015, 10:52:12 AM2/9/15
to ggp...@googlegroups.com
I am trying to create side-by-side comparisons using `geom_bar` while conditioning on one and two factors.  Below I simulate the data frame `DF`.


set.seed(13)
fac1
<- factor(sample(letters[1:5], 500, replace = TRUE))
fac2
<- factor(sample(LETTERS[1:3], 500, replace = TRUE))
sex
<- factor(sample(c("Male", "Female"), 500, replace = TRUE))
DF
<- data.frame(fac1, fac2, sex)
rm
(fac1, fac2, sex)
head
(DF)




When there is a single factor, I get what I would expect with the code below.


T1 <- xtabs(~ fac1, data = DF)
PT1
<- prop.table(T1)
PT1
ggplot
(data = DF, aes(x = fac1)) +
  geom_bar
(aes(y = ..count../sum(..count..))) +
  geom_hline
(y = PT1[1], linetype = "dashed")



I want to create barplots of the first factor (`fac1`) while conditioning on the second factor (`fac2`).  I am expecting bars to be of the heights in `PT2` with the following code.  However, the values are not what I was expecting.  Specifically, I expected the height of the bars for each level of `fac2` to sum to 1 but in fact they are greater than 1 for the three `facets`.  Any pointers as to what the code is actually giving me would be most appreciated. 


T2 <- ftable(DF$fac2, DF$fac1)
PT2
<- prop.table(T2, 1)
PT2
ggplot
(data = DF, aes(x = fac1)) +
  facet_grid
(fac2 ~.) +
  geom_bar
(aes(y = ..density.., group = fac2)) +
  geom_hline
(y = PT2[1, 1], linetype = "dashed")




I would like to create barplots conditioning on both `fac2` and `sex` such that the height of the bars would agree with the values in `PT3`.  The following code returns values that are similar to PT3  but not the exact values in PT3 I was expecting.   I was expecting the heights of the bars for `Male` and `Female` in each `facet` to sum to 1 (they are generally something over 1). Any pointers as to what the code is actually returning and or pointers to create the desired barplots with heights in agreement with `PT3` would be most appreciated. 


T3 <- ftable(DF$fac2, DF$sex, DF$fac1)
PT3
<- prop.table(T3, 1)
PT3
ggplot
(data = DF, aes(fac1)) +
  geom_bar
(position = "dodge", aes(y = ..density.., fill = sex, group = sex)) +
  facet_grid
(fac2 ~.) +
  geom_hline
(y = PT3[1, 1], linetype = "dashed")



I am attaching an *Rmd file If anyone want to run the code that way.  TIA. 
SH.Rmd

Alan

unread,
Feb 9, 2015, 11:03:08 AM2/9/15
to ggp...@googlegroups.com
I omitted loading the ggplot2 package in the previous *.Rmd file.  ... this one should includes loading `ggplot2`.
SH.Rmd

Dennis Murphy

unread,
Feb 9, 2015, 1:03:15 PM2/9/15
to Alan, ggplot2
Hi:

Is this what you're after?

ggplot(DF, aes(x = fac2, fill = fac1)) +
theme_bw() +
geom_bar(position = "fill") +
ylab("Proportion")


Dennis
> --
> --
> You received this message because you are subscribed to the ggplot2 mailing
> list.
> Please provide a reproducible example:
> https://github.com/hadley/devtools/wiki/Reproducibility
>
> To post: email ggp...@googlegroups.com
> To unsubscribe: email ggplot2+u...@googlegroups.com
> More options: http://groups.google.com/group/ggplot2
>
> ---
> You received this message because you are subscribed to the Google Groups
> "ggplot2" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to ggplot2+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Alan

unread,
Feb 9, 2015, 2:45:45 PM2/9/15
to ggp...@googlegroups.com, arnh...@appstate.edu
Dennis,

Thanks for the reply.  I would like the y axis to represent the conditional percentage for each category.  Given:
> T2 <- ftable(DF$fac2, DF$fac1)
> PT2 <- prop.table(T2, 1)
> PT2
           a         b         c         d         e
                                                    
A  0.2173913 0.1677019 0.2111801 0.1987578 0.2049689
B  0.2485207 0.1656805 0.2307692 0.1597633 0.1952663
C  0.2470588 0.1470588 0.2176471 0.2176471 0.1705882

Your suggestion does ensure the probabilities are 1.  However, what I 
I would like is for the the hight of the bars (in the code below) in the first facet  to be 0.2173913, 0.1677019, 0.2111801, 0.1987578, and 0.2049689....the height of the bars in the second facet to be 0.2485207 0.1656805 0.2307692 0.1597633 0.1952663....the height of the bars in the third facet to be 
0.2470588 0.1470588 0.2176471 0.2176471 0.1705882.  I have a line dashed line in the first facet where I was expecting the height of the first bar to extend up to. (It goes higher).  

ggplot(data = DF, aes(x = fac1)) + 
  facet_grid(fac2 ~.) +
  geom_bar(aes(y = ..density.., group = fac2)) +
  geom_hline(y = PT2[1, 1], linetype = "dashed")



Dennis Murphy

unread,
Feb 9, 2015, 7:14:14 PM2/9/15
to Alan, ggplot2
Faceting implies you want a separate bar chart of fac1 for each level
of fac2 (or vice versa). Since it's not at all clear to me what you
want, let's try a few options and hopefully your expected graph is
somewhere in the mix.

When dealing with conditional relative frequencies, you want to
compute them outside of ggplot2 because stat_bin() doesn't behave
properly.

## --------- begin code chunk 1
set.seed(13)
fac1 <- factor(sample(letters[1:5], 500, replace = TRUE))
fac2 <- factor(sample(LETTERS[1:3], 500, replace = TRUE))
sex <- factor(sample(c("Male", "Female"), 500, replace = TRUE))
DF <- data.frame(fac1, fac2, sex)
rm(fac1, fac2, sex)

library(ggplot2)

# Start with the initial data frame
# Stacked bar charts of fac1 by levels of fac2

ggplot(DF, aes(x = fac2, fill = fac1)) +
theme_bw() +
geom_bar(position = "fill") +
ylab("Proportion")

# Separate bar charts of fac1 by levels of fac2
# Doesn't work because sum(..count..) from stat_bin() is
# the overall sum 500, not the conditional sums

ggplot(DF, aes(x = fac1)) +
geom_bar(aes(y = ..count../sum(..count..)), fill = "blue") +
facet_wrap(~ fac2)
## --------- end code chunk 1


Instead, generate a data frame of conditional relative frequencies and
pass that into ggplot(). This isn't too difficult to do in plyr,
although you could use dplyr, too. You could also convert your
prop.table() into a suitable data frame if you wish.

##------- begin code chunk 2
library(plyr)
# Use the count() function to generate a 3-way table of frequencies
# in data frame form in case you want to do something with gender later
T0 <- count(DF, c("fac1", "fac2", "sex"))

# Sum the frequencies over sex for each fac1/fac2 combination
T1 <- ddply(T0, .(fac1, fac2), summarise, freq = sum(freq))
## Equivalent to
## T1 <- count(DF, c("fac1", "fac2"))

# Compute the conditional proportions of fac1 within levels of fac2
T2 <- ddply(T1, "fac2", mutate, prop = freq/sum(freq))

# Separate facet for each level of fac2
# T2 is a data frame
ggplot(T2, aes(x = fac1, y = prop)) +
geom_bar(stat = "identity", fill = "blue") +
facet_wrap(~ fac2)

# Dodge fac1 by level of fac2
ggplot(T2, aes(x = fac2, y = prop, fill = fac1)) +
geom_bar(stat = "identity", position = "dodge")

# Stack fac2 by levels of fac1
# position = "fill" will look exactly the same with this input
ggplot(T2, aes(x = fac2, y = prop, fill = fac1)) +
geom_bar(stat = "identity", position = "stack")

## ------- end code chunk 2

Dennis

Alan

unread,
Feb 10, 2015, 8:30:34 AM2/10/15
to ggp...@googlegroups.com, arnh...@appstate.edu
Dennis,

Thanks for the detailed response.  I really appreciate your expertise here. 

Alan-
Reply all
Reply to author
Forward
0 new messages