ggplot legend not filled - r

I'm having difficulty modifying the legends on my ggplot to plot to my liking.
The correct fill is not showing (the circles are all black atm, so I can't tel which group the points refer to)
The position is incorrect (position = "bottom" is not working)
Ideally, I would like the legends to use squares (the same as the shape used in my plot) rather than circles, but I'm not sure how to achieve this.
My data:
ors_fi = structure(list(col.x = c("Pre-frail", "Frail", "Pre-frail", "Frail",
"Pre-frail", "Frail"), col.estimate = c(0.865018872113144, 1.63924006466768,
0.972589483876898, 1.74300310363782, 0.836325226050668, 1.51301774619739
), col.stderr = c(0.0865502520576455, 0.0991158308454572, 0.0933472713471928,
0.109413945850029, 0.0901760485538073, 0.109136014040399), col.group = c("Wave 1",
"Wave 1", "Wave 2", "Wave 2", "Mean", "Mean")), row.names = c(NA,
-6L), class = c("tbl_df", "tbl", "data.frame"))
My code:
plot_ors_fi = ggplot(data = ors_fi,
aes(x = `col.x`, y = exp(`col.estimate`), group = as.factor(`col.group`))) +
# Plot the point estimates
geom_point(aes(size = 1,
shape = 22,
fill = as.factor(`col.group`)),
stroke = 0.5,
position = position_dodge(width = 0.4)) +
# Plot point estimates text
geom_text(aes(y = exp(`col.estimate`+1.96*`col.stderr`),
label = format(round(exp(`col.estimate`), 2), nsmall = 2)),
vjust = -0.8,
size = 3,
position = position_dodge(width = 0.4)) +
# Set the scale for the size of boxes
scale_radius(guide = "none",
limits = c(0, NA_real_),
range = c(0, 3)) +
# Plot the CIs
geom_linerange(aes(ymin = exp(`col.estimate`-1.96*`col.stderr`),
ymax = exp(`col.estimate`+1.96*`col.stderr`),
colour = "black"),
lwd = 0.5,
position = position_dodge(width = 0.4)) +
# Use identity for aesthetic scales
scale_shape_identity() +
scale_colour_identity() +
# Set the scale for fill colours
scale_fill_grey(start = 0,
end = 1,
limits=c("Wave 1", "Wave 2", "Mean"),
guide="legend",
position = "bottom", ## NOT WORKING?
name=NULL) +
# Set the y-axis scale
scale_y_continuous(trans = "log", breaks = c(1, 2, 4, 8)) +
# Set the x-axis scale
scale_x_discrete(limits = c("Pre-frail", "Frail")) +
# Add titles
xlab("Frailty Index") +
ylab("OR (95% CI)") +
ggtitle("")
This produces:
As you can see the legend at present is not very useful. I couldn't find a similar question in SO where the legend fill colour does not show at all?
I'd appreciate some advice and thanks in advance.

Shape needs to come out of the aes tag:
ggplot(data = ors_fi,
aes(x = `col.x`, y = exp(`col.estimate`), group = as.factor(`col.group`))) +
# Plot the point estimates
geom_point(aes(size = 1,
fill = as.factor(`col.group`)),
shape = 22,
stroke = 0.5,
position = position_dodge(width = 0.4))
# etc.
And legend position can be set by theme:
# Add titles
xlab("Frailty Index") +
ylab("OR (95% CI)") +
ggtitle("") +
theme(legend.position = "bottom")
Which is hopefully what you're looking for?

Related

How to present the results of a dataframe in a serial scale using ggplot as in the example attached?

I have this data frame :
Raw.Score = c(0,1,2,3,4,5,6,7,8)
Severity = c(-3.56553994,-2.70296933,-1.63969850,-0.81321707,-0.04629182,
0.73721320,1.61278518,2.76647043,3.94804472)
x = data.frame(Raw.Score = Raw.Score, Severity = Severity)
Raw.score are raw numbers from 0 to 8 (let's consider them as the labels of the severity numbers)
Severity are relative numbres that represent the locations of the scores in the diagram
I want to graphically present the results as in the following example using ggplot (the example includes different numbers but I want something similar)
As a fun exercise in ggplot-ing here is one approach to achieve or come close to your desired result.
Raw.Score = c(0,1,2,3,4,5,6,7,8)
Severity = c(-3.56553994,-2.70296933,-1.63969850,-0.81321707,-0.04629182,
0.73721320,1.61278518,2.76647043,3.94804472)
dat <- data.frame(Raw.Score, Severity)
library(ggplot2)
dat_tile <- data.frame(
Severity = seq(-4.1, 4.1, .05)
)
dat_axis <- data.frame(
Severity = seq(-4, 4, 2)
)
tile_height = .15
ymax <- .5
ggplot(dat, aes(y = 0, x = Severity, fill = Severity)) +
# Axis line
geom_hline(yintercept = -tile_height / 2) +
# Colorbar
geom_tile(data = dat_tile, aes(color = Severity), height = tile_height) +
# Sgements connecting top and bottom labels
geom_segment(aes(xend = Severity, yend = -ymax, y = ymax), color = "orange") +
# Axis ticks aka dots
geom_point(data = dat_axis,
y = -tile_height / 2, shape = 21, stroke = 1, fill = "white") +
# ... and labels
geom_text(data = dat_axis, aes(label = Severity),
y = -tile_height / 2 - .1, vjust = 1, fontface = "bold") +
# Bottom labels
geom_label(aes(y = -ymax, label = scales::number(Severity, accuracy = .01))) +
# Top labels
geom_point(aes(y = ymax, color = Severity), size = 8) +
geom_text(aes(y = ymax, label = Raw.Score), fontface = "bold") +
# Colorbar annotations
annotate(geom = "text", fontface = "bold", label = "MILD", color = "black", x = -3.75, y = 0) +
annotate(geom = "text", fontface = "bold", label = "SEVERE", color = "white", x = 3.75, y = 0) +
# Fixing the scales
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(limits = c(-ymax, ymax)) +
# Color gradient
scale_fill_gradient(low = "orange", high = "red", guide = "none") +
scale_color_gradient(low = "orange", high = "red", guide = "none") +
# Get rid of all non-data ink
theme_void() +
# Add some plot margin
theme(plot.margin = rep(unit(10, "pt"), 4)) +
coord_cartesian(clip = "off")

How do I change the symbol for just one legend entry using ggplot2?

I am trying to change the orange dot in the legend to be a diamond with a line through it. I have been unable to change only the one symbol; my attempts have either changed all of the symbols to diamonds, or the legend lists the shapes and colors separately.
Here's reproducible data:
data <- structure(list(Period = c(1, 2, 5, 4, 3),
y1 = c(0.0540540540540541, 0.0256410256410256, 0.454545454545455, 0.451612903225806, 0.333333333333333),
y2 = c(0.157894736842105, 0.163265306122449, 0.277027027027027, 0.289473684210526, 0.318181818181818),
y3 = c(0.0917, 0.1872, 0.1155, 0.0949, 0.2272)), row.names = c(NA, -5L),
class = c("tbl_df", "tbl", "data.frame"))
and
CIinfo <- structure(list(Period = c(1, 2, 3, 4, 5),
PointEstimate = c(0.09170907, 0.18715355, 0.22718774, 0.09494454, 0.11549015),
LowerCI = c(0.02999935,0.09032183, 0.1859676, 0.06469029, 0.08147854),
UpperCI = c(0.1534188, 0.2839853, 0.2684079, 0.1251988, 0.1495018)),
row.names = c(NA, 5L), class = "data.frame")
To generate the plot:
library(ggplot2)
library(ggtext) #text box for plot title
scatter <- ggplot(data) +
geom_point(aes(x=Period, y=y1, colour="prevalence"), size=4) + #colour is for legend label
geom_segment(data = CIinfo, aes(x=Period, y=LowerCI, xend=Period, yend=UpperCI, #bars for 95% CI
colour="estimated probability and 95%CI"),
size=2, lineend = "round", alpha=0.7, show.legend = FALSE) + #alpha is transparency
geom_point(aes(x=Period, y=y2, colour="median prevalence"), size=3) +
geom_point(aes(x=Period, y=y3, colour="estimated probability and 95%CI"), size=4, shape=18) +
theme_minimal() +
scale_color_manual(values = c("#d2d2d2","#365C8DFF","#EB6529FF"),
breaks = c("prevalence","median prevalence","estimated probability and 95%CI"), #set order of legend
labels = ~ stringr::str_wrap(.x, width = 28)) + #width of legend
labs(x = "Time Period",
title ="Estimated Probability and Prevalence Rates") +
theme(plot.title = element_textbox(hjust = 0.5, #center title
margin = margin(b = 15)), #pad under the title
plot.title.position = "plot",
axis.title.x = element_text(margin = margin(t = 10, r = 0, b = 0, l = 0)), #pad x axis label
axis.title.y = element_blank(), # remove y-axis label
axis.text = element_text(face="bold"), #bold axis labels
panel.grid.minor.x = element_blank(), # remove vertical minor gridlines
legend.title = element_blank(), # remove legend label
legend.spacing.y = unit(8, "pt") # space legend entries
) +
guides(colour = guide_legend(byrow = TRUE)) + # space legend entries
scale_y_continuous(labels = scales::percent, limits = c(0, .5)) # y-axis as %
scatter
Does something like this help? I'm using a random example, but hopefully it points you in the right direction:
library(tidyverse)
draw_key_custom <- function(data, params, size) {
if (data$colour == "orange") {
data$size <- .5
draw_key_pointrange(data, params, size)
} else {
data$size <- 2
draw_key_point(data, params, size)
}
}
mtcars |>
ggplot(aes(hp, mpg, color = as.factor(cyl)))+
geom_point(key_glyph = "custom")+
guides(color = guide_legend(
override.aes = list(shape = c(16,16,18),
color= c("black", "black", "orange")))
)
P.S. I borrowed some code from this question: R rotate vline in ggplot legend with scale_linetype_manual

How to fill stat_ellipse with patterns with transparent backgrounds in R?

I am using stat_ellipse in R to generate ellipse area polygons from the data. However, they overlap significantly and turning the alpha level to transparent "kind of" works. I wanted to see if there was a way to fill specific ellipses with a pattern that has a transparent background since they overlap so much. Maybe I could have some solid colors and others patterns?
This is my working plot code now:
ggplot(data = claw3,
aes(x = iso1,
y = iso2,
fill = group,
lty = community,
shape = community)) +
stat_ellipse(aes(group = interaction(group, community),
lty = community),
alpha = 0.85, #trasparent level trying to make 2012 West more visible
color = "black",
level = p.ell,
type = "norm",
geom = "polygon",
size = 1.1) +
geom_point(aes(fill = group), size = 2, alpha = 1, color = "black") +
scale_fill_manual(values = c("blue", "grey30","00FFFFFF"),labels = c("2012", "2014","2016"))+
scale_color_manual(values = c( "blue", "grey30","00FFFFFF"))+
scale_linetype_manual(values = c("dotted","solid"))+
scale_shape_manual(values = c(21, 24))+
guides(shape = guide_legend(override.aes = list(fill = "white")), #overrides legend for the community boxes filled white
fill = guide_legend(override.aes = list(shape = NA, size = 1))) + #overrides legend for group removes shapes in year
ylab(expression(paste(delta^{15}, "N (\u2030)"))) +
xlab(expression(paste(delta^{13}, "C (\u2030)"))) +
scale_x_continuous(breaks= seq(-26.5, -19.5, by = 1),
#labels = c( -24, rep("", 2), -23, rep("", 2), -21),
limits = c(-26.5, -19.5),
expand = c(0, 0)) +
scale_y_continuous(breaks= seq(4, 11),
labels = c(4, "",6, "",8, "", 10, ""),
limits = c(3.5, 11),
expand = c(0, 0)) +
theme(text = element_text(size=14)) +
theme_classic(base_size = 14) +
theme(legend.title = element_blank())
I have recently found ggpattern, but it does not look like its friendly with stat_ellipse or I really just don't understand where to put it. I believe Id have to remove the scale_fill_manual and scale_color_manual commands, but thats about it.

ggarrange align y-axes for graphs with same y-axis but annotations of different heights

I have arranged a series of 9 graphs using ggarrange. Although all of the graphs have the same y-axis scale, I have added significance values to each plot individually. Because the graphs have different numbers of comparisons, the annotations go to different heights. How can I scale the ggarrange figure so that the y-axes are aligned?
Below is an example of the syntax for one of the graphs and the ggarrange function:
pwc_example <- example_melt %>%
wilcox_test(value ~ variable, paired = TRUE, p.adjust.method = "holm", detailed = TRUE)
gg_example <- ggplot(example_melt, aes(x = reorder(variable, value), y = value)) +
stat_summary(fun = mean, geom = "bar", width = 0.75, aes(fill = variable)) +
stat_summary(fun.data = mean_cl_boot, geom = "errorbar",
colour="black", position=position_dodge(1), width=.2) +
stat_pvalue_manual(pwc_example, label = "p.adj.signif", tip.length = 0.02, step.increase = 0.05, hide.ns = TRUE, y.position = c(6.3, 6.4, 6.5), label.size = 3) +
scale_x_discrete(labels = c(init_com_rank = "Initial Communication", intervention_rank = "Intervention Needed", no_prob_rank = "No Problem", fin_com_rank = "Close communication", com_interrupted_rank = "Broken Communication", battery_rank = "Low Battery")) +
ggtitle("Sequence 9") +
theme(plot.title = element_text(size=10, hjust = 0.5, face = "bold")) +
scale_y_continuous(breaks = seq(1,6,by = 1)) +
theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.25))
gg_1 + scale_fill_manual(values = c("#9E0142", "#FDAE61","#FDAE61", "#FDAE61", "#FDAE61", "#FDAE61")) + theme(legend.position = "none")
figure <- ggarrange(gg_1, gg_2, gg_3, gg_4, gg_5, gg_6, gg_7, gg_8, gg_9, ncol = 3, nrow = 3, align = "hv")
pdf("figure.pdf", height=14)
ggdraw(figure)
dev.off()
This results in the following figure (bottom row cut off, but you can still see the different range in heights of the y-axis)
I tried manually changing the positions of the annotations using y.position in stat_pvalue_manual(pwc, label = "p.adj.signif", tip.length = 0.02, step.increase = 0.05, hide.ns = TRUE, y.position = c(6.3, 6.4, 6.5),so that each graph had the same maximum y-coordinate (6.5), but this did not work. I cannot change the y-axis scale in scale_y_continuous(breaks = seq(1,6,by = 1)) to anything higher, as this goes beyond the actual scale (the annotations are "extra"), so 6 needs to be the maximum number (I don't know if it is possible to add extra and somehow "hide" the numbers above 6?). I have also played around with the aspect ratios of the graphs and the width and height in ggarrange, but haven't been able to work out a solution yet.
EDIT
I updated the syntax for each graph to ```scale_y_continuous(breaks = seq(1,10,by = 1), labels = c("1", "2", "3", "4", "5", "6", "", "", "", "")) as per the suggestion by #danloo, but the heights are still different, is there a way to force an upper limit?
scale_y_continuous has the option labels to hide some y ticks you need to add due to the significance bar
The function patchwork::wrap_plots enforces that the physical height of the axis is the same in every subplot.

R ggplot2 ggrepel labelling positions

I am trying to add labels to a ggplot object. The labels do not look neat and tidy due to their positioning. I have tried using various geom_label_repel and geom_text_repel options but am not having much luck.
I cannot share the data unfortunately, but I have inserted one of my codes below and a screenshot of one section of the redacted graph. The graph has multiple peaks that need labelling. Each label has 2 lines.
I would like the lines connecting the labels to be directly above each peak on the x axis, then turn at a right angle and the line continue horizontally slightly. I would then like the label to sit on top of this horizontal section of the line.
Some peaks are very close together, so the labels will end up being pushed up the y axis so they are able to stack up neatly.
I hope that description makes sense. I would appreciate it if anyone is able to help.
Thank you!
library(ggplot2)
library(ggrepel)
library(dplyr)
upper_plot <- ggplot() +
geom_point(data = plot_data[which(analysis == "Analysis1"),],
aes(x = rel_pos, y = logged_p, color = as.factor(chr)),
size = 0.25) +
scale_color_manual(values = rep(my_upper_colors, nrow(axis_df))) +
geom_point(data=upper_highlight_pos2_old,
aes(x = rel_pos, y = logged_p),
color= c('grey'),
size=0.75,
pch = 16) +
geom_point(data=upper_labels_old,
aes(x = rel_pos, y = logged_p),
color='dark grey',
size=2,
pch = 18) +
geom_point(data=upper_highlight_pos2_novel,
aes(x = rel_pos, y = logged_p),
color= c('black'),
size=0.75,
pch = 16) +
geom_point(data=upper_labels_novel,
aes(x = rel_pos, y = logged_p),
color='black',
size=2,
pch = 18) +
scale_x_continuous(labels = axis_df$chr,
breaks = axis_df$chr_center,
expand = expansion(mult = 0.01)) +
scale_y_continuous(limits = c(0, maxp),
expand = expansion(mult = c(0.02, 0.06))) +
# geom_hline(yintercept = -log10(1e-5), color = "red", linetype = "dashed",
# size = 0.3) +
geom_hline(yintercept = -log10(5e-8), color = "black", linetype = "dashed",
size = 0.3) +
labs(x = "", y = bquote(atop('GWAS', '-log'[10]*'(p)'))) +
theme_classic() +
theme(legend.position = "none",
axis.title.x = element_blank(),
plot.margin = margin(t=5, b = 5, r=5, l = 10)) +
geom_label_repel(data = upper_labels,
aes(x = rel_pos, y = logged_p, label = label),
ylim = c(maxp / 3, NA),
size = 2,
force_pull = 0,
nudge_x = 0.5,
box.padding = 0.5,
nudge_y = 0.5,
min.segment.length = 0, # draw all lines no matter how short
segment.size = 0.2,
segment.curvature = -0.1,
segment.ncp = 3,
segment.angle = 45,
label.size=NA, #no border/box
fill = NA, #no background
)
This is my current untidy layout...
EDIT:
This is the sort of layout I am after. The lines will need to be flexible and either be right-handed or left-handed depending on space (source: https://www.nature.com/articles/s41588-020-00725-7)

Resources