Related
Some ways to add labels on contour plots
# load packages
library('mgcv')
library('gratia') # draw(); smooth_estimates()
library('metR') # geom_contour2(); geom_text_contour()
library('ggplot2')
Simulate data using the example from Gavin Simpson's website: https://fromthebottomoftheheap.net/2018/10/23/introducing-gratia/
set.seed(1)
dat <- gamSim(2, n = 4000, dist = "normal", scale = 1, verbose = FALSE)
mod <- gam(y ~ s(x, z, k = 30), data = dat$data, method = "REML")
sm <- smooth_estimates(mod); sm
Plot using gratia with the number of contour lines automatically adjusted:
draw(mod) +
geom_text_contour(
aes(z = est), # 'est' from smooth_estimates(mod)
colour = "black", size = 4.5, fontface = "bold",
stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text
skip = 0, # number of contours to skip
rotate = FALSE, # horizontal labeling; if TRUE, rotate text following the contour
label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line
However, contour lines and labeling do no longer match if we use e.g. 'n_contour = 10' within draw().
To allow this matching, use 'n_contour = 0' within draw(), define 'binwidth' within geom_contour2() and 'breaks' within geom_text_contour(), as follows.
Plot using gratia::draw with 'binwidth'-adjusted contour lines:
min(sm$est); max(sm$est) # find min() and max() for adjusting the 'est' z-scale
draw(mod, n_contour = 0) +
geom_contour2(aes(z = est), binwidth = 0.2) +
geom_text_contour(
aes(z = est), # 'est' from smooth_estimates(mod)
breaks = seq(-0.4, 0.4, by = 0.2), # 'breaks' must match with 'binwidth' above
colour = "black", size = 4.5, fontface = "bold",
stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text
skip = 0, # number of contours to skip
rotate = FALSE, # horizontal labelling; if TRUE, rotate text following the contour
label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from contour lines' extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line
Also possible to customize the graph directly with ggplot2:
ggplot(data = sm, aes(x = x, y = z, z = est)) +
geom_contour2(aes(z = est), binwidth = 0.1) +
geom_text_contour(
aes(z = est), # 'est' from smooth_estimates(mod)
breaks = seq(-0.4, 0.4, by = 0.1), # 'breaks' instead of 'bins' to not have too many decimals
colour = "black", size = 4.5, fontface = "bold",
stroke = 0.3, stroke.colour = "white", # 'stroke' controls the width of stroke relative to the size of the text
skip = 0, # number of contours to skip
rotate = FALSE, # horizontal labelling; if TRUE, rotate text following the contour
label.placer = label_placer_fraction(frac = 0.5)) # 'frac = 0.5' places the label at equal distance from contour lines' extremities. Try 'label.placer = label_placer_n(2)' to display two labels per contour line
You can use geom_textcontour from geomtextpath to obtain nicely placed labels without having to tweak lots of different parameters:
library(geomtextpath)
ggplot(sm, aes(x, z, z = est)) + geom_textcontour()
To use it within the gratia::draw framework, you can remove the existing contour from the plot first:
p <- draw(mod)
p$layers[[2]] <- NULL
p + geom_textcontour(aes(z = est), fontface = 'bold')
EDIT
To get a similar effect to the stroke parameter we can do:
library(ggfx)
p + with_outer_glow(geom_textcontour(aes(z = est), fontface = 'bold',
linetype = NA),
colour = 'white', expand = 3, sigma = 1) +
geom_textcontour(aes(z = est), fontface = 'bold', textcolour = NA)
I am studying how long it takes for individuals diagnosed with a psychiatric condition, compared to healthy individuals, to detect fearful, happy and neutral emotional faces.
I am performining analysis with Generalized Linear Mixed Model, and the best-fitting model was the following:
gmod3.5 <- glmer(rt ~ Group * Emotion + trialnum2 +(1+Emotion|ID)+(1|Actor), data=CFS_21, family=Gamma(link="identity"))
The factor group has two levels (SZ, CG), the factor emotion has tree leves (F, H, N) and age is centered.
For the plot, I wrote the following code:
set_theme(base = theme_classic(), #To remove the background color and the grids
axis.title.size = 1.0, #To change axis title size
axis.textsize.x = 1.0, #To change x axis text size
axis.textsize.y = 1.0,
legend.item.backcol = "white",
legend.item.bordercol = "white",
legend.backgroundcol = "white",
legend.inside = TRUE)
p<-plot_model(gmod3.5, type = "int", vars = c("Emotion", "Group", "trialnum2"),
title="Response time per group considering the emotion",
colors = "Set2",
show.values = TRUE,
value.offset = .2,
value.size = 2,
dot.size = 2,
line.size = 1,
width = 1)
p + scale_y_continuous(limits = c(0, 4), breaks = c(0, 1, 2, 3, 4))
However, I need to use CI for the error bars and to make the groups (CG and SZ) closer to each other
Does anyone known how to do this using the plot_model function?
I am quite new to Lattice and I am stuck with some possibly basic coding. I am using shapefiles and geoTIFFS to produce maps of animals distribution and in particular I have:
1 x point shapefile
2 x geoTIFF
1 x polygon shapefile
I am overlapping a levelplot of one of the geoTIFF (UD generated with adehabitatHR) with a contourplot of the same geoTIFF at specific intervals (percentile values), a contourplot of the second geoTIFF (depth raster from ETOPO2) for three specific values (-200, -1000 and -2000), the point shapefile (animal locations) and the polygon shapefile (land). All works fine but I need to change the font size of contour plot labels, their length (i.e. from 0.12315 to 0.123) and positioning for all the contourplots. For the depth contourplot I would like to change the style of each line in something like "continous line", "dashed line" and "point line", and for the contourplot of the UD I would like to change the color of each line using a yellow to red palette.
As far as I understand, I should use panel functions to implement these changes (e.g. Controlling z labels in contourplot) but i am not quite sure how to do it. Part of my code to generate the "plot":
aa <-
quantile(
UD_raster,
probs = c(0.25, 0.75),
type = 8,
names = TRUE
)
my.at <- c(aa[1], aa[2])
depth<-c(-100, -200, -2000)
levelplot(
UD_raster,
xlab = "",
ylab = "",
margin = FALSE,
contour = FALSE,
col.regions = viridis(100),
main = "A",
maxpixels = 2e5
) + layer(sp.polygons(Land, fill = "grey40", col = NA)) + layer(sp.points(locations, pts = 2, col = "red")) + contourplot(
UD_raster,
at = my.at,
labels = TRUE,
margin = FALSE
) + contourplot(
ETOPO2,
at = depth,
labels = TRUE,
margin = FALSE
)
A simplified image, with no UD layer and no point shapefile can be found here and as you can see it is pretty messy. Thanks for your help.
So far for the ETOPO2 countourplot I have solved by eliminating the labels and adding the argument lty to style the line. Because I can't figure out how to use lty with different values for each single line in my contour, I have replicated the contourplot function three times on the same surface, one for each contour I am interested into (this was easy because I only need three contours).
For the position, font and font size of the labels of the remaining contourplot I have used
labels = list(cex = 0.8, "verdana"),
label.style = "flat"
To "shorten" the length of the labels I have used the function round where I specify to which decimal digit to round number.
So now my new code looks like:
aa <-
quantile(
UD_raster,
probs = c(0.25, 0.75),
type = 8,
names = TRUE
)
my.at <- c(aa[1], aa[2])
my.at <- round(my.at, 3)
levelplot(
UD_raster,
xlab = "",
ylab = "",
margin = FALSE,
contour = FALSE,
col.regions = viridis(100),
main = "A",
maxpixels = 2e5
) + layer(sp.polygons(Land, fill = "grey40", col = NA)) + layer(sp.points(positions, pts = 2, col = "red")) + contourplot(
UD_raster,
at = my.at,
labels = list(cex = 0.8, "verdana"),
label.style = "flat",
margin = FALSE
) + contourplot(
ETOPO2,
at = -200,
labels = FALSE,
margin = FALSE,
lty = 1,
pretty = TRUE
) + contourplot(
ETOPO2,
at = -1000,
labels = FALSE,
margin = FALSE,
lty = 2,
pretty = TRUE
) + contourplot(
ETOPO2,
at = -2000,
labels = FALSE,
margin = FALSE,
lty = 3,
pretty = TRUE
)
As one could expect, it takes a bit longer to produce the plot. Still no idea on how to change the colors of the UD contourplot.
I am trying to keep my plots with consistent variable colours while using the factoextra library to plot PCA results. Reproducible example below:
data("decathlon2")
df <- decathlon2[1:23, 1:10]
library("FactoMineR")
res.pca <- PCA(df, graph = FALSE)
get_eig(res.pca)
# Contributions of variables to PC1
fviz_contrib(res.pca, choice = "var", axes = 1, top = 10)
# Contributions of variables to PC2
fviz_contrib(res.pca, choice = "var", axes = 2, top = 10)
I would like the plot for PC1 and PC2 to have a color palette with 10 colours which is identical across plots (i.e. x100m will be red in both). However, in my actual data-set I have 15 explanatory variables which seems to be above the limit for color brewer so there are 2 problems:
How to maintain consistent colour scheme
Be able to utilise 15 colors
Thank you in advance.
(I assume you already know you need to add fill = "name" to the fviz_contrib() call; otherwise the bars will default to fill = "steelblue".)
You can define the palette manually, such that each variable corresponds to the same colour.
To simulate the problem using the example in the question, suppose we only want to show the top 7, when there are 10 variables all together:
# naive way with 7-color palette applied to different variables
fviz_contrib(res.pca, choice = "var", fill = "name", color = "black", axes = 1, top = 7)
fviz_contrib(res.pca, choice = "var", fill = "name", color = "black", axes = 2, top = 7)
We can create a palette using hue_pal() from the scales package, for 10 different colours (one for each column of df).
(You can also use palettes such as rainbow() / heat.colors() / etc. from the base grDevices package. I find their default colour range to be rather intense, though, with a tendency to be overly glaring for a bar chart.)
mypalette <- scales::hue_pal()(ncol(df))
names(mypalette) <- colnames(df)
# optional: see what each color corresponds to
ggplot(data.frame(x = names(mypalette),
y = 1,
fill = mypalette)) +
geom_tile(aes(x = x, y = y, fill = fill), color = "black") +
scale_fill_identity() +
coord_equal()
Use scale_fill_manual() with the self-defined palette on each chart:
fviz_contrib(res.pca, choice = "var", fill = "name", color = "black", axes = 1, top = 7) +
scale_fill_manual(values = mypalette)
fviz_contrib(res.pca, choice = "var", fill = "name", color = "black", axes = 2, top = 7) +
scale_fill_manual(values = mypalette)
I am creating a number of histograms and I want to add annotations towards the top of the graph. I am plotting these using a for loop so I need a way to place the annotations at the top even though my ylims change from graph to graph. If I could store the ylim for each graph within the loop I could cause the y coordinates for my annotation to vary based on the current graph. The y value I include in my annotation must change dynamically as the loop proceeds across iterations. Here is some sample code to demonstrate my issue (Notice how the annotation moves around. I need it to change based on the ylim for each graph):
library(ggplot2)
cuts <- levels(as.factor(diamonds$cut))
pdf(file = "Annotation Example.pdf", width = 11, height = 8,
family = "Helvetica", bg = "white")
for (i in 1:length(cuts)) {
by.cut<-subset(diamonds, diamonds$cut == cuts[[i]])
print(ggplot(by.cut, aes(price)) +
geom_histogram(fill = "steelblue", alpha = .55) +
annotate ("text", label = "My annotation goes at the top", x = 10000 ,hjust = 0, y = 220, color = "darkred"))
}
dev.off()
ggplot uses Inf in its positions to represent the extremes of the plot range, without changing the plot range. So the y value of the annotation can be set to Inf, and the vjust parameter can also be adjusted to get a better alignment.
...
print(ggplot(by.cut, aes(price)) +
geom_histogram(fill = "steelblue", alpha = .55) +
annotate("text", label = "My annotation goes at the top",
x = 10000, hjust = 0, y = Inf, vjust = 2, color = "darkred"))
...
For i<-2, this looks as:
There may be a neater way, but you can get the max count and use that to set y in the annotate call:
for (i in 1:length(cuts)) {
by.cut<-subset(diamonds, diamonds$cut == cuts[[i]])
## get the cut points that ggplot will use. defaults to 30 bins and thus 29 cuts
by.cut$cuts <- cut(by.cut$price, seq(min(by.cut$price), max(by.cut$price), length.out=29))
## get the highest count of prices in a given cut.
y.max <- max(tapply(by.cut$price, by.cut$cuts, length))
print(ggplot(by.cut, aes(price)) +
geom_histogram(fill = "steelblue", alpha = .55) +
## change y = 220 to y = y.max as defined above
annotate ("text", label = "My annotation goes at the top", x = 10000 ,hjust = 0, y = y.max, color = "darkred"))
}