How to Plot Bars and One Line on Two Y-Axes in the Same Chart, with R-Ggplot

How to plot bars and one line on two Y-axes in the same chart, with R-ggplot?

Solution by tweaking Kohske's example. This is results in similar plot to hrbrmstr's solution - completely agree over rethinking the plot.

library(ggplot2)
library(gtable)
library(reshape2)

# Data
featPerf <- data.frame( exp=c("1", "2", "3", "4"),
A=c(1000, 1000, 1000, 1000),
B=c(0, 5000, 5000, 5000),
C=c(1000, 5000, 10000, 0),
D=c(1000, 5000, 10000 ,20000),
accuracy=c(0.4, 0.5, 0.65, 0.9) )

# Barplot ------------------------------------------------
# Reshape data for barplot
df.m <- melt(featPerf[-6])

# Labels for barplot
df.m$barlab <- factor(paste("Experiment", df.m$exp) )

p1 <- ggplot(df.m , aes(x=barlab, y=value, fill=variable)) +
geom_bar( stat="identity", position="dodge") +
scale_fill_grey(start =.1, end = .7 ) +
xlab("Experiments") +
ylab("Number of Labels") +
theme(legend.position="top")
g1 <- ggplotGrob(p1)

# Lineplot ------------------------------------------------
p2 <- ggplot(featPerf , aes(x=exp, y=accuracy, group=1)) + geom_line(size=2) +
scale_y_continuous(limits=c(0,1)) +
ylab("Accuracy") +
theme(panel.background = element_rect(fill = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
g2 <- ggplotGrob(p2)

# Add plots together
pp <- c(subset(g1$layout, name == "panel", se = t:r))
g <- gtable_add_grob(g1, g2$grobs[[which(g2$layout$name == "panel")]], pp$t,
pp$l, pp$b, pp$l)

# Add second axis for accuracy
ia <- which(g2$layout$name == "axis-l")
ga <- g2$grobs[[ia]]
ax <- ga$children[[2]]
ax$widths <- rev(ax$widths)
ax$grobs <- rev(ax$grobs)
ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
g <- gtable_add_cols(g, g2$widths[g2$layout[ia, ]$l], length(g$widths) - 1)
g <- gtable_add_grob(g, ax, pp$t, length(g$widths) - 1, pp$b)

# Add second y-axis title
ia <- which(g2$layout$name == "ylab")
ax <- g2$grobs[[ia]]
# str(ax) # you can change features (size, colour etc for these -
# change rotation below
ax$rot <- 270
g <- gtable_add_cols(g, g2$widths[g2$layout[ia, ]$l], length(g$widths) - 1)
g <- gtable_add_grob(g, ax, pp$t, length(g$widths) - 1, pp$b)

grid.draw(g)

ggplot with 2 y axes on each side and different scales

Sometimes a client wants two y scales. Giving them the "flawed" speech is often pointless. But I do like the ggplot2 insistence on doing things the right way. I am sure that ggplot is in fact educating the average user about proper visualization techniques.

Maybe you can use faceting and scale free to compare the two data series? - e.g. look here: https://github.com/hadley/ggplot2/wiki/Align-two-plots-on-a-page

R - creating a bar and line on same chart, how to add a second y axis

Starting with version 2.2.0 of ggplot2, it is possible to add a secondary axis - see this detailed demo. Also, some already answered questions with this approach: here, here, here or here. An interesting discussion about adding a second OY axis here.

The main idea is that one needs to apply a transformation for the second OY axis. In the example below, the transformation factor is the ratio between the max values of each OY axis.

# Prepare data
library(ggplot2)
set.seed(2018)
df <- data.frame(x = c(1:5), y = abs(rnorm(5)*100))
df$y2 <- abs(rnorm(5))

# The transformation factor
transf_fact <- max(df$y)/max(df$y2)

# Plot
ggplot(data = df,
mapping = aes(x = as.factor(x),
y = y)) +
geom_col(fill = 'blue') +
# Apply the factor on values appearing on second OY axis
geom_line(aes(y = transf_fact * y2), group = 1) +
# Add second OY axis; note the transformation back (division)
scale_y_continuous(sec.axis = sec_axis(trans = ~ . / transf_fact,
name = "Second axis")) +
geom_label(aes(y = transf_fact * y2,
label = round(y2, 2))) +
theme_bw() +
theme(axis.text.x = element_text(angle = 20, hjust = 1))

Sample Image

But if you have a particular wish for the one-to-one transformation, like, say value 100 from Y1 should correspond to value 1 from Y2 (200 to 2 and so on), then change the transformation (multiplication) factor to 100 (100/1): transf_fact <- 100/1 and you get this:

Sample Image

The advantage of transf_fact <- max(df$y)/max(df$y2) is using the plotting area in a optimum way when using two different scales - try something like transf_fact <- 1000/1 and I think you'll get the idea.

ggplot - dual line chart and stacked bar chart on one plot

Something like this?

Sample Image

library(tidyverse)

data <- tibble(Month = 1:12,Brands = c(1,1,1,1,1,1,2,2,2,2,2,2),Generics = Brands + 1,Metric1 = c(5,5,5,5,5,5,6,6,7,8,9,10),Metric2 = c(10,10,11,11,12,13,14,15,16,17,18,19))

data %>%
pivot_longer(cols = c(Brands,Generics)) %>%
pivot_longer(cols = c(Metric1,Metric2),
names_to = "metric_name",values_to = "metric_value") %>%
ggplot(aes(Month))+
geom_col(aes(y = value, fill = name))+
geom_line(aes(y = metric_value, col = metric_name),size = 1.25)+
scale_x_continuous(breaks = 1:12)+
scale_color_manual(values = c("black","purple"))

Combining Bar and Line chart (double axis) in ggplot2

First, scale Rate by Rate*max(df$Response) and modify the 0.9 scale of Response text.

Second, include a second axis via scale_y_continuous(sec.axis=...):

ggplot(df)  + 
geom_bar(aes(x=Year, y=Response),stat="identity", fill="tan1", colour="sienna3")+
geom_line(aes(x=Year, y=Rate*max(df$Response)),stat="identity")+
geom_text(aes(label=Rate, x=Year, y=Rate*max(df$Response)), colour="black")+
geom_text(aes(label=Response, x=Year, y=0.95*Response), colour="black")+
scale_y_continuous(sec.axis = sec_axis(~./max(df$Response)))

Which yields:

Sample Image

Line plot with bars in secondary axis with different scales in ggplot2

Try this approach with scaling factor. It is better if you work with a scaling factor between your variables and then you use it for the second y-axis. I have made slight changes to your code:

library(tidyverse)
#Data
Month <- c("J","F","M","A")
Line <- c(2.5,2,0.5,3.4)
Bar <- c(30,33,21,40)
df <- data.frame(Month,Line,Bar)
#Scale factor
sfactor <- max(df$Line)/max(df$Bar)
#Plot
ggplot(df, aes(x=Month)) +
geom_line(aes(y = Line,group = 1)) +
geom_col(aes(y=Bar*sfactor))+
scale_y_continuous("Line",
sec.axis = sec_axis(trans= ~. /sfactor, name = "Bar"))

Output:

Sample Image

ggplot2 bar chart with two bars for each x value of data and two y-axis

Is that what you mean?

df=tribble(
~Id, ~Cat, ~Type, ~Value,
1, "A", "price", 13,
2, "A", "number", 5,
3, "B", "price", 19,
4, "B", "number", 12,
5, "C", "price", 8,
6, "C", "number", 11)

df %>% ggplot(aes(Cat))

df %>% ggplot(aes(x=Type, fill=Type, y=Value))+
geom_col()+
facet_grid(~Cat)

Sample Image

P.S.
I changed your values a bit because you could not see much when the differences were of the order of 10 ^ 7!

With these numbers, the logarithmic scale is better suited

df=tribble(
~Id, ~Cat, ~Type, ~Value,
1, "A", "price", 12745,
2, "A", "number", 5,
3, "B", "price", 34874368,
4, "B", "number", 143,
5, "C", "price", 84526,
6, "C", "number", 11)

df %>% ggplot(aes(x=Type, fill=Type, y=Value))+
geom_col()+
scale_y_continuous(trans='log10')+
facet_grid(~Cat)

Sample Image



Related Topics



Leave a reply



Submit