I have 3 trials (trial: e1, e2, e3), 2 products/trial (products: A, B), 5 rates/product (.1,1,10,100,1000), total of 6 curves (curve: c1,...,c6).
After fitting a non linear mixed model, I want to plot all the curves and the curves resulting from the model in the same chart.
This is the reference (package medrc in github): https://doseresponse.github.io/medrc/articles/medrc.html
This is the code to generate the non-linear mixed model.
#packages
library(drc)
library(medrc)
library(dplyr)
library(tidyr)
#my data
trial <- c("e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1",
"e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1","e1",
"e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2",
"e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2","e2",
"e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3",
"e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3","e3")
curve <- c("c1","c1","c1","c1","c1","c1","c1","c1","c1","c1","c1","c1","c1","c1","c1",
"c2","c2","c2","c2","c2","c2","c2","c2","c2","c2","c2","c2","c2","c2","c2",
"c3","c3","c3","c3","c3","c3","c3","c3","c3","c3","c3","c3","c3","c3","c3",
"c4","c4","c4","c4","c4","c4","c4","c4","c4","c4","c4","c4","c4","c4","c4",
"c5","c5","c5","c5","c5","c5","c5","c5","c5","c5","c5","c5","c5","c5","c5",
"c6","c6","c6","c6","c6","c6","c6","c6","c6","c6","c6","c6","c6","c6","c6")
rates <- c(.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000,
.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000,
.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000,
.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000,
.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000,
.1,.1,.1,1,1,1,10,10,10,100,100,100,1000,1000,1000)
product <- c("A","A","A","A","A","A","A","A","A","A","A","A","A","A","A",
"B","B","B","B","B","B","B","B","B","B","B","B","B","B","B",
"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A",
"B","B","B","B","B","B","B","B","B","B","B","B","B","B","B",
"A","A","A","A","A","A","A","A","A","A","A","A","A","A","A",
"B","B","B","B","B","B","B","B","B","B","B","B","B","B","B")
resp <- c(.295,.3232,.3015,.155,.1501,.1483,.0511,.036,.0445,.0021,.0022,.0035,.0015,.0025,.0009,
.312,.3373,.2994,.265,.2501,.2482,.1022,.103,.1142,.0220,.0198,.0159,.0036,.0099,.0100,
.289,.3122,.3093,.141,.1612,.1398,.0722,.022,.0581,.0019,.0015,.0011,.0018,.0009,.0014,
.325,.3451,.2952,.267,.2412,.2398,.1125,.109,.1019,.0554,.0547,.0118,.0029,.0075,.0078,
.294,.2452,.2991,.121,.1925,.1485,.0871,.025,.0658,.0019,.0019,.0010,.0025,.0019,.0008,
.285,.3412,.3069,.124,.1861,.1958,.1276,.132,.1985,.0325,.0201,.0225,.0031,.0089,.0094)
data.test <- data.frame(trial,curve,rates,product,resp) #my data frame
#my model
m1 <- medrm(resp ~ rates,
curveid=b + c + d + e ~ product,
data = data.test,
fct=LL.4(),
random = c + d ~ 1|trial,
start=NULL)
To make the plot:
#plotting
pdata <- data.test%>%
group_by(curve, product) %>%
expand(rates=exp(seq(-3, 10, length=50)))
#pdata$resp_ind <- predict(m1, newdata=pdata)
pdata$resp <- predict(m1, newdata=pdata, level=0)
ggplot(data.test, aes(x=log(rates), y=resp,
colour=product, group=curve, shape=product)) +
geom_point() +
geom_line(data=pdata) +
#geom_line(data=pdata, aes(y=resp_ind), linetype=2) +
theme_bw() +
scale_x_continuous("DOSE",
breaks=log(c(.1, 1, 10, 100, 1000)),
labels=c(.1, 1, 10, 100, 1000))
Note that two rows of code are commented.
When extracting the predict data per curve, I cannot specify the levels, i.e. the curves that gave the random components. What am I missing?
pdata$resp_ind <- predict(m1, newdata=pdata)
is resulting the error:
Error in predict.nlme(object$fit, newdata = newdata, level = level) :
cannot evaluate groups for desired levels on 'newdata'
So I cannot use this row of code to plot each curve line
geom_line(data=pdata, aes(y=resp_ind), linetype=2) +
These are similar questions, but I'm getting the average trend with the code:
pdata$resp <- predict(m1, newdata=pdata, level=0)
I wanted to specify the levels to get all curves.
R: lme, cannot evaluate groups for desired levels on 'newdata'
https://stats.stackexchange.com/questions/58031/prediction-on-mixed-effect-models-what-to-do-with-random-effects
I could identify the problem and I'll share what I found.
To have the plot code to working as it is in the question, the random factor row in the model should have curve instead of trial
#my model
m1 <- medrm(resp ~ rates,
curveid=b + c + d + e ~ product,
data = data.test,
fct=LL.4(),
random = c + d ~ 1|curve,
start=NULL)
#plotting
pdata <- data.test%>%
group_by(curve, product) %>%
expand(rates=exp(seq(-3, 10, length=50)))
pdata$resp_ind <- predict(m1, newdata=pdata)
pdata$resp <- predict(m1, newdata=pdata, level=0)
ggplot(data.test, aes(x=log(rates), y=resp,
colour=product, group=curve, shape=product)) +
geom_point() +
geom_line(data=pdata) +
geom_line(data=pdata, aes(y=resp_ind), linetype=2) +
theme_bw() +
scale_x_continuous("DOSE",
breaks=log(c(.1, 1, 10, 100, 1000)),
labels=c(.1, 1, 10, 100, 1000))
Other models with different random parameters with trial should have the trial in the group_by to plot:
#my model
m2 <- medrm(resp ~ rates,
curveid=b + c + d + e ~ product,
data = data.test,
fct=LL.4(),
random = c + d ~ 1|trial/curve,
start=NULL)
#plotting
pdata <- data.test%>%
group_by(trial, curve, product) %>%
expand(rates=exp(seq(-3, 10, length=50)))
pdata$resp_ind <- predict(m2, newdata=pdata)
pdata$resp <- predict(m2, newdata=pdata, level=0)
ggplot(data.test, aes(x=log(rates), y=resp,
colour=product, group=curve, shape=product)) +
geom_point() +
geom_line(data=pdata) +
geom_line(data=pdata, aes(y=resp_ind), linetype=2) +
theme_bw() +
scale_x_continuous("DOSE",
breaks=log(c(.1, 1, 10, 100, 1000)),
labels=c(.1, 1, 10, 100, 1000))
The correct model to use depends on each case and it is another subject.
I use lme4 in R to fit the mixed model
lmer(value~status+(1|experiment)))
where value is continuous, status(N/D/R) and experiment are factors, and I get
Linear mixed model fit by REML
Formula: value ~ status + (1 | experiment)
AIC BIC logLik deviance REMLdev
29.1 46.98 -9.548 5.911 19.1
Random effects:
Groups Name Variance Std.Dev.
experiment (Intercept) 0.065526 0.25598
Residual 0.053029 0.23028
Number of obs: 264, groups: experiment, 10
Fixed effects:
Estimate Std. Error t value
(Intercept) 2.78004 0.08448 32.91
statusD 0.20493 0.03389 6.05
statusR 0.88690 0.03583 24.76
Correlation of Fixed Effects:
(Intr) statsD
statusD -0.204
statusR -0.193 0.476
I would like to graphically represent the fixed effects evaluation. However the seems to be no plot function for these objects. Is there any way I can graphically depict the fixed effects?
Using coefplot2 (on r-forge):
Stealing the simulation code from #Thierry:
set.seed(101)
dataset <- expand.grid(experiment = factor(seq_len(10)),
status = factor(c("N", "D", "R"), levels = c("N", "D", "R")),
reps = seq_len(10))
X <- model.matrix(~status,dataset)
dataset <- transform(dataset,
value=rnorm(nrow(dataset), sd = 0.23) + ## residual
rnorm(length(levels(experiment)), sd = 0.256)[experiment] + ## block effects
X %*% c(2.78,0.205,0.887)) ## fixed effects
Fit model:
library(lme4)
model <- lmer(value~status+(1|experiment), data = dataset)
Plot:
install.packages("coefplot2",repos="http://r-forge.r-project.org")
library(coefplot2)
coefplot2(model)
edit:
I have frequently been having problems with the R-Forge build. This fallback should work if the R-Forge build is not working:
install.packages("coefplot2",
repos="http://www.math.mcmaster.ca/bolker/R",
type="source")
Note that the coda dependency must already be installed.
I like the coefficient confidence interval plots, but it may be useful to consider some additional plots to understand the fixed effects..
Stealing the simulation code from #Thierry:
library(ggplot2)
library(lme4)
library(multcomp)
dataset <- expand.grid(experiment = factor(seq_len(10)), status = factor(c("N", "D", "R"), levels = c("N", "D", "R")), reps = seq_len(10))
dataset$value <- rnorm(nrow(dataset), sd = 0.23) + with(dataset, rnorm(length(levels(experiment)), sd = 0.256)[experiment] + ifelse(status == "D", 0.205, ifelse(status == "R", 0.887, 0))) + 2.78
model <- lmer(value~status+(1|experiment), data = dataset)
Get a look at the structure of the data...looks balanced..
library(plotrix); sizetree(dataset[,c(1,2)])
It might be interesting to track the correlation between fixed effects, especially if you fit different correlation structures. There's some cool code provided at the following link...
http://hlplab.wordpress.com/2012/03/20/correlation-plot-matrices-using-the-ellipse-library/
my.plotcorr(
matrix(c(1, .891, .891,
.891, 1, .891,
.891, .891, 1), nrow=3)
)
Finally it seems relevant to look at the variability across the 10 experiments as well as the variability across "status" within experiments. I'm still working on the code for this as I break it on unbalanced data, but the idea is...
My2Boxes(m=4,f1=dataset$experiment,f2=dataset$status,x=dataset$value,color=c("red","yellow","green"))
Finally the already mentioned Piniero and Bates (2000) book strongly favored lattice from what little I've skimmed.. So you might give that a shot. Maybe something like plotting the raw data...
lattice::xyplot(value~status | experiment, groups=experiment, data=dataset, type=c('p','r'), auto.key=F)
And then plotting the fitted values...
lattice::xyplot(fitted(model)~status | experiment, groups=experiment, data=dataset, type=c('p','r'), auto.key=F)
Here are a few suggestions.
library(ggplot2)
library(lme4)
library(multcomp)
# Creating datasets to get same results as question
dataset <- expand.grid(experiment = factor(seq_len(10)),
status = factor(c("N", "D", "R"),
levels = c("N", "D", "R")),
reps = seq_len(10))
dataset$value <- rnorm(nrow(dataset), sd = 0.23) +
with(dataset, rnorm(length(levels(experiment)),
sd = 0.256)[experiment] +
ifelse(status == "D", 0.205,
ifelse(status == "R", 0.887, 0))) +
2.78
# Fitting model
model <- lmer(value~status+(1|experiment), data = dataset)
# First possibility
tmp <- as.data.frame(confint(glht(model, mcp(status = "Tukey")))$confint)
tmp$Comparison <- rownames(tmp)
ggplot(tmp, aes(x = Comparison, y = Estimate, ymin = lwr, ymax = upr)) +
geom_errorbar() + geom_point()
# Second possibility
tmp <- as.data.frame(confint(glht(model))$confint)
tmp$Comparison <- rownames(tmp)
ggplot(tmp, aes(x = Comparison, y = Estimate, ymin = lwr, ymax = upr)) +
geom_errorbar() + geom_point()
# Third possibility
model <- lmer(value ~ 0 + status + (1|experiment), data = dataset)
tmp <- as.data.frame(confint(glht(model))$confint)
tmp$Comparison <- rownames(tmp)
ggplot(tmp, aes(x = Comparison, y = Estimate, ymin = lwr, ymax = upr)) +
geom_errorbar() + geom_point()
This answer illustrates the newer dotwhisker::dwplot + broom.mixed solution.
Adding one more variable in the simulation:
dataset <- transform(dataset,
value=rnorm(nrow(dataset), sd = 0.23) + ## residual
rnorm(length(levels(experiment)), sd = 0.256)[experiment] + ## block effects
X %*% c(2.78,0.205,0.887),
var2=rnorm(nrow(dataset))) ## fixed effects
Fitting two different models:
library(lme4)
model <- lmer(value~status+var2 + (1|experiment), data = dataset)
model2 <- update(model, . ~ . -var2)
Plotting:
library(broom.mixed)
library(dotwhisker)
dwplot(list(first=model,second=model2), effects="fixed")+
geom_vline(xintercept=0, lty=2)
(using effects="fixed" gets us just the fixed-effect parameters, dropping the intercept by default).
broom.mixed has many other options. When I want to do something complex I may use ggplot + ggstance::geom_pointrangeh (+ position="position_dodgev") to make my own custom plot rather than relying on dotwhisker::dwplot().