Overlay bar graphs in ggplot2
Try adding position = "identity"
to your geom_bar
call. You'll note from ?geom_bar
that the default position is stack
which is the behavior you're seeing here.
When I do that, I get:
print(ggplot(melted,aes(x=x,y=value,fill=variable)) +
geom_bar(stat="identity",position = "identity", alpha=.3))
And, as noted below, probably position = "dodge"
would be a nicer alternative:
Overlay two bar plots with geom_bar()
If you don't need a legend, Solution 1 might work for you. It is simpler because it keeps your data in wide format.
If you need a legend, consider Solution 2. It requires your data to be converted from wide format to long format.
Solution 1: Without legend (keeping wide format)
You can refine your aesthetics specification on the level of individual geoms (here, geom_bar
):
ggplot(data=my_data, aes(x=Block)) +
geom_bar(aes(y=Start), stat="identity", position ="identity", alpha=.3, fill='lightblue', color='lightblue4') +
geom_bar(aes(y=End), stat="identity", position="identity", alpha=.8, fill='pink', color='red')
Solution 2: Adding a legend (converting to long format)
To add a legend, first use reshape2::melt
to convert your data frame from wide format into long format.
This gives you two columns,
- the
variable
column ("Start" vs. "End"), - and the
value
column
Now use the variable
column to define your legend:
library(reshape2)
my_data_long <- melt(my_data, id.vars = c("Block"))
ggplot(data=my_data_long, aes(x=Block, y=value, fill=variable, color=variable, alpha=variable)) +
geom_bar(stat="identity", position ="identity") +
scale_colour_manual(values=c("lightblue4", "red")) +
scale_fill_manual(values=c("lightblue", "pink")) +
scale_alpha_manual(values=c(.3, .8))
How do I overlay a bar charts in ggplot2?
At the moment your data is in wide format. It is going to be difficult to plot it without reshaping.
Your current data looks like this:
agrisum
#> agri_work Male_ratio_02 Female_ratio_02 Male_ratio_04 Female_ratio_04
#> 1 0 25.72 25.77 28.78 27.92
#> 2 1 23.13 26.58 20.36 22.95
In order to plot this easily, we should have one observation per row, and one variable per column. At the moment, the gender and year are distributed between columns. We can fix this using dplyr
and tidyr
from the tidyverse:
library(dplyr)
library(tidyr)
agrisum <- agrisum %>%
pivot_longer(2:5) %>%
separate(name, sep = "_", into = c("Gender", NA, "Year")) %>%
mutate(Work = c("Agricultural", "Non-Agricultural")[agri_work + 1])
agrisum
#> # A tibble: 8 x 5
#> agri_work Gender Year value Work
#> <dbl> <chr> <chr> <dbl> <chr>
#> 1 0 Male 02 25.7 Agricultural
#> 2 0 Female 02 25.8 Agricultural
#> 3 0 Male 04 28.8 Agricultural
#> 4 0 Female 04 27.9 Agricultural
#> 5 1 Male 02 23.1 Non-Agricultural
#> 6 1 Female 02 26.6 Non-Agricultural
#> 7 1 Male 04 20.4 Non-Agricultural
#> 8 1 Female 04 23.0 Non-Agricultural
So now we can do:
ggplot(agrisum, aes(Work, value, fill = Gender)) +
geom_col(position = position_dodge(width = 0.8), width = 0.6,
color = "black", size = 0.1) +
scale_fill_manual(values = c(Female = "orange", Male = "deepskyblue3")) +
facet_grid(. ~ paste0("20", Year)) +
theme_bw() +
theme(strip.background = element_blank(),
panel.border = element_blank(),
axis.line.x = element_line())
How to position overlapping bar plot and bar width manually?
How about something like this:
d1 <- c(1.0, 2.0, 3.0, 4.0, 4.8)
d2 <- c(0.15, 0.5, 1.0, 1.5, 3.3)
d3 <- c(0.16,0.7,0.7,1,2.5)
df <- data.frame(d1, d2, d3)
library(dplyr)
library(ggplot2)
df %>%
mutate(index = seq_along(d1)) %>%
ggplot() +
geom_col(
aes(x = index, y = d1),
width = 0.8,
col = "black",
fill = "forestgreen"
) +
geom_col(
aes(x = index, y = d2),
width = 0.4,
col = "black",
fill = "red",
position = ggplot2::position_nudge(x = 0.2)
) +
geom_col(
aes(x = index, y = d3),
width = 0.4,
col = "black",
fill = "blue",
position = ggplot2::position_nudge(x = -0.2)
) +
labs(y = NULL)
Created on 2022-03-30 by the reprex package (v2.0.1)
Update: patterns instead of colors
This is actually not straightforward with ggplot2 itself. If you're OK with downloading packages outside CRAN then you can download ggpattern
and do something like this:
df %>%
mutate(index = seq_along(d1)) %>%
ggplot() +
geom_col(
aes(x = index, y = d1),
width = 0.8,
col = "black",
fill = "forestgreen"
) +
ggpattern::geom_col_pattern(
aes(x = index, y = d2),
width = 0.4,
color = "black",
fill = "white",
pattern = 'stripe',
pattern_size = 0.1,
pattern_fill = "black",
position = ggplot2::position_nudge(x = 0.2)
) +
ggpattern::geom_col_pattern(
aes(x = index, y = d3),
width = 0.4,
color = "black",
fill = "lightgray",
pattern = 'crosshatch',
pattern_fill = "black",
pattern_angle = 0,
pattern_size = 0.1,
position = ggplot2::position_nudge(x = -0.2)
) +
labs(y = NULL)
Created on 2022-03-31 by the reprex package (v2.0.1)
ggpattern
is well documented, so you should be able to adjust the above example to your needs.
If you don't want to rely on packages outside CRAN then you can take a look at any of these old questions for possible workarounds:
- How to add texture to fill colors in ggplot2
- Adding hatches or patterns to ggplot bars
- ggplot2: Add Different Textures to Colored Barplot and Legend
- How can I add hatches, stripes or another pattern or texture to a barplot in ggplot?
Overlaying ggplot bar plots
I have to options for you, but I like the second one better (although it might be a little bit longer).
library(ggplot2)
ggplot(csv_total) +
geom_col(aes(x = habitates, y = obs_flore, fill = "obs_flore"), alpha = 0.5) +
geom_col(aes(x = habitates, y = obs_faune, fill = "obs_faune"), alpha = 0.5) +
theme(axis.text.x = element_text(angle = 50, hjust = 1))
This looks ok, but with the following, we don't have to artifically create a fill legend like above and we can use the option "dodge" to shoe the columns side by side.
First we have to transform the data into the long format (using gather
from tidyr
):
library(tidyr)
csv_total_long <- gather(csv_total, flore_faune, obs, obs_flore, obs_faune)
csv_total_long
# A tibble: 12 x 4
# habitates surf_ha flore_faune obs
# <chr> <dbl> <chr> <dbl>
# 1 A 0.4 obs_flore 0
# 2 B 0.7 obs_flore 0
# 3 C 385. obs_flore 1.1
# 4 D 2941. obs_flore 2.1
# 5 E 45.9 obs_flore 2.3
# 6 F 306. obs_flore 2.5
# 7 A 0.4 obs_faune 2.4
# 8 B 0.7 obs_faune 15.6
# 9 C 385. obs_faune 0
# 10 D 2941. obs_faune 1
# 11 E 45.9 obs_faune 0.3
# 12 F 306. obs_faune 1
Now we have an extra row for each faune and flore observation. Then we can plot the columns next to each other. You would get the same plot as above without the position = "dodge"
.
ggplot(csv_total_long, aes(x = habitates, y = obs, fill = flore_faune)) +
geom_col(alpha = 0.5, position = "dodge") +
scale_fill_brewer(palette = "Dark2") +
theme(axis.text.x = element_text(angle = 50, hjust = 1))
I used geom_col
here as it's the same as geom_bar
with stat = "identity"
.
Data
I used letters for the habitates, as the original once weren't properly recognized by my system and that was not the point here.
csv_total <- structure(list(habitates = c("A", "B", "C", "D", "E", "F"),
surf_ha = c(0.4, 0.7, 384.8, 2940.8, 45.9, 306.4),
obs_flore = c(0.0, 0.0, 1.1, 2.1, 2.3, 2.5),
obs_faune = c(2.4, 15.6, 0.0, 1.0, 0.3, 1.0)),
row.names = c(NA, -6L),
class = c("tbl_df", "tbl", "data.frame"))
overlay/superimpose grouped bar plots in ggplot2
I think the clue is to set the width
of the "after" bars, but to dodge them as if their width are 0.9 (i.e. the same (default) width as the "before" bars). In addition, because we don't map fill
of the "after" bars, we need to use the group
aesthetic instead to achieve the dodging.
I prefer to have only one data set and just subset it in each call to geom_col
.
ggplot(mapping = aes(x = question, y = n, fill = factor(ans))) +
geom_col(data = d[d$t == "before", ], position = "dodge") +
geom_col(data = d[d$t == "after", ], aes(group = ans),
fill = "black", width = 0.5, position = position_dodge(width = 0.9))
Data:
set.seed(2)
d <- data.frame(t = rep(c("before", "after"), each = 6),
question = rep(c("pain", "fear"), each = 3),
ans = 1:3, n = sample(12))
Alternative data preparation using data.table
, starting with your original 'df':
library(data.table)
d <- melt(setDT(df), measure.vars = names(df), value.name = "ans")
d[ , c("t", "question") := tstrsplit(variable, "_")]
Either pre-calculate the counts and proceed as above with geom_col
# d2 <- d[ , .N, by = .(question, ans)]
Or let geom_bar
do the counting:
ggplot(mapping = aes(x = question, fill = factor(ans))) +
geom_bar(data = d[d$t == "before", ], position = "dodge") +
geom_bar(data = d[d$t == "after", ], aes(group = ans),
fill = "black", width = 0.5, position = position_dodge(width = 0.9))
Data:
df <- data.frame(before_fear = c(1,1,1,2,3), before_pain = c(2,2,1,3,1),
after_fear = c(1,3,3,2,3),after_pain = c(1,1,2,3,1))
Overlaying a Bar chart with multiple bars with a line graph with ggplot2 in R
The main problem here is the fill
in the ggplot
call. Because you have it in ggplot(aes())
, it is propagating to both geom_bar
and geom_line
. If you just set fill = variable
inside ggplot(aes()), you wouldn't get the error, but you would have an Total_1 in the legend, which is not what I think you want.
You also don't need to use the df.1.1$
and df.1.2$
inside aes()
. You want to put your data in the data argument and then the variables in aes()
without calling the data frame again.
Here is a solution.
ggplot(data = df.1.1, aes(x = Month, y = value)) +
geom_bar(aes(fill = variable), position = position_dodge(),stat = 'identity') +
geom_line(data = df.1.2, aes(x=Month, y=value, group=1))
One other note is that you can use geom_col
instead of geom_bar
with stat='identity'
.
ggplot(data = df.1.1, aes(x = Month, y = value)) +
geom_col(aes(fill = variable), position = position_dodge()) +
geom_line(data = df.1.2, aes(x=Month, y=value, group=1))
Reorder Overlaid Bars in Plot so Longer Bars are in back -R
I would recommend you switch to geom_col
if you are going to be plotting bars, but supplying x=
and y=
aesthetics. See here for the documentation explanation. With that being said, it will work either way. Basically:
Tidy Data: I cannot confirm, but it seems that your dataset
df2
is not organized following Tidy Data Principles, which makes things much easier withggplot2
and many other methods of data analysis. Instead of having your y values split amongdf2$Mar-20
anddf2$Apr-20
, you should have a column for the category (let's call itdf2$date
) and a column for the actual valuedf2$y
. You would then have one call togeom_bar
givingaes(x=id, y=y)
. You can do this viadplyr::gather()
ormelt
from thereshape2
package.Arrange Data: Outside of any other influence (such as ordering of the levels of a factor), the plotting function in
ggplot2
will plot data according to the arrangement of the actual dataframe for thex=
andy=
aesthetic. This means if you order the data beforehand on a particular, non-factor value, this will dictate the order in whichggplot2
will plot. So, you should order the plot based on descendingdf2$y
prior to plotting to have the largest bars plotted first and smallest last, which means the smallest will be in front.
Here's a complete example with dummy data:
library(ggplot2)
library(dplyr)
set.seed(1234)
df <- data.frame(
x=rep(LETTERS[1:10],3),
y=sample(1:100,30,replace=TRUE),
id=c(rep('Group1',10),rep('Group2',10),rep('Group3',10))
)
df %>%
arrange(-y) %>%
ggplot(aes(x,y)) + theme_bw() +
geom_bar(aes(fill=id),stat='identity',position=position_identity())
Try that ggplot()
function without the arrange()
function and you'll see the effect is what you're looking to do.
Related Topics
How to Remove Row If It Has a Na Value in One Certain Column
Changing Font Size in R Datatables (Dt)
Calculate Rolling Correlation Using Rollapply
How to Use Earlier Declared Variables Within Aes in Ggplot with Special Operators (..Count.., etc.)
Reading Excel File: How to Find the Start Cell in Messy Spreadsheets
Arithmetic Mean on a Multidimensional Array on R and Matlab: Drastic Difference of Performances
Basic - T-Test -> Grouping Factor Must Have Exactly 2 Levels
Add Annotation and Segments to Groups of Legend Elements
Delete Columns Where All Values Are 0
Identify Points Within Specified Distance in R
R: Robust Se's and Model Diagnostics in Stargazer Table
How to Call External R Script from R Markdown (.Rmd) in Rstudio
How to Get the Text Between Two Words in R
Rollmean with Dplyr and Magrittr
R Shiny Mouseover Text for Table Columns
Email Dataframe as Table in Email Body with Sendmailr
Jitter If Multiple Outliers in Ggplot2 Boxplot
R: How to Display Clustered Matrix Heatmap (Similar Color Patterns Are Grouped)