R highlight a point on a line - r

Here is my code that produces a plot. You can run it:
library(ggplot2)
library(grid)
time <- c(87,87.5, 88,87,87.5,88)
value <- c(10.25,10.12,9.9,8,7,6)
variable <-c("a","a","a","b","b","b")
PointSize <-c(5,5,5,5,5,5)
ShapeType <-c(10,10,10,10,10,10)
stacked <- data.frame(time, value, variable, PointSize, ShapeType)
stacked$PointSize <- ifelse(stacked$time==88, 8, 5)
stacked$ShapeType <- ifelse(stacked$time==88, 16,10)
MyPlot <- ggplot(stacked, aes(x=time, y=value, colour=variable, group=variable)) + geom_line() + xlab("Strike") + geom_point(aes(shape = ShapeType, size = PointSize)) + theme(axis.text.x = element_text(angle = 90, hjust = 1), axis.text = element_text(size = 10), axis.title=element_text(size=14), plot.title = element_text(size = rel(2)) , legend.position = "bottom", legend.text = element_text(size = 10), legend.key.size = unit(1, "cm") ) + scale_shape_identity(guide="none")+scale_size_identity(guide="none")
MyPlot
The plot that is produced highlight the point on the line where the time = 88.
I want to also highlight the point on the the line where the time = 87.925
Is this possible? The thing is that I do not have corresponding value for that time. IS there a way to just find put the point on the lines where time = 87.925 or does some interpolation need to take place so I can get a a value for that time?
Thank you!

You can use ggplot_build to pull out an interpolated value for each line . . .
## create a fake ggplot to smooth your values using a linear fit ##
tmp.plot <- ggplot(stacked, aes(x = time, y = value, colour = variable)) + stat_smooth(method="lm")
## use ggplot_build to pull out the smoothing values ##
tmp.dat <- ggplot_build(tmp.plot)$data[[1]]
## find the x values closest to 87.925 for each variable ##
tmp.ids <- which(abs(tmp.dat$x - 87.925)<0.001)
## store the x and y values for each variable ##
new.points <- tmp.dat[tmp.ids,2:3]
## create a data frame with the new points ##
newpts <- data.frame(new.points,c("a","b"),c(8,8),c(16,16))
names(newpts) <- c("time","value","variable","PointSize","ShapeType")
## add the new points to your original data frame ##
stacked <- rbind(stacked,newpts)
## plot ##
MyPlot

Instead of using a point for highlighting the 87.925 value for time, you can also use a vertical line:
ggplot(stacked, aes(x=time, y=value, colour=variable, group=variable)) +
geom_line() +
geom_point(aes(shape = ShapeType, size = PointSize)) +
geom_vline(aes(xintercept=87.925)) +
xlab("Strike") +
theme(axis.text.x = element_text(angle = 90, hjust = 1), axis.text = element_text(size = 10),
axis.title=element_text(size=14), plot.title = element_text(size = rel(2)), legend.position = "bottom",
legend.text = element_text(size = 10), legend.key.size = unit(1, "cm")) +
scale_shape_identity(guide="none") +
scale_size_identity(guide="none")
the result:
Update: you can add short lines with geom_segment. Replace geom_vline with
geom_segment(aes(x = 87.925, y = 6, xend = 87.925, yend = 6.3), color="black") +
geom_segment(aes(x = 87.925, y = 9.8, xend = 87.925, yend = 10.05), color="black") +
which results in:

Related

How to remove zig-zag pattern in marginal distribution plot of integer values in R?

I am including marginal distribution plots on a scatterplot of a continuous and integer variable. However, in the integer variable maringal distribution plot (y-axis) there is this zig-zag pattern that shows up because the y-values are all integers. Is there any way to increase the "width" (not sure that's the right term) of the bins/values the function calculates the distribution density over?
The goal is to get rid of that zig-zag pattern that develops because the y-values are integers.
library(GlmSimulatoR)
library(ggplot2)
library(patchwork)
### Create right-skewed dataset that has one continous variable and one integer variable
set.seed(123)
df1 <- data.frame(matrix(ncol = 2, nrow = 1000))
x <- c("int","cont")
colnames(df1) <- x
df1$int <- round(rgamma(1000, shape = 1, scale = 1),0)
df1$cont <- round(rgamma(1000, shape = 1, scale = 1),1)
p1 <- ggplot(data = df1, aes(x = cont, y = int)) +
geom_point(shape = 21, size = 2, color = "black", fill = "black", stroke = 1, alpha = 0.4) +
xlab("Continuous Value") +
ylab("Integer Value") +
theme_bw() +
theme(panel.grid = element_blank(),
text = element_text(size = 16),
axis.text.x = element_text(size = 16, color = "black"),
axis.text.y = element_text(size = 16, color = "black"))
dens1 <- ggplot(df1, aes(x = cont)) +
geom_density(alpha = 0.4) +
theme_void() +
theme(legend.position = "none")
dens2 <- ggplot(df1, aes(x = int)) +
geom_density(alpha = 0.4) +
theme_void() +
theme(legend.position = "none") +
coord_flip()
dens1 + plot_spacer() + p1 + dens2 +
plot_layout(ncol = 2, nrow = 2, widths = c(6,1), heights = c(1,6))
From ?geom_density:
adjust: A multiplicate [sic] bandwidth adjustment. This makes it possible
to adjust the bandwidth while still using the a bandwidth
estimator. For example, ‘adjust = 1/2’ means use half of the
default bandwidth.
So as a start try e.g. geom_density(..., adjust = 2) (bandwidth twice as wide as default) and go from there.

Is there a method to set the theta-axis ticks for circular ring plot in ggplot2?

I want to show a tick mark for theta axis in the ggplot2 poar plot. However, both axis.ticks.y and axis.ticks.y in the theme() does not work for theta axis. Any help would be appreciated, thanks
library(ggplot2)
df <- data.frame(
start = c(0, 121, 241),
end = c(120, 240, 359),
group = letters[1:3]
)
# a example circular ring plot
base <- ggplot(df, aes(ymax = end, ymin = start,
xmin = 0.8, xmax = 1,
fill = group)) +
geom_rect() +
coord_polar(theta = "y") +
xlim(c(0, 1))
base
# the tick of y axis can be changed
base + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank())
# set the tick of x axis not worked for the theta axis
base + theme(axis.ticks.x = element_line(color = "black", size = 2))
Thanks for #Vishal A., the answer from Controlling ticks and odd text in a pie chart generated from a factor variable in ggplot2 used the panel.grid.major.y. However, it will add the major grids rather than ticks like the following:
base + theme(panel.grid.major.y = element_line(colour = "black"))
Created on 2021-12-20 by the reprex package (v2.0.1)
I see two options. You can use the panel grids, but you need to hide them. The usefulness of this solution depends on your intended plot background. I've used white, but this can be customised, of course.
Second option is to fake the ticks with annotation, e.g., with the symbol "|".
Further smaller comments in the code below.
library(tidyverse)
df <- data.frame(
start = c(0, 121, 241),
end = c(120, 240, 359),
group = letters[1:3]
)
ggplot(df) +
## annotate with a rectangle, effectively covering your central hole
annotate(geom = "rect", xmin = 0, xmax = 1, ymin = min(df$start), ymax = max(df$end),
fill = "white") +
## move aes to the geom_layer
geom_rect(aes(ymax = end, ymin = start,
xmin = 0.8, xmax = 1,
fill = group)) +
coord_polar(theta = "y") +
xlim(c(0, 1)) +
theme(panel.grid.major.y = element_line(colour = "black"))
## Option 2 - fake the ticks
## the position along the theta axis is defined by y
## you need to change the angle of your fake ticks according to the angle.
df_annot <-
data.frame(y = seq(0,300,100), x = Inf, angle = 360-seq(0,300,100))
ggplot(df) +
## annotate with text, along your y
## by placing it beneath your geom_rect layer it will automatically be covered
geom_text(data = df_annot, aes(x, y, label = "|", angle = angle)) +
## move aes to the geom_layer
geom_rect(aes(ymax = end, ymin = start,
xmin = 0.8, xmax = 1,
fill = group)) +
coord_polar(theta = "y") +
xlim(c(0, 1))
Created on 2021-12-21 by the reprex package (v2.0.1)
You need to use panel.grid.minor.y instead of axis.ticks.y in order to change the ticks.
Your code will look like this:
base + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank())
base + theme(panel.grid.minor.y = element_line(color = "black", size = 1))
The output will look like this:

How can I make a discontinuous axis in R with ggplot2?

I have a dataframe (dat) with two columns 1) Month and 2) Value. I would like to highlight that the x-axis is not continuous in my boxplot by interrupting the x-axis with two angled lines on the x-axis that are empty between the angled lines.
Example Data and Boxplot
library(ggplot2)
set.seed(321)
dat <- data.frame(matrix(ncol = 2, nrow = 18))
x <- c("Month", "Value")
colnames(dat) <- x
dat$Month <- rep(c(1,2,3,10,11,12),3)
dat$Value <- rnorm(18,20,2)
ggplot(data = dat, aes(x = factor(Month), y = Value)) +
geom_boxplot() +
labs(x = "Month") +
theme_bw() +
theme(panel.grid = element_blank(),
text = element_text(size = 16),
axis.text.x = element_text(size = 14, color = "black"),
axis.text.y = element_text(size = 14, color = "black"))
The ideal figure would look something like below. How can I make this discontinuous axis in ggplot?
You could make use of the extended axis guides in the ggh4x package. Alas, you won't easily be able to create the "separators" without a hack similar to the one suggested by user Zhiqiang Wang
guide_axis_truncated accepts vectors to define lower and upper trunks. This also works for units, by the way, then you have to pass the vector inside the unit function (e.g., trunc_lower = unit(c(0,.45), "npc") !
library(ggplot2)
library(ggh4x)
set.seed(321)
dat <- data.frame(matrix(ncol = 2, nrow = 18))
x <- c("Month", "Value")
colnames(dat) <- x
dat$Month <- rep(c(1,2,3,10,11,12),3)
dat$Value <- rnorm(18,20,2)
# this is to make it slightly more programmatic
x1end <- 3.45
x2start <- 3.55
p <-
ggplot(data = dat, aes(x = factor(Month), y = Value)) +
geom_boxplot() +
labs(x = "Month") +
theme_classic() +
theme(axis.line = element_line(colour = "black"))
p +
guides(x = guide_axis_truncated(
trunc_lower = c(-Inf, x2start),
trunc_upper = c(x1end, Inf)
))
Created on 2021-11-01 by the reprex package (v2.0.1)
The below is taking user Zhiqiang Wang's hack a step further. You will see I am using simple trigonometry to calculate the segment coordinates. in order to make the angle actually look as it is defined in the function, you would need to set coord_equal.
# a simple function to help make the segments
add_separators <- function(x, y = 0, angle = 45, length = .1){
add_y <- length * sin(angle * pi/180)
add_x <- length * cos(angle * pi/180)
## making the list for your segments
myseg <- list(x = x - add_x, xend = x + add_x,
y = rep(y - add_y, length(x)), yend = rep(y + add_y, length(x)))
## this function returns an annotate layer with your segment coordinates
annotate("segment",
x = myseg$x, xend = myseg$xend,
y = myseg$y, yend = myseg$yend)
}
# you will need to set limits for correct positioning of your separators
# I chose 0.05 because this is the expand factor by default
y_sep <- min(dat$Value) -0.05*(min(dat$Value))
p +
guides(x = guide_axis_truncated(
trunc_lower = c(-Inf, x2start),
trunc_upper = c(x1end, Inf)
)) +
add_separators(x = c(x1end, x2start), y = y_sep, angle = 70) +
# you need to set expand to 0
scale_y_continuous(expand = c(0,0)) +
## to make the angle look like specified, you would need to use coord_equal()
coord_cartesian(clip = "off", ylim = c(y_sep, NA))
I think it is possible to get what you want. It may take some work.
Here is your graph:
library(ggplot2)
set.seed(321)
dat <- data.frame(matrix(ncol = 2, nrow = 18))
x <- c("Month", "Value")
colnames(dat) <- x
dat$Month <- rep(c(1,2,3,10,11,12),3)
dat$Value <- rnorm(18,20,2)
p <- ggplot(data = dat, aes(x = factor(Month), y = Value)) +
geom_boxplot() +
labs(x = "Month") +
theme_bw() +
theme(panel.grid = element_blank(),
text = element_text(size = 16),
axis.text.x = element_text(size = 14, color = "black"),
axis.text.y = element_text(size = 14, color = "black"))
Here is my effort:
p + annotate("segment", x = c(3.3, 3.5), xend = c(3.6, 3.8), y = c(14, 14), yend = c(15, 15))+
coord_cartesian(clip = "off", ylim = c(15, 25))
Get something like this:
If you want to go further, it may take several tries to get it right:
p + annotate("segment", x = c(3.3, 3.5), xend = c(3.6, 3.8), y = c(14, 14), yend = c(15, 15))+
annotate("segment", x = c(0, 3.65), xend = c(3.45, 7), y = c(14.55, 14.55), yend = c(14.55, 14.55)) +
coord_cartesian(clip = "off", ylim = c(15, 25)) +
theme_classic()+
theme(axis.line.x = element_blank())
Just replace axis with two new lines. This is a rough idea, it may take some time to make it perfect.
You could use facet_wrap. If you assign the first 3 months to one group, and the other months to another, then you can produce two plots that are side by side and use a single y axis.
It's not exactly what you want, but it will show the data effectively, and highlights the fact that the x axis is not continuous.
dat$group[dat$Month %in% c("1", "2", "3")] <- 1
dat$group[dat$Month %in% c("10", "11", "12")] <- 2
ggplot(data = dat, aes(x = factor(Month), y = Value)) +
geom_boxplot() +
labs(x = "Month") +
theme_bw() +
theme(panel.grid = element_blank(),
text = element_text(size = 16),
axis.text.x = element_text(size = 14, color = "black"),
axis.text.y = element_text(size = 14, color = "black")) +
facet_wrap(~group, scales = "free_x")
* Differences in the plot are likely due to using different versions of R where the set.seed gives different result

Combine legend for fill and colour ggplot to give only single legend

I am plotting a smooth to my data using geom_smooth and using geom_ribbon to plot shaded confidence intervals for this smooth. No matter what I try I cannot get a single legend that represents both the smooth and the ribbon correctly, i.e I am wanting a single legend that has the correct colours and labels for both the smooth and the ribbon. I have tried using + guides(fill = FALSE), guides(colour = FALSE), I also read that giving both colour and fill the same label inside labs() should produce a single unified legend.
Any help would be much appreciated.
Note that I have also tried to reset the legend labels and colours using scale_colour_manual()
The below code produces the below figure. Note that there are two curves here that are essentially overlapping. The relabelling and setting couours has worked for the geom_smooth legend but not the geom_ribbon legend and I still have two legends showing which is not what I want.
ggplot(pred.dat, aes(x = age.x, y = fit, colour = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci, fill = tagged), alpha = 0.2, colour = NA) +
theme_classic() +
labs(x = "Age (days since hatch)", y = "Body mass (g)", colour = "", fill = "") +
scale_colour_manual(labels = c("Untagged", "Tagged"), values = c("#3399FF", "#FF0033")) +
theme(axis.title.x = element_text(face = "bold", size = 14),
axis.title.y = element_text(face = "bold", size = 14),
axis.text.x = element_text(size = 12),
axis.text.y = element_text(size = 12),
legend.text = element_text(size = 12))
The problem is that you provide new labels for the color-aesthetic but not for the fill-aesthetic. Consequently ggplot shows two legends because the labels are different.
You can either also provide the same labels for the fill-aesthetic (code option #1 below) or you can set the labels for the levels of your grouping variable ("tagged") before calling ggplot (code option #2).
library(ggplot2)
#make some data
x = seq(0,2*pi, by = 0.01)
pred.dat <- data.frame(x = c(x,x),
y = c(sin(x), cos(x)) + rnorm(length(x) * 2, 0, 1),
tag = rep(0:1, each = length(x)))
pred.dat$lci <- c(sin(x), cos(x)) - 0.4
pred.dat$uci <- c(sin(x), cos(x)) + 0.4
#option 1: set labels within ggplot call
pred.dat$tagged <- as.factor(pred.dat$tag)
ggplot(pred.dat, aes(x = x, y = y, color = tagged, fill = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci), alpha = 0.2, color = NA) +
scale_color_manual(labels = c("untagged", "tagged"), values = c("#F8766D", "#00BFC4")) +
scale_fill_manual(labels = c("untagged", "tagged"), values = c("#F8766D", "#00BFC4")) +
theme_classic() + theme(legend.title = element_blank())
#option 2: set labels before ggplot call
pred.dat$tagged <- factor(pred.dat$tag, levels = 0:1, labels = c("untagged", "tagged"))
ggplot(pred.dat, aes(x = x, y = y, color = tagged, fill = tagged)) +
geom_smooth(size = 1.2) +
geom_ribbon(aes(ymin = lci, ymax = uci), alpha = 0.2, color = NA) +
theme_classic() + theme(legend.title = element_blank())

R Windrose percent label on figure

I am using the windrose function posted here: Wind rose with ggplot (R)?
I need to have the percents on the figure showing on the individual lines (rather than on the left side), but so far I have not been able to figure out how. (see figure below for depiction of goal)
Here is the code that makes the figure:
p.windrose <- ggplot(data = data,
aes(x = dir.binned,y = (..count..)/sum(..count..),
fill = spd.binned)) +
geom_bar()+
scale_y_continuous(breaks = ybreaks.prct,labels=percent)+
ylab("")+
scale_x_discrete(drop = FALSE,
labels = waiver()) +
xlab("")+
coord_polar(start = -((dirres/2)/360) * 2*pi) +
scale_fill_manual(name = "Wind Speed (m/s)",
values = spd.colors,
drop = FALSE)+
theme_bw(base_size = 12, base_family = "Helvetica")
I marked up the figure I have so far with what I am trying to do! It'd be neat if the labels either auto-picked the location with the least wind in that direction, or if it had a tag for the placement so that it could be changed.
I tried using geom_text, but I get an error saying that "aesthetics must be valid data columns".
Thanks for your help!
One of the things you could do is to make an extra data.frame that you use for the labels. Since the data isn't available from your question, I'll illustrate with mock data below:
library(ggplot2)
# Mock data
df <- data.frame(
x = 1:360,
y = runif(360, 0, 0.20)
)
labels <- data.frame(
x = 90,
y = scales::extended_breaks()(range(df$y))
)
ggplot(data = df,
aes(x = as.factor(x), y = y)) +
geom_point() +
geom_text(data = labels,
aes(label = scales::percent(y, 1))) +
scale_x_discrete(breaks = seq(0, 1, length.out = 9) * 360) +
coord_polar() +
theme(axis.ticks.y = element_blank(), # Disables default y-axis
axis.text.y = element_blank())
#teunbrand answer got me very close! I wanted to add the code I used to get everything just right in case anyone in the future has a similar problem.
# Create the labels:
x_location <- pi # x location of the labels
# Get the percentage
T_data <- data %>%
dplyr::group_by(dir.binned) %>%
dplyr::summarise(count= n()) %>%
dplyr::mutate(y = count/sum(count))
labels <- data.frame(x = x_location,
y = scales::extended_breaks()(range(T_data$y)))
# Create figure
p.windrose <- ggplot() +
geom_bar(data = data,
aes(x = dir.binned, y = (..count..)/sum(..count..),
fill = spd.binned))+
geom_text(data = labels,
aes(x=x, y=y, label = scales::percent(y, 1))) +
scale_y_continuous(breaks = waiver(),labels=NULL)+
scale_x_discrete(drop = FALSE,
labels = waiver()) +
ylab("")+xlab("")+
coord_polar(start = -((dirres/2)/360) * 2*pi) +
scale_fill_manual(name = "Wind Speed (m/s)",
values = spd.colors,
drop = FALSE)+
theme_bw(base_size = 12, base_family = "Helvetica") +
theme(axis.ticks.y = element_blank(), # Disables default y-axis
axis.text.y = element_blank())

Resources