Plot Line and Bar Graph (With Secondary Axis for Line Graph) Using Ggplot

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

Plot line and bar graph (with secondary axis for line graph) using ggplot

ggplot is a "high-level" plotting library, meaning that it is built to express clear relationships in data, rather than a simple system for drawing shapes. One of its fundamental assumptions is that secondary or dual data axes are usually a bad idea; such figures plot more than one relationship into the same space, with no guarantee that the two axes actually share a meaningful relationship (see for example spurious correlations).

All that said, ggplot does indeed have the ability to define secondary axes, although using it for the purpose you describe is intentionally difficult. One way to accomplish your goal would be to split your data set into two separate ones, then plot these in the same ggplot object. It's certainly possible, but take note of how much extra code is required to produce the effect you're after.

library(tidyverse)
library(scales)

df.base <- df[c('MONTHS', 'BASE')] %>%
mutate(MONTHS = factor(MONTHS, MONTHS, ordered = T))

df.percent <- df[c('MONTHS', 'INTERNETPERCENTAGE', 'SMARTPHONEPERCENTAGE')] %>%
gather(variable, value, -MONTHS)

g <- ggplot(data = df.base, aes(x = MONTHS, y = BASE)) +
geom_col(aes(fill = 'BASE')) +
geom_line(data = df.percent, aes(x = MONTHS, y = value / 40 * 12500000 + 33500000, color = variable, group = variable)) +
geom_point(data = df.percent, aes(x = MONTHS, y = value / 40 * 12500000 + 33500000, color = variable)) +
geom_label(data = df.percent, aes(x = MONTHS, y = value / 40 * 12500000 + 33500000, fill = variable, label = sprintf('%i%%', value)), color = 'white', vjust = 1.6, size = 4) +
scale_y_continuous(sec.axis = sec_axis(~(. - 33500000) / 12500000 * 40, name = 'PERCENT'), labels = comma) +
scale_fill_manual(values = c('lightblue', 'red', 'darkgreen')) +
scale_color_manual(values = c('red', 'darkgreen')) +
coord_cartesian(ylim = c(33500000, 45500000)) +
labs(fill = NULL, color = NULL) +
theme_minimal()
print(g)

Sample Image

How to plot a combined bar and line plot in ggplot2

Is this what you had in mind?

ratio <- max(feat$Count)/max(feat$CI2)
ggplot(feat) +
geom_bar(aes(x=Feat, y=Count),stat="identity", fill = "steelblue") +
geom_line(aes(x=Feat, y=OR*ratio),stat="identity", group = 1) +
geom_point(aes(x=Feat, y=OR*ratio)) +
geom_errorbar(aes(x=Feat, ymin=CI1*ratio, ymax=CI2*ratio), width=.1, colour="orange",
position = position_dodge(0.05)) +
scale_y_continuous("Count", sec.axis = sec_axis(~ . / ratio, name = "Odds Ratio"))

Sample Image

Edit: Just for fun with the legend too.

ggplot(feat) +
geom_bar(aes(x=Feat, y=Count, fill = "Count"),stat="identity") + scale_fill_manual(values="steelblue") +
geom_line(aes(x=Feat, y=OR*ratio, color = "Odds Ratio"),stat="identity", group = 1) + scale_color_manual(values="orange") +
geom_point(aes(x=Feat, y=OR*ratio)) +
geom_errorbar(aes(x=Feat, ymin=CI1*ratio, ymax=CI2*ratio), width=.1, colour="orange",
position = position_dodge(0.05)) +
scale_y_continuous("Count", sec.axis = sec_axis(~ . / ratio, name = "Odds Ratio")) +
theme(legend.key=element_blank(), legend.title=element_blank(), legend.box="horizontal",legend.position = "bottom")

Sample Image

Since you asked about adding p values for comparisons in the comments, here is a way you can do that. Unfortunately, because you don't really want to add **all* the comparisons, there's a little bit of hard coding to do.

library(ggplot2)
library(ggsignif)
ggplot(feat,aes(x=Feat, y=Count)) +
geom_bar(aes(fill = "Count"),stat="identity") + scale_fill_manual(values="steelblue") +
geom_line(aes(x=Feat, y=OR*ratio, color = "Odds Ratio"),stat="identity", group = 1) + scale_color_manual(values="orange") +
geom_point(aes(x=Feat, y=OR*ratio)) +
geom_errorbar(aes(x=Feat, ymin=CI1*ratio, ymax=CI2*ratio), width=.1, colour="orange",
position = position_dodge(0.05)) +
scale_y_continuous("Count", sec.axis = sec_axis(~ . / ratio, name = "Odds Ratio")) +
theme(legend.key=element_blank(), legend.title=element_blank(), legend.box="horizontal",legend.position = "bottom") +
geom_signif(comparisons = list(c("A","H"),c("B","F"),c("D","E")),
y_position = c(150,60,40),
annotation = c("***","***","n.s."))

Sample Image

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"))

How to plot multi bar char with multi line char on secondary y-axis?

You can use the following code

library(tidyverse)

test.data<-data.frame(TYPE=c("A","B","C","D"), D1=c(20,10,1,1.1),
D2=c(40,20,3,2), D3=c(9,30,8,3), D4=c(12,20,3,6), D5=c(30,10,6,3) )

df1 <- test.data %>%
pivot_longer(cols = -TYPE) %>%
subset(TYPE %in% c("A","B"))

df2 <- test.data %>%
pivot_longer(cols = -TYPE) %>%
subset(TYPE %in% c("C","D"))

ggplot() +
geom_col(data = df1, aes(x = name, y = value, fill = TYPE), position = position_dodge()) +
scale_fill_manual("Type", values = c("A" = "#56B4E9", "B" = "#E69F00"))+
geom_point(data = df2, aes(x = name, y = value*5, group = TYPE, col = TYPE)) +
geom_line(data = df2, aes(x = name, y = value*5, group = TYPE, col = TYPE)) +
scale_color_manual("Type", values = c("C" = "darkgrey", "D" = "black"))+
scale_y_continuous(name = "First Axis",
sec.axis = sec_axis(trans = ~.*1/5, name="Second Axis"))+
theme_bw()

Sample Image

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

ggplot2 (Barplot + LinePlot) - Dual Y axis

ggplot2 supports dual axis (for good or for worse), where the second axis is a linear transformation of the main axis.

We can work it out for this case:

library(ggplot2)
ggplot(df, aes(x = TimesReshop)) +
geom_col(aes( y = OccuranceCT, fill="redfill")) +
geom_text(aes(y = OccuranceCT, label = OccuranceCT), fontface = "bold", vjust = 1.4, color = "black", size = 4) +
geom_line(aes(y = AverageRepair_HrsPerCar * 1500, group = 1, color = 'blackline')) +
geom_text(aes(y = AverageRepair_HrsPerCar * 1500, label = round(AverageRepair_HrsPerCar, 2)), vjust = 1.4, color = "black", size = 3) +
scale_y_continuous(sec.axis = sec_axis(trans = ~ . / 1500)) +
scale_fill_manual('', labels = 'Occurance', values = "#C00000") +
scale_color_manual('', labels = 'Time Reshop', values = 'black') +
theme_minimal()

Sample Image



Related Topics



Leave a reply



Submit