library(knitr)
library(dplyr)
library(ggplot2)
library(tidyr)
library(pheatmap)
library(RColorBrewer)
library(plotly)
Para realizar a análise estatística de uma amostra como a Turnera, com o objetivo de comparar os compostos orgânicos voláteis (COVs) da folha e da flor, foram definidas, na matriz de dados (previamente processada utilizando o MZmine), duas classes referentes às amostras analisadas em triplicata, sendo elas flor e folha.
Foi utilizado o teste t de Student, uma ferramenta estatística empregada para verificar se existe diferença significativa entre dois grupos, neste caso, as duas classes (flor e folha). Esse teste calcula um valor t com base na diferença entre as médias, na variância e no tamanho das amostras, comparando-o a um valor de p, que é utilizado para aceitar ou rejeitar a hipótese nula. Nos casos em que um composto apresenta valor de p < 0,05, rejeita-se a hipótese nula, admitindo-se que há diferença significativa entre os grupos para esse composto.
dados <- read.csv2("Comparação_TURNERA.csv")
# 1. Transformar Class em fator
dados$Class <- factor(dados$Class)
Class <- dados$Class
sample <- dados$Sample
# 2. Selecionar apenas dados numéricos
dados_numericos <- dados[, -c(1, 2)]
# 3. Converter todas as colunas para numérico
dados_numericos[] <- lapply(dados_numericos, function(x) as.numeric(as.character(x)))
# 4. Normalização z-score
dados_normalizados <- as.data.frame(scale(dados_numericos))
# 5. Teste t sobre os dados NORMALIZADOS
p_valores <- apply(dados_normalizados, 2, function(coluna) {
tryCatch(t.test(coluna ~ Class)$p.value, error = function(e) NA)
})
# Filtra apenas resultados significativos (p < 0,05)
resultados_significativos <- p_valores[!is.na(p_valores) & p_valores < 0.05]
# Tabela com composto e p-valor, ordenado os mais significativos
tabela_p_valores <- data.frame(
Composto = names(resultados_significativos),
P_Valor = resultados_significativos,
row.names = NULL
) %>%
arrange(P_Valor)
knitr::kable(tabela_p_valores, digits = 4,
caption = "Compostos com diferença significativa (p < 0,05) — dados normalizados")
| Composto | P_Valor |
|---|---|
| Caryophyllene | 0.0108 |
| Humulene | 0.0112 |
| X4.Hexen.1.ol…Z. | 0.0117 |
| beta.Cadinene | 0.0159 |
| cis.muurola.3.5.diene | 0.0211 |
| Oxirane…1.methylbutyl. | 0.0237 |
| Cadina.3.9.diene | 0.0373 |
| D.Limonene | 0.0472 |
| X3.Carene | 0.0481 |
# 1. Unir Class com dados normalizados
dados_para_media <- cbind(Class = Class, dados_normalizados)
# 2. Calcular médias por grupo (sintaxe atualizada do dplyr)
medias_calculadas <- dados_para_media %>%
group_by(Class) %>%
summarise(across(everything(), ~ mean(.x, na.rm = TRUE)))
# 3. Transpor para formato de tabela
nomes_compostos <- colnames(dados_normalizados)
tabela_medias <- as.data.frame(t(medias_calculadas[, -1]))
colnames(tabela_medias) <- medias_calculadas$Class
tabela_medias$Composto <- nomes_compostos
# 4. Tabela final com p-valor e destaque por grupo
tabela_final <- tabela_medias %>%
mutate(
P_Valor = p_valores[Composto],
Destaque = ifelse(flor > folha, "FLOR", "FOLHA")
) %>%
filter(P_Valor < 0.05) %>%
arrange(P_Valor) %>%
select(Composto, flor, folha, P_Valor, Destaque)
rownames(tabela_final) <- NULL
knitr::kable(tabela_final, digits = 4,
caption = "Área normalizada, p-valor e grupo com maior intensidade")
| Composto | flor | folha | P_Valor | Destaque |
|---|---|---|---|---|
| Caryophyllene | -0.8810 | 0.8810 | 0.0108 | FOLHA |
| Humulene | -0.8928 | 0.8928 | 0.0112 | FOLHA |
| X4.Hexen.1.ol…Z. | -0.8919 | 0.8919 | 0.0117 | FOLHA |
| beta.Cadinene | -0.8846 | 0.8846 | 0.0159 | FOLHA |
| cis.muurola.3.5.diene | 0.8755 | -0.8755 | 0.0211 | FLOR |
| Oxirane…1.methylbutyl. | -0.8711 | 0.8711 | 0.0237 | FOLHA |
| Cadina.3.9.diene | 0.8483 | -0.8483 | 0.0373 | FLOR |
| D.Limonene | 0.8322 | -0.8322 | 0.0472 | FLOR |
| X3.Carene | 0.8309 | -0.8309 | 0.0481 | FLOR |
Para a visualização dos dados, foram construídos gráficos do tipo boxplot (diagrama de caixas) com o objetivo de representar a distribuição das intensidades normalizadas dos compostos mais significativos. Os gráficos foram gerados separadamente para os compostos com maior destaque em flor e em folha.
No eixo X estão representados os compostos selecionados e no eixo Y as intensidades normalizadas. A mediana, os quartis e os valores extremos permitem visualizar de forma clara as diferenças de distribuição entre as duas classes.
# 1. Compostos significativos separados por grupo de destaque
compostos_flor <- tabela_final$Composto[tabela_final$Destaque == "FLOR"]
compostos_folha <- tabela_final$Composto[tabela_final$Destaque == "FOLHA"]
# 2. gerar boxplot
gerar_boxplot <- function(compostos, titulo, grupo_destaque) {
if (length(compostos) == 0) {
message(paste("Nenhum composto significativo para:", titulo))
return(invisible(NULL))}
# Transformar para formato longo
dados_long <- dados_normalizados[, compostos, drop = FALSE] %>%
mutate(Class = Class) %>%
pivot_longer(cols = -Class, names_to = "Composto", values_to = "Intensidade")
# Remover níveis não usados
dados_long$Class <- droplevels(dados_long$Class)
# Definir cores
cores <- if (grupo_destaque == "FLOR") {
c("flor" = "#E07B8A", "folha" = "gray70")
} else {
c("flor" = "gray70", "folha" = "#6BAE75")
}
# Gerar gráfico
ggplot(dados_long, aes(x = Composto, y = Intensidade, fill = Class)) +
geom_boxplot(outlier.shape = 21, outlier.size = 2, alpha = 0.85) +
scale_fill_manual(values = cores, drop = TRUE) +
labs(
title = titulo,
x = "Composto",
y = "Intensidade normalizada",
fill = "Classe"
) +
theme_classic(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 9),
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "top",
legend.title = element_text(face = "bold"))}
# 3. Gerar os gráficos
gerar_boxplot(compostos_flor, "Compostos com maior intensidade em FLOR", "FLOR")
gerar_boxplot(compostos_folha, "Compostos com maior intensidade em FOLHA", "FOLHA")
Outra forma de visualizar as intensidades dos compostos entre as duas classes é por meio de um mapa de calor (heatmap). Essa abordagem pode ser combinada com a análise de agrupamento hierárquico (HCA), permitindo avaliar se as amostras se agrupam de acordo com suas respectivas classes, folha e flor.
# 1. MATRIZ COMPLETA
matriz_total <- dados_normalizados
rownames(matriz_total) <- sample
# Clustering das amostras
dist_rows <- dist(matriz_total, method = "euclidean")
hclust_rows <- hclust(dist_rows, method = "ward.D2")
# 2. MATRIZ SIGNIFICATIVA
compostos_sig <- tabela_final$Composto
matriz_sig <- dados_normalizados[, compostos_sig, drop = FALSE]
rownames(matriz_sig) <- sample
# 3. ANOTAÇÃO
anotacao <- data.frame(Classe = Class)
rownames(anotacao) <- sample
cores_anotacao <- list(
Classe = c("flor" = "#E07B8A", "folha" = "#6BAE75"))
# 4. CORES HEATMAP
cores_heatmap <- colorRampPalette(
rev(brewer.pal(n = 7, name = "RdYlBu")))(100)
# 5. HEATMAP FINAL
pheatmap(
matriz_sig,
cluster_rows = hclust_rows,
# Colunas (compostos)
clustering_method = "ward.D2",
clustering_distance_cols = "euclidean",
annotation_row = anotacao,
annotation_colors = cores_anotacao,
scale = "none",
color = cores_heatmap,
fontsize = 10,
fontsize_row = 8,
fontsize_col = 8,
main = "Heatmap + HCA dos Compostos Significativos")
Com base no heatmap dos compostos significativos, observa-se de forma evidente uma clara distinção entre as amostras das classes folha e flor, em função das diferenças nas intensidades dos compostos. Essa separação é reforçada pela análise de agrupamento hierárquico (HCA), que evidencia a formação de clusters bem definidos, correspondentes a cada uma das classes analisadas.
Também foi construído um heatmap utilizando todos os compostos detectados, com o objetivo de visualizar o perfil geral das amostras, com base na intensidade dos compostos.
# 1. Matriz de dados
matriz_heatmap <- dados_normalizados
# 2. Definir nomes das amostras
rownames(matriz_heatmap) <- sample
# 3. Criar anotação (classe)
anotacao <- data.frame(Classe = Class)
rownames(anotacao) <- sample
# 4. Cores da anotação
cores_anotacao <- list(
Classe = c("flor" = "#E07B8A", "folha" = "#6BAE75"))
# 5. Paleta de cores do heatmap (melhor contraste)
cores_heatmap <- colorRampPalette(
rev(brewer.pal(n = 7, name = "RdYlBu")))(100)
# 6. Gerar heatmap + HCA
pheatmap(matriz_heatmap,
# Clustering
clustering_method = "ward.D2",
clustering_distance_rows = "euclidean",
clustering_distance_cols = "euclidean",
# Anotações
annotation_row = anotacao,
annotation_colors = cores_anotacao,
# Aparência
scale = "none",
color = cores_heatmap,
fontsize = 10,
fontsize_row = 8,
fontsize_col = 6,
show_colnames = TRUE,
main = "Heatmap + HCA de Todos os Compostos")
Novamente, foi possível observar uma separação bem definida entre os clusters correspondentes às classes folha e flor. Além disso, as intensidades das áreas apresentaram uma distribuição heterogênea entre as classes, refletindo diferenças no perfil químico das amostras.
A análise de componentes principais (PCA) foi empregada como uma abordagem multivariada com o objetivo de reduzir a dimensionalidade dos dados e identificar padrões de similaridade entre as amostras. Essa técnica permite projetar os dados em um novo espaço de variáveis ortogonais (componentes principais), maximizando a variância explicada. Dessa forma, torna-se possível avaliar a separação entre as classes folha e flor.
A análise de componentes principais (PCA) foi realizada utilizando a biblioteca plotly, que permite a construção de gráficos interativos, possibilitando melhor exploração visual dos dados em três dimensões. Essa abordagem facilita a identificação de padrões e a separação entre as amostras.
Como o conjunto de dados é composto por uma única amostra analisada em triplicata e apresenta um número reduzido de compostos orgânicos voláteis (VOCs), a PCA conseguiu explicar uma elevada proporção da variância total dos dados, atingindo 93,3% nos três primeiros componentes principais (PC1, PC2 e PC3). Esse resultado indica que a maior parte da informação contida nos dados foi capturada nesses eixos.
Além disso, observa-se uma clara separação entre as amostras das classes folha e flor no espaço multivariado, evidenciando diferenças consistentes entre os perfis químicos, mesmo considerando o número limitado de variáveis analisadas.
# PCA
pca_res <- prcomp(dados_normalizados, scale. = FALSE)
# Scores (amostras)
scores <- as.data.frame(pca_res$x)
scores$Class <- Class
scores$Sample <- sample
# Variância explicada
var_exp <- (pca_res$sdev^2 / sum(pca_res$sdev^2)) * 100
# PCA 3D
plot_ly(
data = scores,
x = ~PC1,
y = ~PC2,
z = ~PC3,
color = ~Class,
colors = c("flor" = "#E07B8A", "folha" = "#6BAE75"),
text = ~Sample,
type = "scatter3d",
mode = "markers",
marker = list(size = 5)
) %>%
layout(
title = "PCA 3D das Amostras",
scene = list(
xaxis = list(title = paste0("PC1 (", round(var_exp[1],1), "%)")),
yaxis = list(title = paste0("PC2 (", round(var_exp[2],1), "%)")),
zaxis = list(title = paste0("PC3 (", round(var_exp[3],1), "%)"))))