Plot linear mixed-effects model using function ggemmeans - r

Say, I have a linear mixed-effects model:
## create data
iris$group = c(rep('A', each = 75),
rep('B', each = 75))
iris$id = rep(c(1:10), each = 15)
## assign contrasts
iris$group <- factor(iris$group, levels=c('A', 'B'))
(contrasts(iris$group) <- matrix(dimnames=list(levels(iris$group), c('.sum.group')), c(-0.5, 0.5), nrow=2))
## build the model
LMM = lmer(Sepal.Length ~ 1 +
Sepal.Width +
Petal.Length +
Petal.Width +
group*Petal.Width +
(1|id),
data = iris,
control=lmerControl(optimizer="bobyqa",
optCtrl=list(maxfun=2e5)),
REML = FALSE)
Now, I need to plot the interaction group*Petal.Width from the model using the function ggemmeans. Exactly this function must be used in my case.
The documentation for the function says, it gives a standard ggplot-object as an output. However, I couldn't figure out how to adjust its aesthetics, and it seems that it's not exactly how ggplot would behave. Here is the best that I could achieve:
ggemmeans(LMM, terms = c("Petal.Width", "group")) %>% plot()+
geom_line(aes(linetype=group))+
aes(linetype = group)+
theme(legend.title = element_text(size=30),
legend.position = 'top',
legend.key.size = unit('1.5', 'cm'),
axis.title.y = element_text(size = rel(2), angle = 90),
axis.title.x = element_text(size = rel(2)),
axis.text.x = element_text(size=20),
axis.text.y = element_text(size=20))+
scale_colour_manual(values = c("orangered", "purple"))
Now, how could I at least
Remove the thin dashed line behind the thick dashed one?
Change the color of the confidence intervals to the color of the lines? (now the plot is using standard colors for CIs, whatever color I assign to the lines)
Make sure these CIs reflect 95% CIs and not something else? (e.g., in a similar function plot_model, there's an argument for controlling this)

The fill and color are mapped to a variable called group_col created by ggemmeans, so you can do:
ggemmeans(LMM, terms = c("Petal.Width", "group")) %>%
plot() +
aes(linetype = group_col) +
theme(legend.title = element_text(size=30),
legend.position = 'top',
legend.key.size = unit('1.5', 'cm'),
axis.title.y = element_text(size = rel(2), angle = 90),
axis.title.x = element_text(size = rel(2)),
axis.text.x = element_text(size=20),
axis.text.y = element_text(size=20))+
scale_colour_manual("group", values = c("orangered", "purple")) +
scale_fill_manual("group", values = c("orangered", "purple"),
guide = "legend") +
scale_linetype_manual("group", values = 1:2) +
guides(fill = guide_legend(override.aes =
list(fill = c("orangered", "purple"))))

Related

histogram with densities estimated by a model in ggplot2

I'm trying to make a plot in ggplot2 of the densities estimated by a model fitted in gamlss.
I performed this using R base, as shown below:
library(gamlss)
library(ggplot2)
data(Orange)
mod.g = gamlss(circumference ~ age,
family=GA, data = Orange)
pred.g <- predict(mod.g, type = "r")
shapex = (mean(pred.g)/sd(pred.g))^2
ratex = mean(pred.g)/sd(pred.g)^2
hist(Orange$circumference, freq = FALSE, breaks = seq(0, 240, 20))
curve(dgamma(x,
shapex,
ratex), add = T,col = "blue",lwd=2)
legend("topright", legend = c("Gamma"), lty = 1, col = "blue")
Result:
However, when I tried to perform this in ggplot2 the lines are not being plotted, see:
ggplot(Orange, aes(x = circumference)) +
geom_histogram(color = "black", fill = "#225EA8", binwidth=30) +
geom_line(aes(shapex, ratex)) +
theme(legend.title = element_text(size = 15),
legend.text = element_text(size = 17),
axis.title = element_text(size = 22),
axis.text.x = element_text(color = "black", hjust=1),
axis.text.y = element_text(color = "black", hjust=1),
axis.text = element_text(size = 15),
strip.text.x = element_text(size = 18))
After_stat is necessary, but doesn't do the entire trick. With curve you are actually plotting a function. You are passing two constants to your geom_line - how are you expecting ggplot2 to know that you want to plot a gamma distribution with those two constants as parameter?
For this, you could use stat_function
ggplot(Orange, aes(x = circumference)) +
geom_histogram(aes(y = after_stat(density)), color = "black", fill = "#225EA8", binwidth=30) +
stat_function(fun = function(x) dgamma(x, shapex, ratex))
Created on 2023-02-15 with reprex v2.0.2

Estimated averages of a glmm by the emmeans package

I am trying to plot the estimated means using a negative binomial random effects model with their respective confidence intervals, in which the data and the fit were defined as follows:
d.AD <- data.frame(treatment = gl(12,12),
treatment2 = gl(4,1,36),
counts = rpois(144, 4.03),
treatment3 = gl(3,4,36),
ID = gl(12,12))
d.AD$treatment2 = as.integer(d.AD$treatment2)
d.AD$treatment3 = as.factor(d.AD$treatment3)
d.AD$ID = as.factor(d.AD$ID)
library(ggplot2)
library(lme4)
library(emmeans)
mode1A <- glmer.nb(counts~treatment3+poly(treatment2, 2) + (1|ID),
data=d.AD, verbose=F)
The function to make the averages with the confidence intervals used was the emmeans of the emmeans package. However, see that the shape of the graph is inverted, that is, I would like the x axis to be my y axis and vice versa.
emm1 = emmeans(mode1A,~"treatment3", type="response")
emm1df = as.data.frame(emm1)
plot(emm1) +
geom_point(size=2.2)+
labs(x = 'Response', y = 'Treatment') +
scale_shape_manual(NULL, values = 18) +
theme(legend.title = element_text(size = 25,color = "black"),
legend.text = element_text(size = 25,color = "black"),
axis.title = element_text(size = 25,color = "black"),
axis.text.x = element_text(color = "black", hjust=1),
axis.text.y = element_text(color = "black", hjust=1),
axis.text = element_text(size = 25,color = "black"),
plot.title = element_blank(),
strip.text.x = element_text(size = 15,color = "black"))
I would like something close to this example.
You could do something like:
emm1df |>
ggplot(aes(x = treatment3,
y = response,
label = round(response,2)
)
) +
geom_point(size = 2) +
geom_errorbar(aes(ymin = asymp.LCL,
ymax = asymp.UCL),
width = 0
) +
geom_label(nudge_y = (emm1df$asymp.UCL-emm1df$response)*1.2) +
ylim(c(3,5)) +
labs(x = "Treatment",
y = "Response")
Try
plot(emm1, horizontal = FALSE)
See `? plot.emmGrid

Why a second legend is not being included in my ggplot?

The plot below is to create a ggplot in which I show the relationship between two variables according to the raw data (points) and according to two different models (m1 and m3), from which I extract the coefficients and I drow two continuous lines. The grey line is just to show a relationship of 1:1.
line_colors <- RColorBrewer::brewer.pal(7, "Reds")[c(2,4,6)]
line_colors
Plot_a <- ggplot(Todo.6min, aes(x=VeDBA.V13AP, y=VeDBA.X16, colour=ID)) +
geom_point(size=1.5,alpha=0.2) +
geom_abline(aes(slope=1,intercept=0),linetype="dashed",color="grey52",size=1.5) +
theme_bw() +
theme(strip.background=element_blank(),
axis.title.x =element_blank(),
axis.title.y =element_blank(),
axis.text.x = element_text(angle = 0, hjust = 0.5,size = 15),
axis.text.y = element_text(angle = 0, hjust = 0.5,size = 15),
axis.line = element_line(),
panel.grid.major= element_blank(),
panel.grid.minor = element_blank(),
legend.position = "right",
legend.text=element_text(size=18),
legend.title = element_text(size=19, face = "bold"),
legend.key=element_blank(),
panel.border = element_blank(),
strip.placement = "outside") +
guides(color=guide_legend(override.aes=list(fill=NA)))
Plot_a
Plot_b <- Plot_a +
geom_abline(aes(linetype = "m1"),slope = fixef(mod2b)[2] , intercept = fixef(mod2b)[1], color = line_colors[1], size=1.5) +
geom_abline(aes(linetype = "m3"),slope = fixef(mod2d)[2] + fixef(mod2d)[3], intercept = fixef(mod2d)[1], color = line_colors[2], size=1.5) +
scale_linetype_manual(values = c(m1 = "solid", m3 = "solid"),labels = c(m1 = "m1", m3 = "m3")) +
labs(color = "ID", linetype = expression(Model)) +
guides(linetype = guide_legend(override.aes = list(color = line_colors)))
Plot_b
My doubt is why I do not get a legend for the continuous lines although I used scale_linetype_manual for indicating that I wanted a legend in which appears "m1" and "m3" as legend.text.
Does anyone know where is the mistake?
My suggestion would be to manually populate the mapping of geom_abline(), and then cut the data so that it matches the number of lines you're drawing. Illustrated below with an inbuilt dataset.
library(ggplot2)
ggplot(iris, aes(Sepal.Width, Sepal.Length)) +
geom_abline(aes(linetype = c("m1", "m3"),
slope = c(0.5, 0.75),
intercept = c(0, 0.5)),
data = iris[1:2,])
Created on 2020-11-06 by the reprex package (v0.3.0)
For your use case you'd have to substute slope = c(0.5, 0.75) by slope = c(fixef(mod2b)[2], fixef(mod2d)[2]), etc.

stat_fit_glance and generalized additive models (GAM) error

I am trying to add the p-value and R2 from mgcv::gam results to ggplot with facets. The sample dataframe and code are below. Is there a way to successfully paste the p-value and R2 on the ggplots?
DF <- data.frame(Site = rep(LETTERS[20:24], each = 4),
Region = rep(LETTERS[14:18], each = 4),
time = rep(LETTERS[1:10], each = 10),
group = rep(LETTERS[1:4], each = 10),
value1 = runif(n = 1000, min = 10, max = 15),
value2 = runif(n = 1000, min = 100, max = 150))
DF$time <- as.numeric(DF$time)
GAMFORMULA <- y ~ s(x,bs="cr",k=3)
plot1 <- ggplot(data=DF,
aes(x=time, y=value2)) +
geom_point(col="gray", alpha=0.8,
name="") +
geom_line(col="gray", alpha=0.8,
name="",aes(group=group)) +
geom_smooth(se=T, col="darkorange", alpha=0.8,
name="", fill="orange",
method="gam",formula=GAMFORMULA) +
theme_bw() +
theme(strip.text.x = element_text(size=10),
strip.text.y = element_text(size=10, face="bold", angle=0),
strip.background = element_rect(colour="black", fill="gray90"),
axis.text.x = element_text(size=10), # remove x-axis text
axis.text.y = element_text(size=10), # remove y-axis text
axis.ticks = element_blank(), # remove axis ticks
axis.title.x = element_text(size=18), # remove x-axis labels
axis.title.y = element_text(size=25), # remove y-axis labels
panel.background = element_blank(),
panel.grid.major = element_blank(), #remove major-grid labels
panel.grid.minor = element_blank(), #remove minor-grid labels
plot.background = element_blank()) +
labs(y="Value", x="Time", title = "") +
stat_fit_glance(method = "gam",
method.args = list(formula = GAMFORMULA),
aes(label = sprintf('R^2~"="~%.3f~~italic(p)~"="~%.2f',
stat(..r.squared..),stat(..p.value..))),
parse = TRUE)
plot1 + facet_wrap(Site~group, scales="free_y", ncol=3)
Error in sprintf("R^2~\"=\"~%.3f~~italic(p)~\"=\"~%.2f", r.squared, p.value) :
object 'r.squared' not found
My answer explains why stat_fit_glance() cannot be used to add r.sq to a plot, but I am afraid is does not provide an alternative approach.
stat_fit_glance() is a wrapper on broom:glance() that fits the model and passes the model fit object to broom:glance(). In the case of gam(), broom:glance() does not return an estimate for R2 and consequently also stat_fit_glance() is unable to return it.
To see what computed values are available one can use geom_debug() from package 'gginnards'.
library(ggpmisc)
library(gginnards)
library(mgcv)
DF <- data.frame(Site = rep(LETTERS[20:24], each = 4),
Region = rep(LETTERS[14:18], each = 4),
time = rep(LETTERS[1:10], each = 10),
group = rep(LETTERS[1:4], each = 10),
value1 = runif(n = 1000, min = 10, max = 15),
value2 = runif(n = 1000, min = 100, max = 150))
DF$time <- as.numeric(DF$time)
GAMFORMULA <- y ~ s(x,bs="cr",k=3)
plot1 <- ggplot(data=DF,
aes(x=time, y=value2)) +
geom_point(col="gray", alpha=0.8,
name="") +
geom_line(col="gray", alpha=0.8,
name="",aes(group=group)) +
geom_smooth(se=T, col="darkorange", alpha=0.8,
name="", fill="orange",
method="gam",formula=GAMFORMULA) +
theme_bw() +
theme(strip.text.x = element_text(size=10),
strip.text.y = element_text(size=10, face="bold", angle=0),
strip.background = element_rect(colour="black", fill="gray90"),
axis.text.x = element_text(size=10), # remove x-axis text
axis.text.y = element_text(size=10), # remove y-axis text
axis.ticks = element_blank(), # remove axis ticks
axis.title.x = element_text(size=18), # remove x-axis labels
axis.title.y = element_text(size=25), # remove y-axis labels
panel.background = element_blank(),
panel.grid.major = element_blank(), #remove major-grid labels
panel.grid.minor = element_blank(), #remove minor-grid labels
plot.background = element_blank()) +
labs(y="Value", x="Time", title = "") +
stat_fit_glance(method = "gam",
method.args = list(formula = GAMFORMULA),
# aes(label = sprintf('R^2~"="~%.3f~~italic(p)~"="~%.2f',
# stat(..r.squared..),stat(..p.value..))),
# parse = TRUE)
geom = "debug")
plot1 + facet_wrap(Site~group, scales="free_y", ncol=3)
Shown above are the values returned by stat_fit_glance() for the first two panels in the plot.
Note: There does not seem to be agreement on whether R-square is meaningful for GAM. However the summary() method for gam does return an adjusted R-square estimate as member r.sq.

Adding a manual right-hand-side y-axis in ggplot2

I am wondering if there is any way to get a manual right-side y-axis label when there is no scale, only facet headings.
Here's an example
library(dplyr)
library(Hmisc)
# Plot power vs. n for various odds ratios (base prob.=.1)
(n <- seq(10, 1000, by=10)) # candidate sample sizes
(OR <- as.numeric(sort(c(seq(1/0.90,1/0.13,length.out = 9),2.9)))) # candidate odds ratios, spanning the 95% CI centered around an odds ratio of 2.9
alpha <- c(.001, .01, .05)
# put all of these into a dataset and calculate power
powerDF <- data.frame(expand.grid(OR, n, alpha)) %>%
rename(OR = Var1, num = Var2, alph = Var3) %>%
arrange(OR) %>%
mutate(power = as.numeric(bpower(p1=.29, odds.ratio=OR, n=num, alpha = alph))) %>%
transform(OR = factor(format(round(OR,2),nsmall=2)))
# now plot
pPower <- ggplot(powerDF, aes(x = num, y = power, colour = factor(OR))) +
geom_line() +
facet_grid(factor(alph)~.) +
labs(x = "sample size") +
scale_colour_discrete(name = "Odds Ratio") +
scale_x_continuous(breaks = seq(0,1000,100)) +
scale_y_continuous(breaks = seq(0,1,.1)) +
theme_light() +
theme(axis.title.x = element_text(size = 12, face = "bold"),
axis.title.y = element_text(size = 12, face = "bold"),
axis.text = element_text(size = 11),
panel.grid.minor = element_blank(),
panel.grid.major.y = element_line(colour = "gray95"),
panel.grid.major.x = element_line(colour = "gray95"),
strip.text = element_text(colour = 'black', face = 'bold', size = 12),
legend.text = element_text(size = 12),
legend.title = element_text(size = 12, face = "bold"))
(Please forgive the cluttered axes labels, I had to reduce the size of the image to allow it to be uploaded).
I was wondering if there was any way to have an axis label saying 'significance level' down the right hand side of the graph?
Adding the following to scale_y_continuous seems one way to go (although a bunch of warnings)
sec.axis = sec_axis(trans=I, breaks=NULL, name="Significance")
Alternatively, you can add an additional strip that spans all the panels:
library(grid)
library(gtable)
g <- ggplotGrob(pPower)
rect <- grobTree(rectGrob(gp = gpar(fill = "grey70", col="grey70")),
textGrob("Significance", rot=-90, gp = gpar(col="black")))
g <- gtable_add_cols(g, g$widths[6], 6)
g <- gtable_add_grob(g, rect, l=7, t=7, b=11)
grid.newpage() ; grid.draw(g)

Resources