Análise de experimento exvivo para avaliar severidade e tempo de incubação
Autor
Prof. Dr. Walmes Zeviani
1 Carregamento dos pacotes
Código
#-----------------------------------------------------------------------# Pacotes.library(emmeans)library(glmnet)library(nlme)library(tidyverse)# Para não imprimir a matriz de correlação das estimativas.# assignInNamespace("print.correlation",# function(x, title) { return() },# ns = "nlme")
2 Importação e exame inicial
Código
#-----------------------------------------------------------------------# Importação e preparo dos dados.# Importa da planilha.tb <- readxl::read_xlsx("invivo_reorganized.xlsx",sheet =1,na =c("na", ""),guess_max =5000)str(tb)
tibble [3,600 × 9] (S3: tbl_df/tbl/data.frame)
$ iso : chr [1:3600] "CrAaPR-40" "CrAaPR-40" "CrAaPR-40" "CrAaPR-40" ...
$ exp : num [1:3600] 1 1 1 1 1 1 1 1 1 1 ...
$ mol : num [1:3600] 36 36 36 36 36 24 24 24 24 24 ...
$ vaso : num [1:3600] 1 1 1 1 1 1 1 1 1 1 ...
$ ramo : num [1:3600] 1 1 1 1 1 1 1 1 1 1 ...
$ folha: num [1:3600] 1 2 3 4 5 1 2 3 4 5 ...
$ incid: num [1:3600] 0 0 0 0 0 0 0 0 0 0 ...
$ sev : num [1:3600] NA NA NA NA NA NA NA NA NA NA ...
$ time : num [1:3600] 12 12 12 12 12 12 12 12 12 12 ...
# NOTE: ramo tá representando algo equivocado aqui.
Código
#-----------------------------------------------------------------------# Gráficos.# Molhamento x incidência.ggplot(data = tb,mapping =aes(x = mol, y = incid, color = iso)) +facet_grid(time ~ exp) +# geom_point() +geom_jitter(width =1, height =0.1) +stat_summary(geom ="line", fun ="mean")
Código
# Tempo x incidência.ggplot(data = tb,mapping =aes(x = time, y = incid, color = iso)) +facet_grid(mol ~ exp) +# geom_point() +geom_jitter(width =1, height =0.1) +stat_summary(geom ="line", fun ="mean")
Código
# Efeito da posição da folha x molhamento.ggplot(data = tb,mapping =aes(x = time, y = incid)) +facet_grid(mol ~ folha) +# geom_point() +geom_jitter(width =1, height =0.1) +stat_summary(geom ="line", fun ="mean")
Código
# Efeito da posição da folha x isolado.ggplot(data = tb,mapping =aes(x = time, y = incid)) +facet_grid(iso ~ folha) +# geom_point() +geom_jitter(width =1, height =0.1) +stat_summary(geom ="line", fun ="mean")
Código
# Severidade.ggplot(data =filter(tb, time ==max(time)),mapping =aes(x = mol, y = sev)) +facet_grid(iso ~ exp) +# geom_point() +geom_jitter(width =0.2, height =0.1) +stat_summary(geom ="line", fun ="mean")
3 Severidade
3.1 Preparo e análise exploratória
Código
#-----------------------------------------------------------------------# Análise da severidade ------------------------------------------------#-----------------------------------------------------------------------# Criação da tabela para análise dos dados.# Define os fatores.tb <- tb |>mutate(Iso =factor(iso),Exp =factor(exp),Mol =factor(mol),Vaso =interaction(exp, vaso, sep ="_"),Ramo =interaction(exp, vaso, iso, mol, sep ="_"),Folha =factor(folha)) |>droplevels()str(tb)
# NOTE: As folhas de um mesmo ramo são bastante correlacionadas. Como# não há hipótese para o efeito da folha, vamos trabalhar com valores# agregados por ramo.# Obtenção de um resumo da severidade por ramo.tb_sev <- tb |>filter(time ==max(time)) |>group_by(Exp, Iso, Mol, mol, Vaso, Ramo) |># summarize_at("sev", mean, na.rm = TRUE) |># summarize_at("sev", median, na.rm = TRUE) |>summarize_at("sev", max, na.rm =TRUE) |>ungroup()str(tb_sev)
# NOTE: Vários testes foram feitos. A severidade máxima é a mais# interessante.# Define a escala da variável resposta.# tb_sev$resp_sev <- tb_sev$sevtb_sev$resp_sev <-log10(tb_sev$sev +1)
3.2 Experimento 1
Código
#-----------------------------------------------------------------------# Experimento 1.# Modelo de 1 estrato apenas para examinar os pressupostos.m0 <-lm(resp_sev ~ Iso * Mol + Vaso,data =filter(tb_sev, Exp =="1"))# Exame dos pressupostos. Em caso de afastamento, tentar transformações# para a resposta que reduzam violações.par(mfrow =c(2, 2))plot(m0)
Código
layout(1)# Modelo de efeitos aleatórios.m0 <-lme(resp_sev ~ Iso * Mol,random =~1| Vaso,data =filter(tb_sev, Exp =="1"),method ="ML")# Componentes de variância.VarCorr(m0)
# anova(m1, m0)# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Iso) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Iso emmean SE df lower.CL upper.CL .group
CrAaPR-40 1.456 0.0568 9 1.328 1.585 a
CrAlPR-73 1.226 0.0568 9 1.098 1.354 b
CrAaPR-01 0.613 0.0568 9 0.485 0.742 c
CrAaPR-27 0.442 0.0568 9 0.314 0.571 c
Results are averaged over the levels of: Mol
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 4 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.
# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Mol) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Mol emmean SE df lower.CL upper.CL .group
36 1.156 0.0501 9 1.043 1.270 a
24 0.979 0.0508 9 0.864 1.094 b
12 0.667 0.0508 9 0.552 0.782 c
Results are averaged over the levels of: Iso
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 3 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.
#-----------------------------------------------------------------------# Experimento 2.# Modelo de 1 estrato apenas para examinar os pressupostos.m0 <-lm(resp_sev ~ Iso * Mol + Vaso,data =filter(tb_sev, Exp =="2"))# Exame dos pressupostos. Em caso de afastamento, tentar transformações# para a resposta que reduzam violações.par(mfrow =c(2, 2))plot(m0)
Código
layout(1)# Modelo de efeitos aleatórios.m0 <-lme(resp_sev ~ Iso * Mol,random =~1| Vaso,data =filter(tb_sev, Exp =="2"),method ="ML")# Componentes de variância.VarCorr(m0)
# anova(m1, m0)# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Iso) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Iso emmean SE df lower.CL upper.CL .group
CrAaPR-40 1.396 0.0386 9 1.309 1.483 a
CrAlPR-73 1.251 0.0386 9 1.163 1.338 b
CrAaPR-01 0.517 0.0386 9 0.430 0.604 c
CrAaPR-27 0.461 0.0386 9 0.374 0.548 c
Results are averaged over the levels of: Mol
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 4 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.
# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Mol) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Mol emmean SE df lower.CL upper.CL .group
36 1.157 0.0345 9 1.079 1.235 a
24 1.046 0.0352 9 0.966 1.126 b
12 0.516 0.0352 9 0.437 0.596 c
Results are averaged over the levels of: Iso
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 3 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.
#-----------------------------------------------------------------------# Análise da incidência ------------------------------------------------#-----------------------------------------------------------------------# Preparo dos dados.tb |>select(Exp, Iso, Mol, time, incid) |>summary()
Exp Iso Mol time incid
1:1800 CrAaPR-01:900 12:1200 Min. : 12 Min. :0.0000
2:1800 CrAaPR-27:900 24:1200 1st Qu.: 24 1st Qu.:0.0000
CrAaPR-40:900 36:1200 Median : 42 Median :0.0000
CrAlPR-73:900 Mean : 88 Mean :0.4019
3rd Qu.:120 3rd Qu.:1.0000
Max. :288 Max. :1.0000
NA's :10
Código
# tb |># filter(exp == 2) |># lattice::xyplot(incid ~ sqrt(time) | Ramo,# # group = Folha, auto.key = TRUE,# col = 1,# type = c("p", "a"), pch = 19,# data = _,# # strip = FALSE,# as.table = TRUE)# Não pode a incidência voltar para 0 depois que for 1.tb |>filter(Ramo =="2_6_CrAlPR-73_12") |>select(folha, time, incid) |>arrange(folha, time) |>print(n =Inf)
# A tibble: 3 × 2
incid n
<dbl> <int>
1 0 2107
2 1 1443
3 NA 50
Código
# Em quais ramos ocorrem?tb_incid |>filter(is.na(incid)) |>count(Ramo)
# A tibble: 2 × 2
Ramo n
<fct> <int>
1 1_2_CrAaPR-01_24 25
2 1_2_CrAaPR-27_24 25
Código
# # Como é o padrão?# tb_incid |># filter(Ramo %in% c("1_2_CrAaPR-01_24", "1_2_CrAaPR-27_24")) |># select(Ramo, folha, time, incid) |># arrange(Ramo, folha, time) |># print(n = Inf)# Abandona condições que só tiveram NA.tb_incid <- tb_incid |>filter(!Ramo %in%c("1_2_CrAaPR-01_24", "1_2_CrAaPR-27_24"))
4.2 Estimação do período de incubação
Código
#-----------------------------------------------------------------------# Determinar o tempo para incidencia de 50% usando modelo.# Usar um modelo logístico pode não funcionar porque em muitos casos é# linearmente separável. Então tem que contornar isso, por exemplo,# usando regularização. Também é interessante usar uma escala que deixa# mais regular o espaçamento no tempo. É possível também mudar a função# de ligação. São possibilidades a serem exploradas.##' plot(jitter(incid, 0.1) ~ jitter(log2(time), 0.2),##' data = tb_incid)##' plot(jitter(incid, 0.1) ~ jitter(sqrt(time), 0.2),##' data = tb_incid)# Define a escala da variável tempo para deixar o espaçamento mais# regular.tb_incid$f_time <-sqrt(tb_incid$time)# library(glmnet)# Cria a matriz de dados e a reposta usando todos os registros.X <-model.matrix(~0+ Ramo + f_time, data = tb_incid)y <- tb_incid$incidtable(y) |>prop.table()
y
0 1
0.5923729 0.4076271
Código
# Estimando com todos os dados (sem regularização).coef(glm(incid ~ f_time, data = tb_incid, family ="binomial")) |>setNames(c("b0", "b1")) |>as.list() |>with({c(b0 = b0, b1 = b1, x =-b0/b1) })
# Determina o parâmetro lambda de regularização usando todos os dados.cv_ridge <- glmnet::cv.glmnet(X, y, alpha =0, family ="binomial")cv_ridge$lambda.min
[1] 0.02022836
Código
# Função para fazer o ajuste e retornar o período de incidencia.get_incub_period <-function(tbl,formula = y ~ x,lambda =0,alpha =1) { X <-model.matrix(formula, data = tbl) y <- tbl[[all.vars(formula)[1]]]# Ajusta o modelo com regularização.suppressWarnings({ m0 <- glmnet::glmnet(X, y,family ="binomial",alpha = alpha,lambda = lambda) })# Extrai e renomeia os coeficientes. m0_coef <-coef(m0) |>unclass() |>attr("x") |>setNames(c("b0", "b1"))# Valor em x que corresponde a \hat{p} = 0.5. x <-unname(-m0_coef["b0"]/m0_coef["b1"])return(append(m0_coef, c(x = x)))}
Código
# Calcula o período de incidência para todos os ramos.tb_incub <- tb_incid |>group_by(Ramo) |>summarise(prop_incid =mean(incid),params =list({ params <-try(silent =TRUE,expr = {get_incub_period(tbl =data.frame(time, incid),formula = incid ~ f_time,lambda = cv_ridge$lambda.min) })if (class(params) =="try-error") {tibble(b0 =NA_real_, b1 =NA_real_,x =NA_real_) } else {tibble(b0 = params["b0"], b1 = params["b1"],x = params["x"]) } })) |>unnest(params) |>mutate(incub_period = x^2)tb_incub
# Faz o gráfico com as curvas.ggplot(data = tb_incid,mapping =aes(x = f_time, y = incid)) +facet_wrap(facets =~Ramo) +geom_jitter(width =0.1, height =0.05,color ="gray",pch =19,size =0.75) +geom_line(data = tb_curves,mapping =aes(x = f_time, y = p),inherit.aes =FALSE) +geom_vline(data = tb_incub,mapping =aes(xintercept = x),linewidth =0.25,color ="red") +geom_hline(yintercept =0.5, color ="red", linewidth =0.25) +# theme_minimal() +# theme_void() +theme_bw() +theme(strip.background =element_blank(),strip.text.x =element_blank(),# panel.border = element_blank(),panel.grid.major =element_blank(),panel.grid.minor =element_blank(),panel.background =element_blank())
Código
# IMPORTANT: Aqui foi abandonado as situações em que não houve estimação# de período de incubação, ou seja, onde estimou NA. Acontece que são# situações onde o período de incubação é bem alto e não foi possível# estimar a partir dos dados porque deveria se observar por mais tempo.# Então na realidade o período de incubação ali é grande, porém# desconhecido. Abandonar essas unidades experimentais leva a# subestimação do período de incubação. Seria interessante empregar um# modelo misto para resposta Gaussiana com cersura. Isso vai dificultar# a análise, então iremos abandonar os NA. Para estudos futuros, ajustar# o tempo de observação do experimento para evitar falhas na estimação# do período de incubação.
Código
#-----------------------------------------------------------------------# Análise exploratória.# Estima as proporções de incidência em cada tempo.tb_incid |>group_by(Ramo, time) |>summarise(prop_incid =mean(incid), .groups ="drop") |># ungroup() |>pivot_wider(names_from = time, values_from = prop_incid) |>left_join(tb_incub, by ="Ramo")
# NOTE: Foram poucas os ramos que tiveram incidência observada superior# a 50%. Portanto, por estimativas amostrais, seria bem mais complicado.# Junta os dados.tb_incub <-left_join(tb_incub,distinct(tb_incid, Ramo, Vaso, Exp, Iso, Mol, mol),by ="Ramo")# Análise exploratória.tb_incub |>drop_na(incub_period) |>ggplot(data = _,mapping =aes(x = mol, y = incub_period)) +facet_grid(facets = Exp ~ Iso) +# geom_point() +geom_jitter(width =1, height =1) +stat_summary(geom ="line", fun ="mean")
Código
# Define a escala em que a resposta será analisada.tb_incub$resp_incub <- tb_incub$incub_period# tb_incub$resp_incub <- log10(tb_incub$incub_period)
4.3 Experimento 1
Código
#-----------------------------------------------------------------------# Análise para o experimento 1.# Modelo de 1 estrato apenas para examinar os pressupostos.m0 <-lm(resp_incub ~ Iso * Mol + Vaso,data =filter(tb_incub, Exp =="1"))# Exame dos pressupostos. Em caso de afastamento, tentar transformações# para a resposta que reduzam violações.par(mfrow =c(2, 2))plot(m0)
Código
layout(1)# Modelo de efeitos aleatórios.m0 <-lme(resp_incub ~ Iso * Mol,random =~1| Vaso,data =filter(tb_incub, Exp =="1") |>drop_na(incub_period),method ="ML")# Componentes de variância.VarCorr(m0)
# anova(m1, m0)# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Iso) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Iso emmean SE df lower.CL upper.CL .group
CrAaPR-27 305.2 45.4 9 202.4 408.0 a
CrAaPR-01 258.2 36.1 9 176.7 339.8 a
CrAlPR-73 97.8 33.7 9 21.6 174.1 b
CrAaPR-40 23.4 33.7 9 -52.9 99.6 b
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 4 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.
#-----------------------------------------------------------------------# Análise para o experimento 2.# Modelo de 1 estrato apenas para examinar os pressupostos.m0 <-lm(resp_incub ~ Iso * Mol + Vaso,data =filter(tb_incub, Exp =="2"))# Exame dos pressupostos. Em caso de afastamento, tentar transformações# para a resposta que reduzam violações.par(mfrow =c(2, 2))plot(m0)
Código
layout(1)# Modelo de efeitos aleatórios.m0 <-lme(resp_incub ~ Iso * Mol,random =~1| Vaso,data =filter(tb_incub, Exp =="2") |>drop_na(incub_period),method ="ML")# Componentes de variância.VarCorr(m0)
# anova(m1, m0)# Obtém as médias e aplica comparações múltiplas.tb_means <-emmeans(m1, specs =~Iso) |> multcomp::cld(Letters = letters, reverse =TRUE)tb_means
Iso emmean SE df lower.CL upper.CL .group
CrAaPR-27 274.2 19.1 9 231.0 317.5 a
CrAaPR-01 270.3 19.1 9 227.0 313.6 a
CrAlPR-73 63.7 19.1 9 20.4 106.9 b
CrAaPR-40 19.9 19.1 9 -23.4 63.2 b
Degrees-of-freedom method: containment
Confidence level used: 0.95
P value adjustment: tukey method for comparing a family of 4 estimates
significance level used: alpha = 0.05
NOTE: If two or more means share the same grouping symbol,
then we cannot show them to be different.
But we also did not show them to be the same.