control hierarchy of position_dodge - r

Is there a way to control which element is plotted in front of the other if one uses dodged bar charts.
ggplot(mtcars, aes(x=factor(cyl), fill=factor(vs))) +
geom_bar(position= position_dodge (width = - 0.5))
In this example the blue bars are plotted in front of the red ones. Is it possiple to reverse the order without hacking alpha values?

Your control here is limited. Using factor levels we can control i) the fill color ordering and ii) the ordering of position_dodge using group.
Here are the four options:
p1 <- ggplot(mtcars, aes(x = factor(cyl), fill = factor(vs, 0:1), group = factor(vs, 0:1))) +
geom_bar(position = position_dodge(width = - 0.5))
p2 <- ggplot(mtcars, aes(x = factor(cyl), fill = factor(vs, 0:1), group = factor(vs, 1:0))) +
geom_bar(position = position_dodge(width = - 0.5))
p3 <- ggplot(mtcars, aes(x = factor(cyl), fill = factor(vs, 1:0), group = factor(vs, 0:1))) +
geom_bar(position = position_dodge(width = - 0.5))
p4 <- ggplot(mtcars, aes(x = factor(cyl), fill = factor(vs, 1:0), group = factor(vs, 1:0))) +
geom_bar(position = position_dodge(width = - 0.5))
library(cowplot)
plot_grid(p1, p2, p3, p4, align = 'hv')
So it seems only the dodging order is important. In the dev version at least, the right bar is always plotted in front of the left bar.

Related

how to set dual Y axis in geom_bar plot in ggplot2?

I'd like to draw bar plot like this but in dual Y axis
(https://i.stack.imgur.com/ldMx0.jpg)
the first three indexs range from 0 to 1,
so I want the left y-axis (corresponding to NSE, KGE, VE) to range from 0 to 1,
and the right y-axis (corresponding to PBIAS) to range from -15 to 5.
the following is my data and code:
library("ggplot2")
## data
data <- data.frame(
value=c(0.82,0.87,0.65,-3.39,0.75,0.82,0.63,1.14,0.85,0.87,0.67,-7.03),
sd=c(0.003,0.047,0.006,4.8,0.003,0.028,0.006,4.77,0.004,0.057,0.014,4.85),
index=c("NSE","KGE","VE","PBIAS","NSE","KGE","VE","PBIAS","NSE","KGE","VE","PBIAS"),
period=c("all","all","all","all","calibration","calibration","calibration","calibration","validation","validation","validation","validation")
)
## fix index sequence
data$index <- factor(data$index, levels = c('NSE','KGE','VE',"PBIAS"))
data$period <- factor(data$period, levels = c('all','calibration', 'validation'))
## bar plot
ggplot(data, aes(x=index, y=value, fill=period))+
geom_bar(position="dodge", stat="identity")+
geom_errorbar(aes(ymin=value-sd, ymax=value+sd),
position = position_dodge(0.9), width=0.2 ,alpha=0.5, size=1)+
theme_bw()
I try to scale and shift the second y-axis,
but PBIAS bar plot was removed because of out of scale limit as follow:
(https://i.stack.imgur.com/n6Jfm.jpg)
the following is my code with dual y axis:
## bar plot (scale and shift the second y-axis with slope/intercept in 20/-15)
ggplot(data, aes(x=index, y=value, fill=period))+
geom_bar(position="dodge", stat="identity")+
geom_errorbar(aes(ymin=value-sd, ymax=value+sd),
position = position_dodge(0.9), width=0.2 ,alpha=0.5, size=1)+
theme_bw()+
scale_y_continuous(limits = c(0,1), name = "value", sec.axis = sec_axis(~ 20*.- 15, name="value"))
Any advice for move bar_plot or other solution?
Taking a different approach, instead of using a dual axis one option would be to make two separate plots and glue them together using patchwork. IMHO that is much easier than fiddling around with the rescaling the data (that's the step you missed, i.e. if you want to have a secondary axis you also have to rescale the data) and makes it clearer that the indices are measured on a different scale:
library(ggplot2)
library(patchwork)
data$facet <- data$index %in% "PBIAS"
plot_fun <- function(.data) {
ggplot(.data, aes(x = index, y = value, fill = period)) +
geom_bar(position = "dodge", stat = "identity") +
geom_errorbar(aes(ymin = value - sd, ymax = value + sd),
position = position_dodge(0.9), width = 0.2, alpha = 0.5, size = 1
) +
theme_bw()
}
p1 <- subset(data, !facet) |> plot_fun() + scale_y_continuous(limits = c(0, 1))
p2 <- subset(data, facet) |> plot_fun() + scale_y_continuous(limits = c(-15, 15), position = "right")
p1 + p2 +
plot_layout(guides = "collect", width = c(3, 1))
A second but similar option would be to use ggh4x which via ggh4x::facetted_pos_scales allows to set the limits for facet panels individually. One drawback, the panels have the same width. (I failed in making this approach work with facet_grid and space="free")
library(ggplot2)
library(ggh4x)
data$facet <- data$index %in% "PBIAS"
ggplot(data, aes(x = index, y = value, fill = period)) +
geom_bar(position = "dodge", stat = "identity") +
geom_errorbar(aes(ymin = value - sd, ymax = value + sd),
position = position_dodge(0.9), width = 0.2, alpha = 0.5, size = 1
) +
facet_wrap(~facet, scales = "free") +
facetted_pos_scales(
y = list(
facet ~ scale_y_continuous(limits = c(-15, 15), position = "right"),
!facet ~ scale_y_continuous(limits = c(0, 1), position = "left")
)
) +
theme_bw() +
theme(strip.text.x = element_blank())

R ggplot2 with stacked column instead of grouped

I want to plot the data shown below as a grouped bar_plot.
I tried position = "dodge" or position = "dodge2" but it didn't work. Ι also tried position = position_dodge()
It kinda works if i use geom_bar instead of geom_col and remove the y=overlap_percent:
p3 <- ggplot(data = comp_coors, aes(x = species_code, fill = mirna_form)) +
geom_bar(position = "dodge2") + theme_classic()
p3
but i would like the y_axis to have the overlap_percent.
Another attempt which ends in a stacked barplot is:
p2 <- ggplot(data = comp_coors, aes(x = species_code, y = overlap_percent, fill = mirna_form)) +
geom_bar(stat = "identity") + theme_classic()
p2
Finally by using geom_col, it returns this which it doesn't make sense, at least to me:
p4 <- ggplot(data = comp_coors, aes(x = species_code, y = overlap_percent, fill = mirna_form)) +
geom_col(position = "dodge") + theme_classic()
p4
The data that i want to plot :
comp_coors <- data.table( species = c("aae","cel", "dme","hsa", "mdo"),
mirna_form = c("mature", "precursor"),
overlap_percent = c(100.0, 100.0, 88.0, 95.5, 91.7, 100.0, 96.6, 98.4),
overlapping_attribute = c("ID=MIMAT0014285;Alias=MIMAT0014285", "ID=MI0000043;Alias=MI0000043;Name=cel-mir-72", "ID=MIMAT0000401;Alias=MIMAT0000401;Name=dme-miR-", "ID=MI0000791;Alias=MI0000791;Name=hsa-mir-383", "ID=MI0005331;Alias=MI0005331;Name=mdo-let-7g")
)
Try using species as a factor and add stat = "identity" like this:
ggplot(data = comp_coors, aes(x = factor(species), y = overlap_percent, fill = mirna_form)) +
geom_bar(position = "dodge", stat = "identity") + theme_classic() + labs(x = "Species", y = "Overlap percent")
Output:
A grouped barplot with overlap_percent on y-axis right.

ggplot: adding a frequency plot over a percentage plot

I am interested in doing a plot showing percentages by group.
something like this:
data(iris)
ggplot(iris,
aes(x = Sepal.Length, group = factor(Species), fill = factor(Species))) +
geom_histogram(position = "fill")+theme_bw()
however, I would also like to plot a histogram showing the frequency distribution on top of this graph.
something like the plot below.
ggplot(iris,aes(x = Sepal.Length)) +
geom_histogram()+theme_bw()
Does anyone know how to do this?
Note I know how to do a frequency plot by group: ggplot(iris,aes(x = Sepal.Length, group = factor(Species), fill = factor(Species))) + geom_histogram()+theme_bw(). But this is not what I want. Rather I would like a small frequency distribution at the bottom of the percentage plot presented at the beginning.
Thank you very much
Something like this?
library(gridExtra)
p1 <- ggplot(iris,
aes(x = Sepal.Length,
group = factor(Species),
fill = factor(Species))) +
geom_histogram(position = "fill") +
theme_bw() +
theme(legend.position = "top")
p2 <- ggplot(iris,aes(x = Sepal.Length,
group = factor(Species),
fill = factor(Species))) +
geom_histogram() +
theme_bw() +
theme(legend.position = "none")
grid.arrange(p1, p2,
heights = c(4, 1.5))
Edit: So you are looking for this then? Note that in this case the absolute values of the smaller histogram become meaningless since they were scaled down to be ~25% of the vertical chart range.
ggplot() +
geom_histogram(data = iris,
aes(x = Sepal.Length,
group = factor(Species),
fill = factor(Species)),
position = "fill",
alpha = 1) +
geom_histogram(data = iris,
aes(x = Sepal.Length,
y = ..ncount.. / 4),
alpha = 0.5,
fill = 'black')

geom_bar and geom_point in the same ggplot and within the same groups

I have the current code
ggplot(data = niveles[niveles$departamento=="CUNDINAMARCA" &
niveles$prueba=="MATEMÁTICAS" &
!is.na(niveles$nivel),]) +
geom_bar(stat="identity", position = position_dodge(),
aes(x = año, y = desempeño, fill = nivel)) +
geom_point(data = niveles[niveles$prueba=="MATEMÁTICAS" &
niveles$departamento=="COLOMBIA" &
!is.na(niveles$nivel),], shape = 24,
aes(x = año, y = desempeño, group = nivel, fill = "blue"))
which gives me the following plot:
However, I was hoping to get each one of the "points" withing its corresponding category of the "niveles" variable. Does anyone know how I can do that?
You can dodge points the same way as you dodge bars using position=position_dodge(). However, you need to add a width argument specifying how much to "dodge". A value of 1 should correspond with the dodged bars. You also have an unknown "blue" category in the legend. That's because the fill argument should appear outside the aesthetic (aes)
I also think you should subset the data first instead of doing all that within the ggplot command.
An alternative is to facet by department (see option 2 below).
But first to dodge the points.
Option 1: Subsetting
Create a subset for prueba and non-missing for nivel:
MATH <- niveles[niveles$prueba=="MATEMÁTICAS" & !is.na(niveles$nivel),]
Create subsets for each department:
CUNDINAMARCA <- MATH[MATH$departamento=="CUNDINAMARCA",]
COLOMBIA <- MATH[MATH$departamento=="CUNDINAMARCA",]
Then make your graph:
ggplot(data = CUNDINAMARCA) +
geom_bar(stat="identity", position = position_dodge(),
aes(x = año, y = desempeño, fill = nivel)) +
geom_point(data = COLOMBIA, shape = 24,
position = position_dodge(width=1), # You need this to align points with bars
aes(x = año, y = desempeño, group = nivel), fill = "blue")
I can't test it on your data but I used the mtcars dataset as an example.
mtcars <- mtcars %>%
mutate(gear=factor(gear), cyl=factor(cyl))
VS0 <- mtcars[mtcars$vs==0,]
VS1 <- mtcars[mtcars$vs==1,]
ggplot() +
geom_bar(data = VS0, stat="identity", position = position_dodge(),
aes(x = cyl, y = mpg, fill = gear)) +
geom_point(data = VS1, shape = 24,
position = position_dodge(width=1),
aes(x = cyl, y = mpg, group = gear), fill = "blue")
Option 2: Facetting
ggplot(data = mtcars, group=vs) +
geom_bar(stat="identity", position = position_dodge(),
aes(x = cyl, y = mpg, fill = gear)) +
facet_grid(~vs, labeller=label_both)
For your data, maybe this would work:
DATA <- MATH[MATH$departamento %in% c("CUNDINAMARCA","COLOMBIA"),]
ggplot(data = DATA, group=departamento) +
geom_bar(stat="identity", position = position_dodge(),
aes(x = año, y = desempeño, fill = nivel)) +
facet_grid(~departamento, labeller=label_both)

Annotation above bars:

dodged bar plot in ggplot again has me stumped. I asked about annotating text above bars on here a few weeks back (LINK) and got a terrific response to use + stat_bin(geom="text", aes(label=..count.., vjust=-1)). I figured since I already have the counts I'll just supply them with out the .. before and after and I told stat_bin that the position was dodge. It lines them up over the center of the group and adjusts up and down. Probably something minor. Please help me to get the text over the bars.
mtcars2 <- data.frame(type=factor(mtcars$cyl),
group=factor(mtcars$gear))
library(plyr); library(ggplot)
dat <- rbind(ddply(mtcars2,.(type,group), summarise,
count = length(group)),c(8,4,NA))
p2 <- ggplot(dat,aes(x = type,y = count,fill = group)) +
geom_bar(colour = "black",position = "dodge",stat = "identity") +
stat_bin(geom="text", aes(position='dodge', label=count, vjust=-.6))
I was having trouble getting the position dodges to line up, so I ended up creating a position_dodge object (is that the right terminology?), saving it to a variable, and then using that as the position for both geoms. Somewhat infuriatingly, they still seem to be a little off centre.
dodgewidth <- position_dodge(width=0.9)
ggplot(dat,aes(x = type,y = count, fill = group)) +
geom_bar(colour = "black", position = dodgewidth ,stat = "identity") +
stat_bin(geom="text", position= dodgewidth, aes(x=type, label=count), vjust=-1)
Updated geom_bar() needs stat = "identity"
I think this does what you want as well.
mtcars2 <- data.frame(type = factor(mtcars$cyl), group = factor(mtcars$gear))
library(plyr); library(ggplot2)
dat <- rbind(ddply(mtcars2, .(type, group), summarise, count = length(group)), c(8, 4, NA))
p2 <- ggplot(dat, aes(x = type,y = count,fill = group)) +
geom_bar(stat = "identity", colour = "black",position = "dodge", width = 0.8) +
ylim(0, 14) +
geom_text(aes(label = count, x = type, y = count), position = position_dodge(width = 0.8), vjust = -0.6)
p2

Resources