Creating a Stacked Bar Chart Centered on Zero Using Ggplot

Creating a stacked bar chart centered on zero using ggplot

Here's what I mean:

library(scales)
dat <- read.csv("~/Downloads/bench.csv",stringsAsFactors = TRUE)
dat$Time1 <- ifelse(dat$Phase %in% c('Decode','Deserialize'),-dat$Time,dat$Time)

up <- dat[dat$Time1 >= 0,]
down <- dat[dat$Time1 < 0,]

commapos <- function(x, ...) {
format(abs(x), big.mark = ",", trim = TRUE,
scientific = FALSE, ...)
}

ggplot() +
geom_bar(data = up,aes(x = Protocol,y = Time1,fill = as.integer(Phase)),stat = "identity") +
geom_bar(data = down,aes(x = Protocol,y = Time1,fill = as.integer(Phase)),stat = "identity") +
scale_y_continuous(labels = commapos) +
coord_flip()

The commapos formatter I actually just grabbed from my own existing code from when I've done similar things. You might not want that exact formatting, but the abs(x) is the key part.

Also, note that your Phase variable was a character/factor, and so to get the color bar from your original I had to coerce back to integer.

And, as I mentioned ggplot will complain about stacking not being well defined when using values less than zero. If you try this without separating the data frame into positive/negative values you'll see why.

Back to back bar chart with three levels: Can I center the plot?

I'm not sure this is the best way to do that, but here is a way to do that:

library(dplyr)
d4pos <- d4 %>%
filter(reg != 2018) %>%
group_by(reg, plot) %>%
summarise(total = sum(n)) %>%
ungroup() %>%
mutate(total = total * ifelse(reg == "Both", .5, 1))
d4neg <- d4 %>%
filter(reg != 2017) %>%
group_by(reg, plot) %>%
summarise(total = - sum(n)) %>%
ungroup() %>%
mutate(total = total * ifelse(reg == "Both", .5, 1))

ggplot(data = d4pos, aes(x = plot, y = total, fill = reg)) +
geom_bar(stat = "identity") +
geom_bar(data = d4neg, stat = "identity", aes(x = plot, y = total, fill = reg)) +
coord_flip()

I generate two data frames for the total of each group. One contains the 2017 and (half of) Both, and the other contains the rest. The value for the 2018 data frame is flipped to plot on the negative side.

The output looks like this:

Sample Image

EDIT
If you want to have positive values in both directions for the horizontal axis, you can do something like this:

ggplot(data = d4pos, aes(x = plot, y = total, fill = reg)) + 
geom_bar(stat = "identity") +
geom_bar(data = d4neg, stat = "identity", aes(x = plot, y = total, fill = reg)) +
scale_y_continuous(breaks = seq(-50, 50, by = 25),
labels = abs(seq(-50, 50, by = 25))) +
coord_flip()

geom_text position middle on the stacked bar chart

First use geom_bar and set stat = "identity". After that use position = position_stack(vjust = 0.5). You can use the following code:

# Construct a ggplot object according requirement above and assign it to 'plt'
plt <- ggplot(plotdata,aes(x = sector, y = n, fill = sex))+
geom_bar(stat="identity")+
geom_text(aes(label=n), position = position_stack(vjust = 0.5))+
labs(x = "",
y = "Number of persons",
title = "")

# Display the stacked bar chart
plt

Output:

Sample Image

Showing data values on stacked bar chart in ggplot2

From ggplot 2.2.0 labels can easily be stacked by using position = position_stack(vjust = 0.5) in geom_text.

ggplot(Data, aes(x = Year, y = Frequency, fill = Category, label = Frequency)) +
geom_bar(stat = "identity") +
geom_text(size = 3, position = position_stack(vjust = 0.5))

Sample Image

Also note that "position_stack() and position_fill() now stack values in the reverse order of the grouping, which makes the default stack order match the legend."


Answer valid for older versions of ggplot:

Here is one approach, which calculates the midpoints of the bars.

library(ggplot2)
library(plyr)

# calculate midpoints of bars (simplified using comment by @DWin)
Data <- ddply(Data, .(Year),
transform, pos = cumsum(Frequency) - (0.5 * Frequency)
)

# library(dplyr) ## If using dplyr...
# Data <- group_by(Data,Year) %>%
# mutate(pos = cumsum(Frequency) - (0.5 * Frequency))

# plot bars and add text
p <- ggplot(Data, aes(x = Year, y = Frequency)) +
geom_bar(aes(fill = Category), stat="identity") +
geom_text(aes(label = Frequency, y = pos), size = 3)

Resultant chart

Positioning values of stacked barchart in the center using ggplot2

You no longer need to calculate the position variable. Starting in ggplot2_2.2.0, text can be stacked and centered via position_stack.

The relevant line of geom_text code is below. Notice I don't need to use the "pos" variable at all for text placement.

geom_text(aes(label=paste0(value,"%")),
position = position_stack(vjust = .5), size = 2, color = "black")

The code for the plot and the resulting graph:

ggplot(dre, aes(factor(sex), value, fill = variable)) + 
geom_col() +
facet_wrap(factor(year)~area) +
geom_text(aes(label = paste0(value,"%")),
position = position_stack(vjust = .5), size = 2, color = "black") +
coord_flip() +
theme_bw() +
scale_fill_brewer()

Sample Image

Center a grouped bar chart in R (ggplot2)

The simplest solution is to use the preserve= argument of position_dodge2(). When you remove your "0" values, as you observed, the bars are not equivalent widths. This is the principle behind the preserve= argument of position_dodge2(), which is to say, should the widths of the bars be preserved across each x value("total") or preserve the width of all bars within all x values ("single")? The second is what you want here.

What's the difference between position_dodge() and position_dodge2()? Well, position_dodge() works, but does not center the groupings on the x value (your original problem). position_dodge2() does just that:

PreBar <- PreBar[which(PreBar$Freq!=0),]  # remove your zeros

library(ggplot2)
ggplot(data=PreBar, aes(x=Age, y=Freq, fill=PreCond)) +
geom_bar(position=position_dodge2(preserve='single'), stat="identity") +
theme_light() +
ylab("Percentage of Pre-existing Condition Among Positives") +
xlab("Age Category")

Sample Image



Related Topics



Leave a reply



Submit