I am working on a plot of 21 different odds ratios and their respective confidence intervals - the odds ratios are stratified by racial group (7 groups) and death category (3 categories), and I'm pretty close to what I want, I just am stuck on a few things.
Here is what I've run so far:
library(ggplot2)
install.packages("ggstance")
early <- data.frame(labels=c("Early:Overall","Early:Non-Hispanic White","Early:Non-Hispanic Black","Early:Non-Hispanic Asian",
"Early:Non-Hispanic Other","Early:Hispanic","Early:Unknown","Neo:Overall","Neo:NHW","Neo:NHB",
"Neo:NHA","Neo:NHO","Neo:Hisp","Neo:Unknown","Inf:Overall","Inf:NHW","Inf:NHB","Inf:NHA","Inf:NHO",
"Inf:Hisp","Inf:Unknown"),
odds=c(317.77,355.54,187.82,495.49,213.23,345.45,1818.05,114.02,128.84,52.70,271.15,57.86,158.21,579.40,46.76,52.50,22.46,104.81,22.41,67.93,214.85),
low=c(282.25,301.37,141.12,292.51,113.06,263.85,624.20,103.53,112.63,42.20,168.26,34.34,126.00,255.58,42.87,46.46,18.42,67.25,14.29,55.32,108.01),
high=c(357.64,419.32,249.42,831.05,396.78,450.64,6710.68,125.41,147.03,65.47,426.48,93.53,197.31,1354.38,50.93,59.16,27.16,157.91,33.76,82.78,416.06),
group=rep(c("Overall","Non-Hispanic White","Non-Hispanic Black","Non-Hispanic Asian",
"Non-Hispanic Other","Hispanic","Unknown"),3),
death=c("Early:Overall"="Early Neonatal Death","Early:Non-Hispanic White"="Early Neonatal Death",
"Early:Non-Hispanic Black"="Early Neonatal Death","Early:Non-Hispanic Asian"="Early Neonatal Death",
"Early:Non-Hispanic Other"="Early Neonatal Death","Early:Hispanic"="Early Neonatal Death",
"Early:Unknown"="Early Neonatal Death","Neo:Overall"="Neonatal Death","Neo:NHW"="Neonatal Death",
"Neo:NHB"="Neonatal Death","Neo:NHA"="Neonatal Death","Neo:NHO"="Neonatal Death","Neo:Hisp"="Neonatal Death",
"Neo:Unknown"="Neonatal Death","Inf:Overall"="Infant Death","Inf:NHW"="Infant Death",
"Inf:NHB"="Infant Death","Inf:NHA"="Infant Death","Inf:NHO"="Infant Death","Inf:Hisp"="Infant Death","Inf:Unknown"="Infant Death"))
ggplot(early,aes(x = odds, y = group)) +
geom_rect(aes(xmin = 0.001, xmax = 1000,
ymin = -Inf, ymax = Inf)) +
geom_errorbarh(aes(xmin = low, xmax = high)) +
geom_point(aes(colour = group,shape=death), size = 3 ) +
coord_cartesian(xlim = c(20, 600)) +
facet_grid(labels~., switch = "y") +
theme_bw() +
theme(panel.spacing.y = unit(0, "points"),
panel.border = element_blank(),
panel.background= element_blank(),
panel.grid.major.x = element_line(color="white"),
plot.background = element_rect(fill="white"),
axis.text.y = element_blank(),
axis.ticks.length.y = unit(0, "points"),
strip.text.y.left = element_text(angle = 0),
strip.background.y = element_blank(),
axis.line = element_line(),
legend.box.background = element_blank(),
legend.box.margin = margin(6, 6, 6, 6))
plot+ labs(title="Race-Stratified Odds Ratios by Death Category",x="Odds Ratios",y="Maternal Race Group")
And this is the plot I currently have:
I'm not sure why the background of the plot is still gray or why some of the shapes are partially obstructed, but I'm assuming there's some kind of grey bars over the background. I've tried deleting each line of my code one by one and the grey never went away. I'm trying to make the background just white, so if anyone has any suggestions for how to do that I would really appreciate it!
Also, I was hoping to not show the individual labels (i.e. "Early:Non-Hispanic White") on the plot and instead only have the 3 death labels (i.e. "Early Neonatal Death"). Is there a way to do that?
Thank you!
The problem is simply that you are drawing the gray background with your call to geom_rect, which by default is gray. You can either make this white, or better still, remove it and use scales and themes to give your plot the desired look.
To remove the color guide from the legend, you can add + scale_color_discrete(guide = guide_none()) to your plot.
The symbols are being clipped (and don't align perfectly with the labels) because each of the facets is actually preserving a tiny space for all the groups. You therefore need to specify scales = "free_y" to level everything out, give your error bars greater width and prevent the symbols from clipping.
You can also choose a global theme that requires less individual tweaks to the theme parameters, and you may prefer the look of making the strip labels right-aligned and external to the y axis line.
ggplot(early,aes(x = odds, y = group)) +
geom_errorbarh(aes(xmin = low, xmax = high)) +
geom_point(aes(colour = group, shape = death), size = 3) +
scale_color_discrete(guide = guide_none()) +
coord_cartesian(xlim = c(20, 600)) +
facet_grid(labels ~ ., switch = "y", scales = "free_y") +
labs(title = "Race-Stratified Odds Ratios by Death Category",
x = "Odds Ratios",
y = "Maternal Race Group") +
theme_classic() +
theme(panel.spacing.y = unit(0, "points"),
axis.text.y = element_blank(),
axis.ticks.length.y = unit(0, "points"),
strip.placement = "outside",
strip.text.y.left = element_text(angle = 0, hjust = 1),
strip.background.y = element_blank(),
legend.box.margin = margin(6, 6, 6, 6))
Related
This heatmap has a grid builtin, which I am failing to find the way to customize.
I want to preserve horizontal lines in the grid, if possible increase thickness, and disable vertical lines. Each row should look as a continuous time-serie where data is present and blank where it is not.
Either adding vertical/horizontal lines on-top would possibly cover some data, because of that grid lines, or controlled gaps between tiny rectangles, is preferable.
Alternativelly, geom_raster doesn't shows any grid at all. With which I would need to add the horizontal lines of the grid.
I tried changing linetype, the geom_tile argument, which does seem to change the type or allow to fully disable it with linetype=0, fully disabling the grid, but it wouldn't allow to preserve horizontal grid-lines. I didn't saw any changes by modifying the size argument.
This is the code generating the plot as above:
ggplot( DF, aes( x=rows, y=name, fill = value) ) +
#geom_raster( ) +
geom_tile( colour = 'white' ) +
scale_fill_gradient(low="steelblue", high="black",
na.value = "white")+
theme_minimal() +
theme(
legend.position = "none",
plot.margin=margin(grid::unit(0, "cm")),
#line = element_blank(),
#panel.grid = element_blank(),
panel.border = element_blank(),
panel.grid = element_blank(),
panel.spacing = element_blank(),
#panel.grid = element_line(color="black"),
#panel.grid.minor = element_blank(),
plot.caption = element_text(hjust=0, size=8, face = "italic"),
plot.subtitle = element_text(hjust=0, size=8),
plot.title = element_text(hjust=0, size=12, face="bold")) +
labs( x = "", y = "",
#caption= "FUENTE: propia",
fill = "Legend Title",
#subtitle = "Spaces without any data (missing, filtered, etc)",
title = "Time GAPs"
)
I tried to attach DF %>% dput but I get Body is limited to 30000 characters; you entered 203304. If anyone is familiar with a similar Dataset, please advise.
Additionally,
There are 2 gaps at left&right of the plot area, one is seen inbetween the y-axis, and at the right you can see the X-axis outbounding, and are not controlled by a plot.margin argument.
I would want to set the grid to a thicker line when month changes.
The following data set has the same names and essential structure as your own, and will suffice for an example:
set.seed(1)
DF <- data.frame(
name = rep(replicate(35, paste0(sample(0:9, 10, T), collapse = "")), 100),
value = runif(3500),
rows = rep(1:100, each = 35)
)
Let us recreate your plot with your own code, using the geom_raster version:
library(ggplot2)
p <- ggplot( DF, aes( x=rows, y=name, fill = value) ) +
geom_raster( ) +
scale_fill_gradient(low="steelblue", high="black",
na.value = "white") +
theme_minimal() +
theme(
legend.position = "none",
plot.margin=margin(grid::unit(0, "cm")),
panel.border = element_blank(),
panel.grid = element_blank(),
panel.spacing = element_blank(),
plot.caption = element_text(hjust=0, size=8, face = "italic"),
plot.subtitle = element_text(hjust=0, size=8),
plot.title = element_text(hjust=0, size=12, face="bold")) +
labs( x = "", y = "", fill = "Legend Title", title = "Time GAPs")
p
The key here is to realize that discrete axes are "actually" numeric axes "under the hood", with the discrete ticks being placed at integer values, and factor level names being substituted for those integers on the axis. That means we can draw separating white lines using geom_hline, with values at 0.5, 1.5, 2.5, etc:
p + geom_hline(yintercept = 0.5 + 0:35, colour = "white", size = 1.5)
To change the thickness of the lines, simply change the size parameter.
Created on 2022-08-01 by the reprex package (v2.0.1)
I have this code as given below
ggplot(df1, aes(x = -Ord, y = Value, fill = Item)) +
geom_bar(stat = "identity") +
scale_y_continuous(labels = comma) +
facet_wrap(~Area, scales = "free", drop = T, nrow = 2) +
labs(title = "Major Crops of South Asia, Top 10 on avg of 2006-2015",
caption = "Source: fao.org") +
theme(legend.position = "none",
axis.title.y = element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
plot.caption = element_text(face = "italic")) +
geom_text(aes(label=Item), angle = 65,
vjust=.3, hjust = -.1, size=3)
The image it produces is given as below
as you can see the bar labels are getting cropped. The problem with free scales in facet_wrap is that I cannot use limits in scale_y_continuous otherwise some plot areas would be blank due to wide variation in value range for each country.
So, my query is how to manage adding some extra space by increasing y scale for each plot freely without compromising the scales.
I have tried scale_y_continuous(limits = c(0, max(df1$Value)), labels = comma) but then it kills scales = "free" in facet_wrap.
And, also the max values are to be groupwise in order to be really effective. This part I am unable to sort out.
I have deliberately avoided dput data just in case if it is needed kindly let me know. I would supply it.
Thanks.
I am making a sort of population pyramid using ggplot (plotrix doesn't allow me to do fancy labels etc), then I start with a geom_bar with labels and later I flip the coordinates. Sadly, labels almost cannot being seeing. I would like to move those labels near to the "y- axis" in the middle, that now is showing the age groups.
Data is here: d <- data.frame(age.grp2 = c("1-10", "11-20", "21-30", "31-40", "41-50", "1-10", "11-20", "21-30", "31-40", "41-50"),
sex = c("Female","Female","Female","Female","Female","Male","Male","Male","Male","Male" ),
n.enroll = c(288,500,400,300,200,300,460,300,200,300),
proportion = c(17.1,29.6,23.7,17.8,11.8,51,47.9,42.9,40,60),
proportion2 = c(-17.1,-29.6,-23.7,-17.8,-11.8,51,47.9,42.9,40,60)) My code is this one: ggplot(d, aes(x = age.grp2, y = proportion2, fill = sex)) +
geom_bar(position = position_dodge(width=1), stat='identity') +
geom_label(aes(label = paste(n.enroll," (",proportion,"%)", sep=""), group = factor(sex)),
fill="white", colour = "black",
position= position_dodge(width=1),
size = 3) +
scale_fill_manual(values=c("#BFD5E3", "grey")) +
facet_share(~sex, dir = "h", scales = "free", reverse_num = TRUE) +
coord_flip() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
#panel.border = element_blank(),
panel.background = element_blank(),
legend.position = "none",
#axis.line.x = element_line(color = "black"),
axis.ticks.y = element_blank(),
axis.text.x = element_text(colour = "black", size = 8, face = "bold", angle=0, hjust=0.5),
axis.text.y = element_text(colour = "black", size = 8, face = "bold"),
axis.title.x = element_text(size = 14, face="bold", margin = margin(t = 30, r = 20, b = 10, l = 20)),
plot.margin = unit(c(1,1,1,1),"cm")) +
labs(y = "Enrollment percentage within sex",x="") I am attaching also the plot, where we can see in females the label in the age group 11-20 is cut. I would like to have all labels near to the age group labels, within each bar: female labels moved to the right and male labels move it to the left. Also, I would like to have each x-axis extended to 100% or at least in same range, in females goes up to 30% and in males goes up to 60%. Thanks for all the comments
Here's a minimal solution using the base ggplot package, without most of your formatting. The key part is to add a conditional y = ... into the geom_label(aes()) section:
d %>%
mutate(
label = str_c(n.enroll, " (", proportion, "%)"),
label_loc = if_else(sex == "Female", -9.5, 3),
proportion_for_chart = if_else(sex == "Female", -proportion, proportion)
) %>%
ggplot(aes(x = age.grp2, y = proportion_for_chart, fill = sex)) +
geom_col(show.legend = FALSE) +
geom_label(aes(y = label_loc, label = label), size = 3, fill = "white", hjust = 0) +
coord_flip() +
facet_wrap(~ sex, scales = "free") +
theme(
axis.title = element_blank()
)
Whenever possible, I try to reshape data and use geom_col rather than try to get lucky with geom_bar. You should be able to play around with different hard-coded values of y in the geom_label call to fix the proper location for your labels based on your formatting and image size/scale.
Based off the approach outline in the answer here I have a plot which draws it labels off a separate dataframe. However I was wondering if there would be a way to use geom_label_repel instead of geom_text but have a continuous colour scale applied to the label?
So the original plot would be:
df %>%
ggplot(aes(variable, value, width =.7)) +
geom_col(col="black",aes(fill=X1)) + #Black border around the bars
expand_limits(y = 0) +
scale_y_continuous(limits = c(0,70),
breaks=c(0,10,20, 30,40, 50,60,70),
expand = c(0.02, 0.02))+
geom_label_repel(data = df_max,
aes(label = max, y = max))+ #Here the text is added
scale_fill_brewer(palette="Dark2") + #Changes the colour scale
labs(x="Breed",
y="Weighted score",
fill="") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.direction="vertical", legend.position=c(0.9, 0.85),
legend.text=element_text(size=11),
legend.key = element_rect(size = 4),
legend.key.size = unit(1.5, 'lines'))
My guess, if it's possible, its going to include a variation of scale_fill_gradient but I haven't found a way to make it work?
If I include a scale_fill_gradient like this:
df %>%
ggplot(aes(variable, value, width =.7)) +
geom_col(col="black",aes(fill=X1)) + #Black border around the bars
expand_limits(y = 0) +
scale_fill_gradient2(data=df_max, aes(y=max),
fill=max,
low='blue', mid='green', high='red', midpoint = 10)+
scale_y_continuous(limits = c(0,70),
breaks=c(0,10,20, 30,40, 50,60, 70),
expand = c(0.02, 0.02))+
geom_label_repel(data = df_max,
aes(label = max, y = max))+ #Here the text is added
scale_fill_brewer(palette="Dark2") + #Changes the colour scale
labs(x="Breed",
y="Weighted score",
fill="") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.direction="vertical",
legend.position=c(0.9, 0.85),
legend.text=element_text(size=11),
legend.key = element_rect(size = 4),
legend.key.size = unit(1.5, 'lines')) # Ensures spacing between the
# different lines on the legend.
I get the following error:
Error in continuous_scale("fill", "gradient2", div_gradient_pal(low, mid, :
unused arguments (data = list(variable = 1:4, max = c(15, 14.7, 9.7, 4.1)),
fill = .Primitive("max"))
I have been trying to shift my legend title across to be centered over the legend contents using the guide function. I've been trying to use the following code:
guides(colour=guide_legend(title.hjust = 20))
I thought of trying to make a reproducable example, but I think the reason it's not working has something to do with the above line not matching the rest of my code specifically. So here is the rest of the code I'm using in my plot:
NH4.cum <- ggplot(data=NH4_by_Date, aes(x=date, y=avg.NH4, group = CO2, colour=CO2)) +
geom_line(aes(linetype=CO2), size=1) + #line options
geom_point(size=3) + #point symbol sizes
#scale_shape_manual(values = c(1, 16)) + #manually choose symbols
theme_bw()+
theme(axis.text.x=element_text(colour="white"), #change x axis labels to white.
axis.title=element_text(size=12),
axis.title.x = element_text(color="white"), #Change x axis label colour to white
panel.border = element_blank(), #remove box boarder
axis.line.x = element_line(color="black", size = 0.5), #add x axis line
axis.line.y = element_line(color="black", size = 0.5), #add y axis line
legend.key = element_blank(), #remove grey box from around legend
legend.position = c(0.9, 0.6))+ #change legend position
geom_vline(xintercept=c(1.4,7.5), linetype="dotted", color="black")+ #put in dotted lines for season boundaries
scale_color_manual(values = c("#FF6600", "green4", "#0099FF"),
name=expression(CO[2]~concentration~(ppm))) + #manually define line colour
scale_linetype_manual(guide="none", values=c("solid", "solid", "solid")) + #manually define line types
scale_shape_manual(values = c(16, 16, 16)) + #manually choose symbols
guides(colour=guide_legend(title.hjust = 20))+
scale_y_continuous(expand = c(0, 0), limits = c(0,2200), breaks=seq(0,2200,200))+ #change x axis to intercept y axis at 0
xlab("Date")+
ylab(expression(Membrane~available~NH[4]^{" +"}~-N~(~mu~g~resin^{-1}~14~day^{-1})))+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
geom_errorbar(aes(ymin = avg.NH4 - se.NH4, #set y error bars
ymax = avg.NH4 + se.NH4),
width=0.1)
I have tried doing the following instead with no luck:
guides(fill=guide_legend(title.hjust=20)
I have also adjusted the hjust value from values between -2 to 20 just to see if that made a difference but it didn't.
I'll try to attach a picture of the graph so far so you can see what I'm talking about.
I've looked through all the questions I can on stack overflow and to the best of my knowledge this is not a duplicate as it's specific to a coding error of my own somewhere.
Thank-you in advance!!
The obvious approach e.g.
theme(legend.title = element_text(hjust = .5))
didn't work for me. I wonder if it is related to this open issue in ggplot2. In any case, one manual approach would be to remove the legend title, and position a new one manually:
ggplot(mtcars, aes(x = wt, y = mpg, colour = factor(cyl))) +
geom_point() +
stat_smooth(se = FALSE) +
theme_bw() +
theme(legend.position = c(.85, .6),
legend.title = element_blank(),
legend.background = element_rect(fill = alpha("white", 0)),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()) +
annotate("text", x = 5, y = 27, size = 3,
label = "CO[2]~concentration~(ppm)", parse = TRUE)
Output: