bar plot with named groups on x-axis in ggplot2? - r

it want to make a plot of this type in ggplot, but cant get it to work (made in excel):
So that there are cities on the x-axis, but they are arranged according to which state they are in.
The color of each bar is based on some third property, for example size of city (large, small or medium), and the y-axis is a measurement of whatever! A legend of (large, small, medium) should be included, just isn't in the figure I pasted here.
Example data:
state <- c(rep("Texas",3),rep("Colorado",3),rep("Nevada",3))
city <- c("Houston","Austin","Dallas","Denver","Boulder","Aspen","Reno","Sparks","Henderson")
size <- c(rep(c("large","medium","small"),3))
value <- runif(9, 10,50)
df <- data.frame(state,city,size, value)
So far, I have done this:
plot <- ggplot(df, aes(x=State, y=value)) +
geom_bar(aes(fill=size),position = "dodge", stat = "identity", color="black")
plot
But then each bar is not labeled with the city name.
Any ideas?

Answer
(Credit to https://dmitrijskass.netlify.app/2019/06/30/multi-level-labels-with-ggplot2/ )
Use facet_grid:
ggplot(df, aes(x=city, y = value)) +
geom_col() +
facet_grid(~ state,
scales = "free_x",
space = "free_x",
switch = "x")
More complete version
ggplot(df, aes(x=city, y = value)) +
geom_col() +
facet_grid(~ state,
scales = "free_x",
space = "free_x",
switch = "x") +
theme(panel.spacing = unit(0, units = "cm"), # removes space between panels
strip.placement = "outside", # moves the states down
strip.background = element_rect(fill = "white") # removes the background from the state names

Related

How can I add a a nested y-axis title in my graph?

I created a ggplot graph using ggsegment for certain subcategories and their cost.
df <- data.frame(category = c("A","A","A","A","A","A","B","B","B","B","B","B","B"),
subcat = c("S1","S2","S3","S4","S5","S6","S7","S8","S9","S10","S11","S12","S13"),
value = c(100,200,300,400,500,600,700,800,900,1000,1100,1200,1300))
df2 <- df %>%
arrange(desc(value)) %>%
mutate(subcat=factor(subcat, levels = subcat)) %>%
ggplot(aes(x=subcat, y=value)) +
geom_segment(aes(xend=subcat, yend=0)) +
geom_point(size=4, color="steelblue") +
geom_text(data=df, aes(x=subcat, y=value, label = dollar(value, accuracy = 1)), position = position_nudge(x = -0.3), hjust = "inward") +
theme_classic() +
coord_flip() +
scale_y_continuous(labels = scales::dollar_format()) +
ylab("Cost Value") +
xlab("subcategory")
df2
This code results in a graph that is shown below:
My main issue is I want the category variable on the left of the subcategory variables. It should look like this:
How do I add the category variables in the y-axis, such that it looks nested?
As mentioned in my comment and adapting this post by #AllanCameron to your case one option to achieve your desired result would be the "facet trick", which uses faceting to get the nesting and some styling to remove the facet look:
Facet by category and free the scales and the space so that the distance between categories is the same.
Remove the spacing between panels and place the strip text outside of the axis text.
Additionally, set the expansion of the discrete x scale to .5 to ensure that the distance between categories is the same at the facet boundaries as inside the facets.
library(dplyr)
library(ggplot2)
library(scales)
df1 <- df %>%
arrange(desc(value)) %>%
mutate(subcat=factor(subcat, levels = subcat))
ggplot(df1, aes(x=subcat, y=value)) +
geom_segment(aes(xend=subcat, yend=0)) +
geom_point(size=4, color="steelblue") +
geom_text(data=df, aes(x=subcat, y=value, label = dollar(value, accuracy = 1)), position = position_nudge(x = -0.3), hjust = "inward") +
theme_classic() +
coord_flip() +
scale_y_continuous(labels = scales::dollar_format()) +
scale_x_discrete(expand = c(0, .5)) +
facet_grid(category~., scales = "free_y", switch = "y", space = "free_y") +
ylab("Cost Value") +
xlab("subcategory") +
theme(panel.spacing.y = unit(0, "pt"), strip.placement = "outside")

Increase Plot layout and reduce the legend list

Below is my code to plot Stacked BarPlot
ggplot(data = mdata, aes(x = variable, y = value, fill = Species)) +
geom_bar(position = "fill", stat = "identity") +
theme(legend.text=element_text(size=rel(0.7)),
legend.key.size = unit(0.5, "cm")) +
scale_y_continuous(labels=function(x)x*100) +
coord_flip() +
ylab("Species Percentage") +
xlab("Samples")
OutputPlot:
As you can see from the plot my Species legends are split in to 5 column list, which takes the 50% of the total plot layout.
Is there a way to make/convert legend list in to only 2 or 3 column so that area above and below will be covered and BarPlot can be widened.
Also to make Legend Text Bold its looking blurred with many legends
You can set any number of columns with the ncol argument in guide_legend():
library(ggplot2)
dat <- cbind(car = rownames(mtcars), mtcars)
ggplot(dat, aes(mpg, wt, colour = car)) +
geom_point() +
scale_colour_discrete(guide = guide_legend(ncol = 3))
EDIT: As Z.Lin pointed out, for fill scales; replace scale_colour_* by scale_fill_*.

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

Draw border around certain rows using cowplot and ggplot2

I want to somehow indicate that certain rows in a multipanel figure should be compared together. For example, I want to make this plot:
Look like this plot (with boxes around panels made with PowerPoint):
Here's the code I made to use the first plot. I used ggplot and cowplot:
require(cowplot)
theme_set(theme_cowplot(font_size=12)) # reduce default font size
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) +
geom_point(size=2.5)
plot.diamonds <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() +
theme(axis.text.x = element_text(angle=70, vjust=0.5))
plot.mpg2 <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) +
geom_point(size=2.5)
plot.diamonds2 <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() +
theme(axis.text.x = element_text(angle=70, vjust=0.5))
plot_grid(plot.mpg, plot.diamonds,plot.mpg2, plot.diamonds2, nrow=2,labels = c('A', 'B','C','D'))
Is there a change I can make to this code to get the borders that I want? Or maybe can I even make the panels A and B have a slightly different color than the background for panels C and D? That might be even better.
Since the result of plot_grid() is a ggplot object, one way to do this is to use nested plot grids: one plot_grid() for each row, with the appropriate border added via theme().
plot_grid(
# row 1
plot_grid(plot.mpg, plot.diamonds, nrow = 1, labels = c('A', 'B')) +
theme(plot.background = element_rect(color = "black")),
# row 2
plot_grid(plot.mpg2, plot.diamonds2, nrow = 1, labels = c('C', 'D')) +
theme(plot.background = element_rect(color = "black")),
nrow = 2)

single level variable in ggplot2::geom_tile

I've got a data frame with three variables, location, price, and varname.
I'd like to use ggplot2's geom_tile to make a heat map of sorts. This plot almost looks like a bar chart, but I prefer geom_tile because I like the values, big or small, to be allocated the same amount of physical space on the plot. My code almost gets me there.
The first problem's that I can't format the plot so to get rid of all the white space to the left and right of my pseudo-bar. The second problem's that I can't remove the Price legend below the plot, because I'd like Price only to feature in the legend above the plot.
Thanks for any help!
Starting point (df):
df <- data.frame(location=c("AZ","MO","ID","MI"),price=c(1380.45677,1745.1245,12.45652,1630.65341),varname=c("price","price","price","price"))
Current code:
library(ggplot2)
ggplot(df, aes(varname,location, width=.2)) + geom_tile(aes(fill = price),colour = "white") + geom_text(aes(label = round(price, 3))) +
scale_fill_gradient(low = "ivory1", high = "green") +
theme_classic() + labs(x = "", y = "") + theme(legend.position = "none") + ggtitle("Price")
Don't set the width to 0.2.
Use theme to disable the labels and ticks.
You might want to use coord_equal to get nice proportions (i.e. squares). expand = FALSE gets rid of all white space.
.
ggplot(df, aes(varname, location)) +
geom_tile(aes(fill = price), colour = "white") +
geom_text(aes(label = round(price, 3))) +
scale_fill_gradient(low = "ivory1", high = "green") +
theme_classic() + labs(x = "", y = "") +
theme(legend.position = "none", axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
ggtitle("Price") +
coord_equal(expand = FALSE)

Resources