绘制火山图 R、python
R
对每个微生物(菌种)进行线性混合效应模型(LMM)分析,得到每个微生物的效应量(log2 fold change)和p值。
对p值进行Benjamini-Hochberg多重检验校正,得到校正后的p值(即FDR)。
根据给定的阈值(log2FC的绝对值≥0.58,且-log10(FDR)≥1.301)确定显著差异的微生物。
绘制火山图,用虚线标明阈值线。
由于问题中没有提供具体的数据结构,我们假设数据格式如下:
一个数据框,行是微生物(菌种),列是样本。另外,还有一个分组变量(例如,两组:Group1和Group2)以及可能的随机效应变量(例如,个体ID,时间点等)。
但是,由于问题中只提到了两组微生物的表达数据,并且使用了线性混合效应模型,我们假设数据是纵向数据或者有配对设计,因此需要考虑随机效应。
然而,问题中并没有提供具体的随机效应变量。为了简化,我们假设每个样本有重复测量,并且有一个随机效应(例如,患者ID)。因此,我们需要在模型中加入随机效应。
步骤:
加载必要的R包:lme4(用于线性混合效应模型),lmerTest(用于得到p值),ggplot2(用于绘图),dplyr(用于数据处理)。
准备数据:将微生物表达数据整理成长格式,每一行是一个样本的一个微生物的表达值。
对每个微生物拟合线性混合效应模型,提取log2FC和p值。
对p值进行多重检验校正。
根据阈值标注显著差异的微生物。
绘制火山图。
注意:由于微生物数量可能很多,我们需要高效地拟合模型。可以使用循环或者函数式编程。
假设数据框结构如下:
样本信息:SampleID, Group, SubjectID (随机效应)
微生物表达数据:每个微生物一列,例如 Microbe1, Microbe2, ...
我们将数据转换为长格式,然后按微生物分组拟合模型。
由于问题中没有提供数据,我将生成一个模拟数据来演示。
但是,请注意:在实际分析中,你的数据可能有所不同,需要根据实际情况调整。
我们开始写代码:
步骤1:生成模拟数据(假设有100个微生物,20个样本,10个受试者,每个受试者有两个时间点或两个条件)
步骤2:数据整理
步骤3:模型拟合
步骤4:校正p值
步骤5:火山图
由于线性混合效应模型拟合可能比较慢,我们可以使用lme4包中的lmer函数,并使用lmerTest获取p值。
注意:在模拟数据中,我们假设有一些微生物是差异表达的。
r
# 加载必要的包:
# install.packages(c("ggplot2", "dplyr", "lme4", "lmerTest", "reshape2"))
library(ggplot2)
library(dplyr)
library(lme4)
library(lmerTest)
library(reshape2)
# 设置随机种子以保证结果可重复
set.seed(123)
# 1. 创建模拟数据(如果您已有实际数据,请跳过此步骤)
# 假设有100个微生物物种,20个样本(10个对照组,10个实验组)
create_simulated_data <- function(n_species = 100, n_samples_per_group = 10) {
# 创建微生物名称
species_names <- paste0("Species_", sprintf("%03d", 1:n_species))
# 创建样本信息
samples <- c(paste0("Control_", 1:n_samples_per_group),
paste0("Treatment_", 1:n_samples_per_group))
groups <- factor(c(rep("Control", n_samples_per_group),
rep("Treatment", n_samples_per_group)))
subject <- factor(rep(1:(n_samples_per_group/2), each = 2, times = 2)) # 模拟配对设计
# 创建表达矩阵(添加组间差异)
expr_matrix <- matrix(NA, nrow = n_species, ncol = length(samples))
rownames(expr_matrix) <- species_names
colnames(expr_matrix) <- samples
for (i in 1:n_species) {
# 基础表达水平
base_expr <- rnorm(1, mean = 10, sd = 2)
# 模拟差异表达
if (i <= 10) { # 前10个物种显著上调
control_expr <- rnorm(n_samples_per_group, mean = base_expr, sd = 1)
treatment_expr <- rnorm(n_samples_per_group, mean = base_expr + 2, sd = 1)
} else if (i <= 20) { # 11-20个物种显著下调
control_expr <- rnorm(n_samples_per_group, mean = base_expr, sd = 1)
treatment_expr <- rnorm(n_samples_per_group, mean = base_expr - 2, sd = 1)
} else { # 其他物种无显著差异
control_expr <- rnorm(n_samples_per_group, mean = base_expr, sd = 1)
treatment_expr <- rnorm(n_samples_per_group, mean = base_expr, sd = 1)
}
expr_matrix[i, 1:n_samples_per_group] <- control_expr
expr_matrix[i, (n_samples_per_group + 1):(2 * n_samples_per_group)] <- treatment_expr
}
# 创建元数据
metadata <- data.frame(
Sample = samples,
Group = groups,
Subject = subject
)
return(list(expr_matrix = expr_matrix, metadata = metadata))
}
# 2. 差异表达分析函数(使用线性混合效应模型)
perform_differential_analysis <- function(expr_matrix, metadata) {
results <- data.frame()
# 对每个微生物物种进行分析
for (species in rownames(expr_matrix)) {
# 准备数据
df <- data.frame(
Expression = as.numeric(expr_matrix[species, ]),
Group = metadata$Group,
Subject = metadata$Subject,
Sample = metadata$Sample
)
# 拟合线性混合效应模型
# 这里假设Subject是随机效应,Group是固定效应
tryCatch({
# 使用lmerTest包中的lmer,可以计算p值
model <- lmer(Expression ~ Group + (1 | Subject), data = df)
# 获取模型摘要
model_summary <- summary(model)
# 提取Treatment组的效应
coef_row <- which(rownames(model_summary$coefficients) == "GroupTreatment")
if (length(coef_row) > 0) {
# 计算log2 fold change
# 注意:对于线性模型,系数本身就是Treatment vs Control的差异
log2FC <- model_summary$coefficients[coef_row, "Estimate"]
# 获取p值
p_value <- model_summary$coefficients[coef_row, "Pr(>|t|)"]
# 保存结果
results <- rbind(results, data.frame(
Species = species,
log2FC = log2FC,
p_value = p_value
))
}
}, error = function(e) {
# 如果模型拟合失败,记录NA值
results <<- rbind(results, data.frame(
Species = species,
log2FC = NA,
p_value = NA
))
})
}
# 多重检验校正(Benjamini-Hochberg方法)
results$adj_p_value <- p.adjust(results$p_value, method = "BH")
# 计算-log10(p值)
results$neg_log10_p <- -log10(results$adj_p_value)
return(results)
}
# 3. 筛选显著差异表达的物种
identify_significant_species <- function(results,
log2FC_threshold = 0.58,
neg_log10_p_threshold = 1.301) {
results$Significance <- "Not Significant"
# 根据阈值标注显著性
results$Significance[results$log2FC >= log2FC_threshold &
results$neg_log10_p >= neg_log10_p_threshold] <- "Up-regulated"
results$Significance[results$log2FC <= -log2FC_threshold &
results$neg_log10_p >= neg_log10_p_threshold] <- "Down-regulated"
# 统计显著物种数量
n_up <- sum(results$Significance == "Up-regulated")
n_down <- sum(results$Significance == "Down-regulated")
cat(sprintf("显著上调物种数: %d\n", n_up))
cat(sprintf("显著下调物种数: %d\n", n_down))
cat(sprintf("总显著物种数: %d\n", n_up + n_down))
return(results)
}
# 4. 绘制火山图
create_volcano_plot <- function(results,
log2FC_threshold = 0.58,
neg_log10_p_threshold = 1.301,
title = "Differential Abundance of Microbial Species") {
# 设置颜色
colors <- c("Up-regulated" = "#E64B35",
"Down-regulated" = "#3182bd",
"Not Significant" = "#bdbdbd")
# 创建火山图
p <- ggplot(results, aes(x = log2FC, y = neg_log10_p, color = Significance)) +
geom_point(alpha = 0.6, size = 2) +
scale_color_manual(values = colors) +
# 添加阈值线
geom_vline(xintercept = c(-log2FC_threshold, log2FC_threshold),
linetype = "dashed", color = "black", alpha = 0.5) +
geom_hline(yintercept = neg_log10_p_threshold,
linetype = "dashed", color = "black", alpha = 0.5) +
# 设置坐标轴
labs(x = expression(Log[2]~Fold~Change~"(Treatment/Control)"),
y = expression(-Log[10]~"(Adjusted P-value)"),
title = title,
color = "Significance") +
# 添加主题
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
axis.title = element_text(face = "bold"),
legend.position = "right",
panel.grid.major = element_line(color = "grey90", linewidth = 0.2),
panel.grid.minor = element_blank(),
panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5)
) +
# 调整坐标轴范围
xlim(min(results$log2FC, na.rm = TRUE) - 0.5,
max(results$log2FC, na.rm = TRUE) + 0.5) +
ylim(0, max(results$neg_log10_p, na.rm = TRUE) + 0.5)
return(p)
}
# 5. 获取top差异表达物种
get_top_species <- function(results, n_top = 10) {
# 按显著性排序
results_sorted <- results[order(results$adj_p_value), ]
# 获取上调物种
up_species <- results_sorted[results_sorted$log2FC > 0, ]
top_up <- head(up_species, n_top)
# 获取下调物种
down_species <- results_sorted[results_sorted$log2FC < 0, ]
top_down <- head(down_species, n_top)
return(list(top_up = top_up, top_down = top_down))
}
# 6. 主分析流程
main_analysis <- function() {
cat("=== 微生物差异表达分析 ===\n")
# 步骤1: 创建或加载数据
cat("1. 准备数据...\n")
data <- create_simulated_data(n_species = 100, n_samples_per_group = 10)
# 如果您有实际数据,请替换为:
# expr_matrix <- your_expression_matrix # 行是物种,列是样本
# metadata <- your_metadata_dataframe # 包含Group, Subject等列
# 步骤2: 差异表达分析
cat("2. 进行差异表达分析(线性混合效应模型)...\n")
results <- perform_differential_analysis(data$expr_matrix, data$metadata)
# 步骤3: 筛选显著物种
cat("3. 筛选显著差异表达的物种...\n")
results <- identify_significant_species(results)
# 步骤4: 创建火山图
cat("4. 绘制火山图...\n")
volcano_plot <- create_volcano_plot(results)
print(volcano_plot)
# 步骤5: 获取top物种
cat("5. 获取top差异表达物种...\n")
top_species <- get_top_species(results, n_top = 10)
cat("\n=== Top 10 上调物种 ===\n")
print(top_species$top_up[, c("Species", "log2FC", "adj_p_value")])
cat("\n=== Top 10 下调物种 ===\n")
print(top_species$top_down[, c("Species", "log2FC", "adj_p_value")])
# 步骤6: 保存结果
cat("\n6. 保存结果...\n")
# 保存完整结果
write.csv(results, "microbial_differential_analysis_results.csv", row.names = FALSE)
# 保存火山图
ggsave("volcano_plot.png", volcano_plot, width = 10, height = 8, dpi = 300)
cat("\n分析完成!结果已保存到当前目录。\n")
return(list(results = results, plot = volcano_plot))
}
# 7. 运行分析
analysis_results <- main_analysis()
# 8. 可选:高级可视化(添加物种标签)
create_labeled_volcano <- function(results, n_label = 10) {
# 选择最显著的物种进行标注
results_significant <- results[results$Significance != "Not Significant", ]
results_significant <- results_significant[order(results_significant$adj_p_value), ]
# 获取top上调物种
top_up <- head(results_significant[results_significant$log2FC > 0, ], n_label/2)
# 获取top下调物种
top_down <- head(results_significant[results_significant$log2FC < 0, ], n_label/2)
top_labels <- rbind(top_up, top_down)
# 创建带标签的火山图
p_labeled <- create_volcano_plot(results) +
geom_text(data = top_labels,
aes(label = Species),
size = 3,
color = "black",
vjust = -0.5,
hjust = 0.5,
check_overlap = TRUE) +
labs(subtitle = paste("Labeled:", n_label, "most significant species"))
return(p_labeled)
}
# 创建带标签的火山图
labeled_plot <- create_labeled_volcano(analysis_results$results, n_label = 10)
print(labeled_plot)
ggsave("volcano_plot_labeled.png", labeled_plot, width = 12, height = 9, dpi = 300)
python
我们使用Python中的类似ggplot风格的绘图库,比如plotnine,以及用于统计分析的statsmodels或pingouin等库。
但是请注意,Python中没有直接对应于线性混合效应模型(LMM)并能够方便地进行多重比较校正的完整函数链,因此我们需要结合多个步骤。
步骤:
加载必要的库:pandas, numpy, statsmodels, multipy(用于多重检验校正),以及plotnine用于绘图。
准备数据:假设我们有两个组(例如对照组和实验组),并且有多个样本,每个样本有多个微生物物种的表达量。
对于每个物种,使用线性混合效应模型(LMM)分析,其中组别(Group)作为固定效应,个体(Subject)作为随机效应。
提取每个物种的log2FoldChange和p值,然后进行Benjamini-Hochberg校正。
根据阈值(log2FC绝对值≥0.58,且-log10(校正后p值)≥1.301)标记显著物种。
绘制火山图。
注意:由于Python中LMM的实现(如statsmodels的MixedLM)不直接提供p值,我们需要使用其他方法(如似然比检验)来计算p值,或者使用pingouin包的mixed_anova,但这里的数据结构可能不符合ANOVA的要求。
另一种方法是使用线性模型(LM)而不是混合效应模型,如果数据没有明显的重复测量或配对设计,但根据您的描述,似乎使用了线性混合效应模型,并且有随机效应(Subject)。
由于复杂性,我们将使用一个简化方法:对于每个物种,我们使用线性混合效应模型,但为了简单起见,我们假设数据是独立的(即不使用混合效应模型)并使用线性模型,然后进行多重检验校正。
如果您确实需要混合效应模型,我们可以使用statsmodels的MixedLM,但计算p值可能会比较麻烦。
根据您的要求,我们假设数据已经准备好,并且您希望进行混合效应模型分析。但是,由于时间关系,我们这里使用一个简化的线性模型(固定效应模型)来演示流程,并假设数据是独立的。
如果您需要严格的混合效应模型,请参考statsmodels的MixedLM,并使用似然比检验计算p值。
由于代码的复杂性,我们将按照以下步骤进行:
由于我们可能没有Subject的随机效应,我们这里假设每个样本是独立的,使用普通的线性模型(lm)进行差异分析,然后进行多重检验校正。
但是,根据您的要求,我们假设数据是配对的,所以我们需要使用混合效应模型。然而,在Python中实现混合效应模型并提取p值是一个复杂的过程。
考虑到时间和代码的简洁性,我们这里将使用一个替代方法:使用pingouin库的mixed_anova(如果数据是重复测量的)或者使用statsmodels的MixedLM。
我们选择使用statsmodels的MixedLM,并计算每个物种的p值。
步骤:
对于每个物种,构建一个MixedLM模型,固定效应为Group,随机效应为Subject。
由于物种数量可能很多,我们将循环每个物种,拟合模型,并记录结果。
注意:MixedLM不直接提供p值,我们需要使用参数估计的标准误差来计算t统计量和p值。但是,这要求模型收敛并且参数估计可靠。
我们将尝试使用以下步骤:
导入必要的库。
读取数据(这里我们生成模拟数据)。
定义函数对单个物种进行MixedLM分析。
循环所有物种,收集log2FC和p值。
进行多重检验校正(BH方法)。
根据阈值标记显著物种。
绘制火山图。
由于这个过程的复杂性,我们可能需要处理模型不收敛的情况。
我们首先尝试使用statsmodels的MixedLM。但是,请注意,MixedLM的默认方法可能不提供p值,但我们可以通过使用summary()来获取t统计量和p值(如果模型使用最大似然估计或限制性最大似然估计,并且假设参数渐近正态分布)。
实际上,statsmodels的MixedLM的summary方法提供了参数的t检验和p值,但仅针对固定效应。我们可以使用它来获取Group的p值。
因此,我们可以这样设计:
对于每个物种,我们有一个公式:Expression ~ Group,随机效应是Subject。
代码步骤:
注意:我们假设数据是长格式,即每一行是一个样本的一个物种的表达量,列包括:Sample, Subject, Group, Species, Expression。
但是,通常我们的数据是宽格式,即行是物种,列是样本。所以我们需要先转换成长格式。
由于数据量可能很大,转换长格式可能会消耗内存,所以我们逐个物种分析,而不转换整个数据为长格式。
我们假设原始数据:
expr_matrix: 物种(行)x 样本(列)
metadata: 每个样本的信息,包括Group和Subject
我们逐个物种提取表达量,并与metadata合并,然后拟合模型。
但是,这样循环1000个物种,每个物种拟合一个混合效应模型,可能会很慢。
我们将尝试使用向量化操作,但这里无法避免循环。
开始编写代码:
注意:由于模拟数据,我们假设metadata中的Subject列表示个体,且每个个体有多个样本(例如,同一个体处理前后)。
我们使用以下模型:Expression ~ Group + (1|Subject)
在statsmodels的MixedLM中,随机效应通过groups参数指定。
具体步骤:
导入库
创建模拟数据(长格式)
定义分析函数
循环物种
多重检验校正
绘制火山图
由于时间关系,我们使用一个简化的模拟数据生成过程,生成长格式数据。
如果您的数据是宽格式,请先转换为长格式。
我们假设您已经有表达矩阵和样本信息,我们将它们合并成长格式。
由于代码较长,我们逐步实现。
注意:我们使用plotnine绘制火山图,它的风格类似于ggplot2。
但是,如果数据量很大,MixedLM拟合可能较慢,我们可能需要考虑使用更高效的方法。
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# 设置ggplot风格
plt.style.use('ggplot')
# 设置随机种子
np.random.seed(42)
# ==================== 1. 创建模拟数据 ====================
def create_simulated_data(n_species=100, n_subjects=5, n_replicates=2):
"""
创建模拟的微生物表达数据
n_subjects: 个体数量(用于随机效应)
n_replicates: 每个个体的重复数
"""
# 创建微生物名称
species_names = [f'Species_{i:03d}' for i in range(1, n_species + 1)]
# 创建样本信息
n_samples = n_subjects * n_replicates * 2 # 对照组和实验组
subjects = []
groups = []
sample_names = []
for subj in range(1, n_subjects + 1):
for rep in range(1, n_replicates + 1):
# 对照组样本
subjects.append(f'Subject_{subj}')
groups.append('Control')
sample_names.append(f'Control_S{subj}_R{rep}')
# 实验组样本
subjects.append(f'Subject_{subj}')
groups.append('Treatment')
sample_names.append(f'Treatment_S{subj}_R{rep}')
# 创建表达矩阵
expr_matrix = np.zeros((n_species, n_samples))
# 设置一些物种的差异表达
for i in range(n_species):
# 基础表达水平
base_expr = np.random.lognormal(mean=5, sigma=0.5)
# 添加个体间的随机效应
subject_effects = {}
for subj in range(1, n_subjects + 1):
subject_effects[f'Subject_{subj}'] = np.random.normal(0, 0.3)
# 设置差异表达模式
if i < 10: # 前10个物种显著上调
fc = np.random.uniform(1.6, 3.0) # 1.6-3倍变化
elif i < 20: # 11-20个物种显著下调
fc = np.random.uniform(0.33, 0.625) # 0.33-0.625倍变化
else: # 其他物种无显著差异
fc = np.random.uniform(0.8, 1.25) # 轻微变化
# 为每个样本生成表达值
for j, (subject, group) in enumerate(zip(subjects, groups)):
# 基础表达 + 个体效应 + 组效应 + 技术噪声
if group == 'Control':
group_effect = 0
else: # Treatment
group_effect = np.log2(fc) # log2 fold change
expr_value = base_expr + subject_effects[subject] + group_effect + np.random.normal(0, 0.2)
expr_matrix[i, j] = expr_value
# 创建数据框
expr_df = pd.DataFrame(expr_matrix, index=species_names, columns=sample_names)
# 创建元数据
metadata = pd.DataFrame({
'Sample': sample_names,
'Group': groups,
'Subject': subjects
})
return expr_df, metadata
# ==================== 2. 线性混合效应模型分析 ====================
import statsmodels.api as sm
import statsmodels.formula.api as smf
from statsmodels.stats.multitest import multipletests
def fit_lmm_for_species(expr_values, metadata):
"""为单个物种拟合线性混合效应模型"""
# 准备数据
data = metadata.copy()
data['Expression'] = expr_values
# 拟合线性混合效应模型
try:
# 使用Subject作为随机效应,Group作为固定效应
model = smf.mixedlm("Expression ~ Group", data, groups=data["Subject"])
result = model.fit()
# 提取结果
log2fc = result.params.get('Group[T.Treatment]', 0)
p_value = result.pvalues.get('Group[T.Treatment]', 1)
return {
'log2fc': log2fc,
'p_value': p_value,
'converged': result.converged
}
except:
# 如果模型拟合失败,返回空值
return {
'log2fc': np.nan,
'p_value': np.nan,
'converged': False
}
def perform_differential_analysis(expr_df, metadata):
"""对所有物种进行差异表达分析"""
results = []
print("正在进行差异表达分析...")
for i, species in enumerate(expr_df.index):
if (i + 1) % 10 == 0:
print(f" 正在分析第 {i+1}/{len(expr_df)} 个物种...")
expr_values = expr_df.loc[species].values
result = fit_lmm_for_species(expr_values, metadata)
results.append({
'Species': species,
'log2fc': result['log2fc'],
'p_value': result['p_value'],
'converged': result['converged']
})
# 转换为DataFrame
results_df = pd.DataFrame(results)
# 过滤掉模型未收敛的物种
results_df = results_df[results_df['converged']].copy()
# 多重检验校正 (Benjamini-Hochberg)
results_df['adj_p_value'] = multipletests(results_df['p_value'], method='fdr_bh')[1]
# 计算-log10(p值)
results_df['neg_log10_p'] = -np.log10(results_df['adj_p_value'])
return results_df
# ==================== 3. 识别显著差异物种 ====================
def identify_significant_species(results_df, log2fc_threshold=0.58, neg_log10_p_threshold=1.301):
"""根据阈值识别显著差异表达的物种"""
# 创建显著性列
conditions = [
(results_df['log2fc'] >= log2fc_threshold) & (results_df['neg_log10_p'] >= neg_log10_p_threshold),
(results_df['log2fc'] <= -log2fc_threshold) & (results_df['neg_log10_p'] >= neg_log10_p_threshold)
]
choices = ['Up-regulated', 'Down-regulated']
results_df['Significance'] = np.select(conditions, choices, default='Not Significant')
# 统计
n_up = (results_df['Significance'] == 'Up-regulated').sum()
n_down = (results_df['Significance'] == 'Down-regulated').sum()
n_total = n_up + n_down
print("\n" + "="*50)
print("差异表达分析结果统计:")
print(f" 显著上调物种数: {n_up}")
print(f" 显著下调物种数: {n_down}")
print(f" 总显著物种数: {n_total} (占总物种的 {n_total/len(results_df)*100:.1f}%)")
print("="*50)
return results_df
# ==================== 4. 绘制ggplot风格火山图 ====================
def create_ggplot_volcano(results_df, log2fc_threshold=0.58, neg_log10_p_threshold=1.301):
"""创建ggplot风格的火山图"""
# 创建图形
fig, ax = plt.subplots(figsize=(10, 8))
# 定义颜色映射
colors = {
'Up-regulated': '#E64B35', # 红色
'Down-regulated': '#3182bd', # 蓝色
'Not Significant': '#999999' # 灰色
}
# 为每个点分配颜色
point_colors = [colors[sig] for sig in results_df['Significance']]
# 绘制散点
scatter = ax.scatter(
results_df['log2fc'],
results_df['neg_log10_p'],
c=point_colors,
alpha=0.6,
edgecolors='white',
linewidth=0.5,
s=60 # 点的大小
)
# 添加阈值线
ax.axvline(x=log2fc_threshold, color='black', linestyle='--', alpha=0.7, linewidth=1)
ax.axvline(x=-log2fc_threshold, color='black', linestyle='--', alpha=0.7, linewidth=1)
ax.axhline(y=neg_log10_p_threshold, color='black', linestyle='--', alpha=0.7, linewidth=1)
# 添加显著区域标签
ax.text(log2fc_threshold + 0.1, ax.get_ylim()[1] * 0.95,
'Up-regulated', fontsize=10, color='#E64B35', fontweight='bold')
ax.text(-log2fc_threshold - 0.8, ax.get_ylim()[1] * 0.95,
'Down-regulated', fontsize=10, color='#3182bd', fontweight='bold')
# 设置坐标轴标签
ax.set_xlabel(r'$\log_2$(Fold Change)', fontsize=12, fontweight='bold')
ax.set_ylabel(r'$-\log_{10}$(Adjusted P-value)', fontsize=12, fontweight='bold')
# 设置标题
ax.set_title('Differential Microbial Abundance Analysis\n(Linear Mixed Effect Model)',
fontsize=14, fontweight='bold', pad=20)
# 设置网格
ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
# 设置坐标轴范围
x_min = results_df['log2fc'].min() - 0.5
x_max = results_df['log2fc'].max() + 0.5
ax.set_xlim(x_min, x_max)
# 创建图例
from matplotlib.lines import Line2D
legend_elements = [
Line2D([0], [0], marker='o', color='w', markerfacecolor=colors['Up-regulated'],
markersize=10, label=f'Up-regulated (n={sum(results_df["Significance"]=="Up-regulated")})'),
Line2D([0], [0], marker='o', color='w', markerfacecolor=colors['Down-regulated'],
markersize=10, label=f'Down-regulated (n={sum(results_df["Significance"]=="Down-regulated")})'),
Line2D([0], [0], marker='o', color='w', markerfacecolor=colors['Not Significant'],
markersize=10, label=f'Not Significant (n={sum(results_df["Significance"]=="Not Significant")})'),
]
ax.legend(handles=legend_elements, loc='upper right', framealpha=0.9)
# 添加文本说明
stats_text = f"Thresholds:\n|log₂FC| ≥ {log2fc_threshold}\n-log₁₀(P) ≥ {neg_log10_p_threshold}"
ax.text(0.02, 0.98, stats_text, transform=ax.transAxes,
fontsize=9, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
plt.tight_layout()
return fig, ax
# ==================== 5. 获取Top差异物种 ====================
def get_top_differential_species(results_df, top_n=10):
"""获取Top差异表达的物种"""
# 上调物种
up_species = results_df[results_df['Significance'] == 'Up-regulated'].copy()
up_species = up_species.sort_values('adj_p_value')
top_up = up_species.head(top_n)[['Species', 'log2fc', 'adj_p_value']]
# 下调物种
down_species = results_df[results_df['Significance'] == 'Down-regulated'].copy()
down_species = down_species.sort_values('adj_p_value')
top_down = down_species.head(top_n)[['Species', 'log2fc', 'adj_p_value']]
return top_up, top_down
# ==================== 6. 保存结果 ====================
def save_results(results_df, volcano_fig, prefix='microbial_analysis'):
"""保存分析结果"""
# 保存CSV文件
csv_filename = f'{prefix}_results.csv'
results_df.to_csv(csv_filename, index=False)
print(f"分析结果已保存到: {csv_filename}")
# 保存火山图
png_filename = f'{prefix}_volcano_plot.png'
volcano_fig.savefig(png_filename, dpi=300, bbox_inches='tight')
print(f"火山图已保存到: {png_filename}")
# 保存Top物种
top_up, top_down = get_top_differential_species(results_df)
top_up_filename = f'{prefix}_top_upregulated.csv'
top_down_filename = f'{prefix}_top_downregulated.csv'
top_up.to_csv(top_up_filename, index=False)
top_down.to_csv(top_down_filename, index=False)
print(f"Top上调物种已保存到: {top_up_filename}")
print(f"Top下调物种已保存到: {top_down_filename}")
# ==================== 7. 主分析函数 ====================
def main_analysis():
"""主分析流程"""
print("开始微生物差异表达分析...")
print("="*60)
# 1. 创建模拟数据
print("步骤1: 创建模拟数据...")
expr_df, metadata = create_simulated_data(n_species=100, n_subjects=5, n_replicates=2)
print(f" 数据形状: {expr_df.shape[0]}个物种 × {expr_df.shape[1]}个样本")
print(f" 组别分布: Control={sum(metadata['Group']=='Control')}, Treatment={sum(metadata['Group']=='Treatment')}")
# 2. 差异表达分析
print("\n步骤2: 进行线性混合效应模型分析...")
results_df = perform_differential_analysis(expr_df, metadata)
print(f" 成功分析 {len(results_df)} 个物种")
# 3. 识别显著物种
print("\n步骤3: 识别显著差异表达物种...")
results_df = identify_significant_species(results_df)
# 4. 绘制火山图
print("\n步骤4: 绘制火山图...")
fig, ax = create_ggplot_volcano(results_df)
plt.show()
# 5. 显示Top物种
print("\n步骤5: 显示Top差异表达物种...")
top_up, top_down = get_top_differential_species(results_df, top_n=5)
print("\nTop 5 上调物种:")
print(top_up.to_string(index=False))
print("\nTop 5 下调物种:")
print(top_down.to_string(index=False))
# 6. 保存结果
print("\n步骤6: 保存分析结果...")
save_results(results_df, fig)
print("\n" + "="*60)
print("分析完成!")
return results_df, fig
# ==================== 8. 高级可视化选项 ====================
def create_annotated_volcano(results_df, n_annotate=10):
"""创建带标签的火山图"""
fig, ax = create_ggplot_volcano(results_df)
# 获取最显著的物种
significant_df = results_df[results_df['Significance'] != 'Not Significant'].copy()
significant_df = significant_df.sort_values('adj_p_value')
# 标注Top上调物种
top_up = significant_df[significant_df['log2fc'] > 0].head(n_annotate // 2)
for _, row in top_up.iterrows():
ax.annotate(row['Species'],
xy=(row['log2fc'], row['neg_log10_p']),
xytext=(5, 5), textcoords='offset points',
fontsize=8, color='#E64B35',
bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.7, edgecolor='none'))
# 标注Top下调物种
top_down = significant_df[significant_df['log2fc'] < 0].head(n_annotate // 2)
for _, row in top_down.iterrows():
ax.annotate(row['Species'],
xy=(row['log2fc'], row['neg_log10_p']),
xytext=(5, 5), textcoords='offset points',
fontsize=8, color='#3182bd',
bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.7, edgecolor='none'))
ax.set_title('Annotated Volcano Plot\n(Top Significant Species Labeled)',
fontsize=14, fontweight='bold', pad=20)
plt.tight_layout()
return fig, ax
# ==================== 9. 运行分析 ====================
if __name__ == "__main__":
# 运行主分析
results_df, volcano_fig = main_analysis()
# 可选:创建带标签的火山图
print("\n创建带标签的火山图...")
annotated_fig, _ = create_annotated_volcano(results_df, n_annotate=10)
plt.show()
# 保存带标签的火山图
annotated_fig.savefig('microbial_analysis_annotated_volcano.png', dpi=300, bbox_inches='tight')
print("带标签的火山图已保存到: microbial_analysis_annotated_volcano.png")
代码说明和自定义方法:
1. 准备您的真实数据
如果您有自己的数据,需要准备以下两个DataFrame:
python
# 示例:如何准备您的数据
# expr_df: 行是微生物物种,列是样本
expr_df = pd.read_csv('your_expression_data.csv', index_col=0)
# metadata: 样本信息,必须包含'Group'和'Subject'列
metadata = pd.DataFrame({
'Sample': expr_df.columns.tolist(),
'Group': ['Control', 'Control', 'Treatment', 'Treatment', ...], # 您的组别信息
'Subject': ['S1', 'S1', 'S2', 'S2', ...] # 个体信息,用于随机效应
})
2. 使用您的数据进行分析
python
# 直接使用您的数据进行替代
# 步骤1: 使用您的数据
# expr_df, metadata = 加载您的数据
# 步骤2: 差异表达分析
results_df = perform_differential_analysis(expr_df, metadata)
# 步骤3: 识别显著物种
results_df = identify_significant_species(results_df)
# 步骤4: 绘制火山图
fig, ax = create_ggplot_volcano(results_df)
plt.show()
# 步骤5: 保存结果
save_results(results_df, fig, prefix='your_analysis')
3. 参数调整
您可以根据需要调整阈值:
python
# 调整显著性阈值
log2fc_threshold = 0.58 # 对应1.5倍变化
neg_log10_p_threshold = 1.301 # 对应p=0.05
# 使用自定义阈值
results_df = identify_significant_species(
results_df,
log2fc_threshold=0.58,
neg_log10_p_threshold=1.301
)
fig, ax = create_ggplot_volcano(
results_df,
log2fc_threshold=0.58,
neg_log10_p_threshold=1.301
)
4. 安装依赖包
bash
pip install numpy pandas matplotlib seaborn scipy statsmodels
5. 输出说明
代码会生成以下文件:
microbial_analysis_results.csv - 完整分析结果
microbial_analysis_volcano_plot.png - 火山图
microbial_analysis_top_upregulated.csv - Top上调物种
microbial_analysis_top_downregulated.csv - Top下调物种
microbial_analysis_annotated_volcano.png - 带标签的火山图
6. 统计分析细节
线性混合效应模型:使用statsmodels的mixedlm函数,以Subject作为随机效应
多重检验校正:使用Benjamini-Hochberg (FDR)方法
显著性标准:
|log2FC| ≥ 0.58 (对应1.5倍变化)
-log10(p_adj) ≥ 1.301 (对应p_adj ≤ 0.05)
7. 可视化特点
火山图具有以下ggplot风格特征:
灰色网格背景
清晰的颜色编码(红=上调,蓝=下调,灰=不显著)
虚线表示显著性阈值
图例和统计信息框
合适的点大小和透明度