我们的生物信息学马拉松授课的一个最重要的环节就是表达量矩阵数据处理,其中让大家练习最频繁的就是传统的表达量芯片的差异分析和富集分析啦。
其中一个小伙伴在学员交流群里面晒图就很有意思,看起来就很明显的两个样品分组可能是有误,或者说是两个离群点。文献是:《Changing human hair fibre colour and shape from the follicle》,对应的数据集是GSE193983,这个实验设计主要是为了探究不同毛发形状(直发与卷发)在毛囊中的基因表达差异。研究团队采用了微阵列技术来比较直发和极卷发之间的基因表达水平。总共是11位成年男性捐赠者 ,从每位志愿者那里收集了50个毛囊:6位极卷发捐赠者和5位直发捐赠者,均为非秃顶成年男性,年龄在19至40岁之间。
并通过Affymetrix GeneChip HuGene 1.0 ST Arrays进行分析,以识别在这两种毛发类型中表达不同的基因。实验结果显示,有85个基因在直发和卷发毛囊中表达有显著差异,其中68个基因在极卷发毛囊中表达更高,而17个基因在直发毛囊中表达更高。这项研究有助于我们理解毛发形状的遗传基础,并可能为未来的治疗和美容产品开发提供科学依据。如下所示:

6位极卷发捐赠者和5位直发捐赠者
很简单的读取GSE193983的表达量矩阵和分组信息后进行质量控制和差异分析,目前简单的差异分析流程,基本上转录组测序技术和芯片技术拿到的表达量矩阵后续分析大同小异
我对这个数据集进行了视频演示:
如下所示:

质量控制和差异分析
很明显的可以看到确实是有两个样品分组可能是有误,或者说是两个离群点。我们可以在上面的热图里面标记出来样品的id,然后简单的过滤掉这两个样品,如下所示:
load( file = '../GSE193983/step1_output.Rdata')
# ~~~~~ heatmap~~~~~
cg=names(tail(sort(apply(dat,1,sd)),1000))#apply按行('1'是按行取,'2'是按列取)取每一行的方差,从小到大排序,取最大的1000个
library(pheatmap)
pheatmap(dat[cg,],show_colnames =F,show_rownames = F) #对那些提取出来的1000个基因所在的每一行取出,组合起来为一个新的表达矩阵
n=t(scale(t(dat[cg,]))) # 'scale'可以对log-ratio数值进行归一化
n[n>2]=2
n[n< -2]= -2
head(n)
pheatmap(n,show_colnames =F,show_rownames = F)
ac=data.frame(Group=group_list)
rownames(ac)=colnames(n)
pheatmap(n,show_colnames =T,show_rownames = F,
annotation_col=ac)
colnames(dat)
boxplot(dat['GSTM4',]~group_list)
kp=!colnames(dat) %in% c("GSM5825026" ,"GSM5825027")
dat=dat[,kp]
group_list=group_list[kp]
table(group_list)
save(gse_number,dat,group_list,
# probe_exp,probe_info,pd,
file = 'step1_output.Rdata')
接下来就可以进行同样的质量控制和差异分析,如下所示:

同样的质量控制和差异分析
如果仅仅是看这两次质量控制和差异分析,会认为剔除了两个样品前后的差异分析结果区别很大,因为很明显哪怕是提高了变化倍数这个阈值后,定位到的统计学显著的上下调基因数量都多了很多。
但是,一切阈值都是纸老虎,我们不能是仅仅是绝对数量,而应该是看相对功能:
rm(list = ls())
library(data.table)
raw_deg = fread(file = './GSE193983//DEG.csv',data.table = F)
remove_deg = fread(file = './remove//DEG.csv',data.table = F)
colnames(remove_deg)
rownames(raw_deg)=raw_deg$name
rownames(remove_deg)=remove_deg$name
ids=intersect(rownames(raw_deg),
rownames(remove_deg))
df= data.frame(
raw_deg = raw_deg[ids,'logFC'],
remove_deg = remove_deg[ids,'logFC']
)
library(ggpubr)
ggscatter(df, x = "raw_deg", y = "remove_deg",
color = "black", shape = 21, size = 3, # Points color, shape and size
add = "reg.line", # Add regressin line
add.params = list(color = "blue", fill = "lightgray"), # Customize reg. line
conf.int = TRUE, # Add confidence interval
cor.coef = TRUE, # Add correlation coefficient. see ?stat_cor
cor.coeff.args = list(method = "pearson", label.sep = "\n")
)
df= data.frame(
raw_deg = raw_deg[ids,'g'],
remove_deg = remove_deg[ids,'g']
)
table(df)
gplots::balloonplot(table(df))
首先呢,确实是可以看到剔除了两个样品后定位到的统计学显著的上下调基因数量都多了很多,但实际上并没有改变很多基因的趋势,两次差异分析计算的变化倍数的相关性非常高!

剔除了两个样品后定位到的统计学显著的上下调基因数量都多了很多
而且,我们真正看的应该是功能情况:
symbols_list = split(ids,paste(df[,1],df[,2]))
library(clusterProfiler)
library(org.Hs.eg.db)
library(ReactomePA)
library(ggplot2)
library(stringr)
# 首先全部的symbol 需要转为 entrezID
gcSample = lapply(symbols_list, function(y){
y=as.character(na.omit(AnnotationDbi::select(org.Hs.eg.db,
keys = y,
columns = 'ENTREZID',
keytype = 'SYMBOL')[,2])
)
y
})
gcSample
pro='test'
# 第1个注释是 KEGG
xx <- compareCluster(gcSample, fun="enrichKEGG",
organism="hsa", pvalueCutoff=0.3)
dotplot(xx) + theme(axis.text.x=element_text(angle=45,hjust = 1)) +
scale_y_discrete(labels=function(x) str_wrap(x, width=50))
ggsave(paste0(pro,'_kegg.pdf'),width = 10,height = 8)
如果作者最后定位的就是下面的通路,其实剔除了两个样品前后的差异分析结果没什么区别了,算是"不幸中的万幸"?

我们做一个两分组的表达量矩阵差异分析,然后发现其中一个组里面的2个样品可能被标记错误,然后我们剔除了这两个样品之后,再一次做差异分析。这两次差异分析的结果可以如何比较,一致性如何!
在进行两分组的表达量矩阵差异分析时,如果发现其中一个组的两个样品可能被标记错误,并在剔除这两个样品后再次进行差异分析,比较这两次分析结果的一致性,可以采取以下步骤:
通过这些步骤,可以系统地比较两次差异分析的结果,并评估它们的一致性。重要的是要确保分析方法的一致性,并考虑样本剔除对结果的潜在影响。