dodged bar chart with stack for the total - r

I would like to add a stacked bar to a dodged bar chart, showing the total. I don't want the column for the total to be a solid bar, but to consist of the stacked components.
I can add both geom_bars to the plot, but I haven't been able to move the total bar. I could add a dummy category with zero in the middle, but of course I'd prefer the total to be to the right of the components.
df=data.frame(
treatment=rep(c("Impact","Control")),
type=rep(c("Phylum1","Phylum2"),each=2),
total=c(2,3,4,5))
ggplot(df,aes(y=total,x=treatment,fill=type)) +
geom_bar(position= position_dodge(),stat="identity", alpha = 0.9, width = 0.25) +
geom_bar(position = position_stack(), stat = 'identity', alpha = 0.3, width = 0.125)
This is not the same question they want to stack/dodge by two variables. I just want to summarise the same info twice, but differently.
I can of course add a bar for the solid total and put in the stacked bar by hand, but I get so close with basic ggplot that I thought maybe a little hack (e.g. modifying the return object of position_stack) might be possible.

You could reshape your dataset and use facets to get the effect, although this will show all of your data and not just the largest value in each type/treatment combination.
Your dataset would need to be repeated twice, once for the the original type and once for plotting the totals. You also need a new variable, which I called type2.
df$type2 = df$type
df2 = df
df2$type2 = "Total"
Stack the two datasets together via rbind, and then plot using type2 as the x variable and the alpha variable.
ggplot(rbind(df, df2), aes(y = total, x = type2, fill = type, alpha = type2)) +
geom_col(width = .9) +
facet_wrap(~treatment, strip.position = "bottom") +
scale_alpha_manual(values = c(.9, .9, .3), guide = "none") +
theme(strip.background = element_blank(),
axis.text.x = element_blank(),
axis.ticks.length = unit(0, "mm"),
panel.spacing = unit(0, "mm"),
panel.grid.major.x = element_blank()) +
scale_x_discrete(expand = c(1, 0))

Related

Make X-Axis Lables, value labels and other axis and labels bold in Ggplot2

I work inside a research environment and I can't copy paste the code I used there, but I have previously generated this plot, and have been helped by various people in labelling it with the count number. The problem arises when I screenshot the plot from inside the research environment, and the legends are illegible. I am hoping I can address this by making the labels (including the X-axis label) all bold.
I used some mock-data outside the environment and this is what I have so far.
library(ggplot2)
library(reshape2)
md.df = melt(df, id.vars = c('Group.1'))
tmp = c("virginica","setosa","versicolor")
md.df2 = md.df[order(match(md.df$Group.1, tmp)),]
md.df2$Group.1 = factor(as.character(md.df2$Group.1), levels = unique(md.df2$Group.1))
ggplot(md.df2, aes(x = Group.1, y = value, group = variable, fill = variable)) +
geom_bar(stat="identity",color='black', position = "dodge") +
xlab('Species') + ylab('Values') + theme_bw()+
ylim(0,8)+
theme(text = element_text(size=16),
axis.text.x = element_text(angle=0, hjust=.5),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))+
ggtitle("Order variables in barplot")+
geom_text(aes(label=value), vjust=-0.3, size=4, # adding values
position = position_dodge(0.9))+ element_text(face="bold")
I need to make the labels onto bold, and the element_text isn't working mainly because I am probably using it in the wrong way. I'd appreciate any help with this.
An example of this plot which I haven't been able to find mock data to re-create outside the environment, have asked a question about in the past, is the one where the axis ticks also need to be made bold. This is because the plot is illegible from the outside.
I've tried addressing the illegibility by saving all my plots using ggsave in 300 resolution but it is very illegible.
I'd appreciate any help with this, and thank you for taking the time to help with this.
As I mentioned in my comment to make the value labels bold use geom_text(..., fontface = "bold") and to make the axis labels bold use axis.text.x = element_text(angle=0, hjust=.5, face = "bold").
Using a a minimal reproducible example based on the ggplot2::mpg dataset:
library(ggplot2)
library(dplyr)
# Create exmaple data
md.df2 <- mpg |>
count(Group.1 = manufacturer, name = "value") |>
mutate(
variable = value >= max(value),
Group.1 = reorder(Group.1, -value)
)
ggplot(md.df2, aes(x = Group.1, y = value, group = variable, fill = variable)) +
geom_col(color = "black", position = "dodge") +
geom_text(aes(label = value), vjust = -0.3, size = 4, position = position_dodge(0.9), fontface = "bold") +
labs(x = "Species", y = "Values", title = "Order variables in barplot") +
theme_bw() +
theme(
text = element_text(size = 16),
axis.text.x = element_text(angle = 90, vjust = .5, face = "bold"),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
)
In addition to #stefan 's answer, you can also set tick length and thickness like so:
## ... plot code +
theme(## other settings ...,
axis.ticks = element_line(linewidth = 5),
axis.ticks.length = unit(10, 'pt'))
However, the example "phenotype conditions" will probably remain hard to appraise, regardless of optimization on the technical level. On the conceptual level it might help to display aggregates, e. g. condition counts per Frequency and supplement a textual list of conditions (sorted alphabetically and by Frequency, or the other way round) for those readers who indeed want to look up any specific condition.

ggplot2: combine fill and alpha legends

There are many questions out there pertaining to this topic, but none of the answers I have tried have worked for me so far.
I have a plot that is a heatmap with fill and alpha mapped to different values, i.e. different variables in my data create different colors and alpha values. I want to get a finished product here to see if this figure is worthwhile, so let's not discuss whether this is a good idea at the moment.
What I want to do is combine my fill and alpha legend such that I have the four different transparencies of blue, the four different transparencies of red, and for yellow. I can get those legends separately, or just one of them, but not two in one.
My best guess for code thus far has been
dummy <- data.frame(model=c(rep("X",23),rep("Y",23)),
longvarname=rep(c("CBH","NDMI","CovType","CH","CBD","NDVI_NF_750","Slope","TPI_Valley_1200", "TPI_Ridge_1200",
"TPI_Ridge_100","TPI_Valley_100", "TSHarv","Treat","RxBurn",
"TSTreat","TSRx","Deficit","SpecHumid","MaxRH","MinTemp","MaxTemp", "MaxGustDir", "MaxGustSpd"),2),
vargrp=rep(c(rep("Veg",6), rep("Topo",5), rep("Mgmt",5),rep("Clim",7)),2),
value=runif(46, min=0, max=1),
binary_slope=sample(c("negative","positive", "zero"), 46, replace=TRUE))
ggplot(dummy, aes(x=model, y=longvarname)) +
geom_tile(aes(fill=binary_slope, alpha=value))+
scale_alpha_binned(breaks=c(0.4, 0.6, 0.8, 1))+
facet_grid(vargrp~., scales='free_y', space="free_y")+
xlab("Model")+
ylab("Variable")+
scale_fill_manual(values=c("midnightblue","yellow1","red4"))+
# guides(fill=guide_legend(override.aes = list(fill=c(rep("#191970",4),
# rep("#FFEA00",4),
# rep("#8b0000",4)),
# alpha=rep(c(0.4,0.6,0.8,1),3))))+
theme(panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust=1),
strip.text.y = element_blank(),
axis.ticks = element_blank())
The above code produces both legends which you can see in the example I attached. If you uncomment the guides() lines, the error I am getting is Error in [[<-.data.frame(*tmp*, i, value = c("#191970", "#191970", :
replacement has 12 rows, data has 3.
But most of my efforts have just resulted in only the fill legend at alpha=1. Another thought I had which I thought might get me there was in guides(), putting the alpha hex codes in front of each color hex code and then making alpha guide = "none", but no dice.
Thanks very much for your help!
Instead of making use of both fill and alpha one option would be to make use of just fill like so:
Add a column with your desired fill colors to your dataset using e.g. a left_join.
Manually compute your alpha levels using e.g. cut.
Adjust the transparency of th colors according to the alpha values using colorspace::adjust_transparency
Map the resulting colors on the fill aes and make use of scale_fill_identity. Add guide=guide_legend to get a legend.
library(ggplot2)
library(dplyr)
library(colorspace)
cols <- c(negative = "midnightblue", positive = "yellow1", zero = "red4")
cols <- tibble::enframe(cols, name = "binary_slope", value = "fill")
dummy <- left_join(dummy, cols, by = "binary_slope")
dummy <- mutate(dummy,
alpha = cut(value, breaks = c(0, 0.4, 0.6, 0.8, 1), labels = c(0.4, 0.6, 0.8, 1)),
alpha = as.numeric(as.character(alpha)),
fill = colorspace::adjust_transparency(fill, alpha)
)
ggplot(dummy, aes(x = model, y = longvarname)) +
geom_tile(aes(fill = fill)) +
scale_fill_identity(guide = guide_legend()) +
facet_grid(vargrp ~ ., scales = "free_y", space = "free_y") +
xlab("Model") +
ylab("Variable") +
theme(
panel.background = element_blank(),
axis.text.x = element_text(angle = 45, hjust = 1),
strip.text.y = element_blank(),
axis.ticks = element_blank()
)

How to put value labels inside polygons and not on their edges in R

In R, I have hexbins / tilegrams representing jurisdictions of a Canadian province, but the ID labels of hexbins are sitting on the polygons' corners, not inside them.
The closest I got to the desired chart:
I have been messing with geom_polygon, geom_text and geom_label and a few others, but nothing will do. I have seen people use rgeos for doing this with US statebins but I am guessing this is not necessary in my case, as R does recognise that my hexbins have some ID attached to them, it's just placing them not where I want them. I also tried changing the position argument within geoms.
tileGram_f <- ggplot2::fortify(tileGram2)
tileGram_f <- plyr::join(tileGram_f, tileGram2#data[,c("id", "tile_region")],by="id")
names(tileGram_f)[names(tileGram_f)=="tile_region"] <- "Country"
ggplot(tileGram_f) +
geom_polygon(aes(long, lat, group = group),
fill = "yellow", color = "blue", show.legend = FALSE, size = 1) +
coord_equal() +
geom_text(data = tileGram_f,
aes(x = long, y = lat, label = Country),
size = 8, color = "red", check_overlap = TRUE) +
theme(panel.background = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank())
The hexbins' IDs are made visible by geom_text() or geom_label() successfully (ID numbers, 0-21), but they are placed on each of a polygon / hexbin corner. I want them only ONCE per hexbin and inside it, not around it.

Manually change order of y axis items on complicated stacked bar chart in ggplot2

I've been stuck on an issue and can't find a solution. I've tried many suggestions on Stack Overflow and elsewhere about manually ordering a stacked bar chart, since that should be a pretty simple fix, but those suggestions don't work with the huge complicated mess of code I plucked from many places. My only issue is y-axis item ordering.
I'm making a series of stacked bar charts, and ggplot2 changes the ordering of the items on the y-axis depending on which dataframe I am trying to plot. I'm trying to make 39 of these plots and want them to all have the same ordering. I think ggplot2 only wants to plot them in ascending order of their numeric mean or something, but I'd like all of the bar charts to first display the group "Bird Advocates" and then "Cat Advocates." (This is also the order they appear in my data frame, but that ordering is lost at the coord_flip() point in plotting.)
I think that taking the data frame through so many changes is why I can't just add something simple at the end or use the reorder() function. Adding things into aes() also doesn't work, since the stacked bar chart I'm creating seems to depend on those items being exactly a certain way.
Here's one of my data frames where ggplot2 is ordering my y-axis items incorrectly, plotting "Cat Advocates" before "Bird Advocates":
Group,Strongly Opposed,Opposed,Slightly Opposed,Neutral,Slightly Support,Support,Strongly Support
Bird Advocates,0.005473026,0.010946052,0.012509773,0.058639562,0.071149335,0.31118061,0.530101642
Cat Advocates,0.04491726,0.07013396,0.03624901,0.23719464,0.09141056,0.23404255,0.28605201
And here's all the code that takes that and turns it into a plot:
library(ggplot2)
library(reshape2)
library(plotly)
#Importing data from a .csv file
data <- read.csv("data.csv", header=TRUE)
data$s.Strongly.Opposed <- 0-data$Strongly.Opposed-data$Opposed-data$Slightly.Opposed-.5*data$Neutral
data$s.Opposed <- 0-data$Opposed-data$Slightly.Opposed-.5*data$Neutral
data$s.Slightly.Opposed <- 0-data$Slightly.Opposed-.5*data$Neutral
data$s.Neutral <- 0-.5*data$Neutral
data$s.Slightly.Support <- 0+.5*data$Neutral
data$s.Support <- 0+data$Slightly.Support+.5*data$Neutral
data$s.Strongly.Support <- 0+data$Support+data$Slightly.Support+.5*data$Neutral
#to percents
data[,2:15]<-data[,2:15]*100
#melting
mdfr <- melt(data, id=c("Group"))
mdfr<-cbind(mdfr[1:14,],mdfr[15:28,3])
colnames(mdfr)<-c("Group","variable","value","start")
#remove dot in level names
mylevels<-c("Strongly Opposed","Opposed","Slightly Opposed","Neutral","Slightly Support","Support","Strongly Support")
mdfr$variable<-droplevels(mdfr$variable)
levels(mdfr$variable)<-mylevels
pal<-c("#bd7523", "#e9aa61", "#f6d1a7", "#999999", "#c8cbc0", "#65806d", "#334e3b")
ggplot(data=mdfr) +
geom_segment(aes(x = Group, y = start, xend = Group, yend = start+value, colour = variable,
text=paste("Group: ",Group,"<br>Percent: ",value,"%")), size = 5) +
geom_hline(yintercept = 0, color =c("#646464")) +
coord_flip() +
theme(legend.position="top") +
theme(legend.key.width=unit(0.5,"cm")) +
guides(col = guide_legend(ncol = 12)) + #has 7 real columns, using to adjust legend position
scale_color_manual("Response", labels = mylevels, values = pal, guide="legend") +
theme(legend.title = element_blank()) +
theme(axis.title.x = element_blank()) +
theme(axis.title.y = element_blank()) +
theme(axis.ticks = element_blank()) +
theme(axis.text.x = element_blank()) +
theme(legend.key = element_rect(fill = "white")) +
scale_y_continuous(breaks=seq(-100,100,100), limits=c(-100,100)) +
theme(panel.background = element_rect(fill = "#ffffff"),
panel.grid.major = element_line(colour = "#CBCBCB"))
The plot:
I think this works, you may need to play around with the axis limits/breaks:
library(dplyr)
mdfr <- mdfr %>%
mutate(group_n = as.integer(case_when(Group == "Bird Advocates" ~ 2,
Group == "Cat Advocates" ~ 1)))
ggplot(data=mdfr) +
geom_segment(aes(x = group_n, y = start, xend = group_n, yend = start + value, colour = variable,
text=paste("Group: ",Group,"<br>Percent: ",value,"%")), size = 5) +
scale_x_continuous(limits = c(0,3), breaks = c(1, 2), labels = c("Cat", "Bird")) +
geom_hline(yintercept = 0, color =c("#646464")) +
theme(legend.position="top") +
theme(legend.key.width=unit(0.5,"cm")) +
coord_flip() +
guides(col = guide_legend(ncol = 12)) + #has 7 real columns, using to adjust legend position
scale_color_manual("Response", labels = mylevels, values = pal, guide="legend") +
theme(legend.title = element_blank()) +
theme(axis.title.x = element_blank()) +
theme(axis.title.y = element_blank()) +
theme(axis.ticks = element_blank()) +
theme(axis.text.x = element_blank()) +
theme(legend.key = element_rect(fill = "white"))+
scale_y_continuous(breaks=seq(-100,100,100), limits=c(-100,100)) +
theme(panel.background = element_rect(fill = "#ffffff"),
panel.grid.major = element_line(colour = "#CBCBCB"))
produces this plot:
You want to factor the 'Group' variable in the order by which you want the bars to appear.
mdfr$Group <- factor(mdfr$Group, levels = c("Bird Advocates", "Cat Advocates")

Create a dodged barplot with ggplot2

I have the dataset below:
Database<-c("Composite","DB","TC","RH","DGI","DCH","DCH","DCH","LDP")
Unique_Drugs<-c(12672,5130,1425,3090,6100,2019,250,736,1182)
Unique_Targets<-c(3987,2175,842,2308,2413,1441,198,327,702)
db<-data.frame(Database,Unique_Drugs,Unique_Targets)
and I would like to create a dodged bar chart like the picture below:
This plot came from a dataframe like:
The difference is that in the x-axis I want the 7 unique Database names and the fill argument should be the Unique_Drugs and Unique_Targets in order to create 2 colored bars that will display their values. Im not sure how to make it work.
My code is:
p <- ggplot(data = db, aes(Database)) +
geom_bar(position = position_dodge(preserve = "single"), stat="count", aes(fill = colnames(db[2:4])), color = "black")+
coord_flip()+
theme(legend.position="top",
legend.title=element_blank(),
axis.title.x=element_text(size=18, face="bold", color="#000000"), # this changes the x axis title
axis.text.x = element_text(size=14, face="bold", color="#000000"), #This changes the x axis ticks text
axis.title.y=element_text(size=18, face="bold", color="#000000"), # this changes the y axis title
axis.text.y = element_text(size=14, face="bold", color="#000000"))+ #This changes the y axis ticks text
labs(x = "Database") +
labs(y = "Value") +
scale_x_discrete(limits = rev(factor(Database))) +
scale_fill_manual("Databases", values = c("tomato","steelblue3"))
Here's one way to achieve what you want:
library(reshape2)
ggplot(melt(db), aes(x = Database, y = value, fill = variable)) +
geom_col(position = "dodge") + ylab(NULL) + theme_minimal() +
scale_fill_discrete(NULL, labels = c("Drugs", "Targets"))
If you wanted a bar plot only for drugs, there would be no need for melt as you could use y = Unique_Drugs to specify the bar heights (note that since we have heights we use geom_col). In this case, however, we want to specify two kinds of heights. Your words that fill argument should be the Unique_Drugs and Unique_Targets precisely suggest that we need some transformations because ggplot doesn't accept two variables for the same aesthetic. So, using melt we get all the heights as a single variable and get a single variable for fill.

Resources