seurat-AverageExpression()源碼解析

前段時間宏蛉,一個單細胞分析同行提問阵具,發(fā)現(xiàn)AverageExpression()和通過aggregate計算得到的基因表達均值,兩者的結(jié)果是不一樣的噪叙,疑惑問題出在哪里。

這個問題我們或多或少都有過疑問霉翔,自己手動計算基因在每個cluster的表達均值睁蕾,繪制heatmap熱圖;跟AverageExpression()處理后繪制DoHeatmap會有差異债朵。網(wǎng)上網(wǎng)友也提過類似的問題子眶,也不知其解。決心看下AverageExpression()的源碼序芦。

image.png

一臭杰、測試AverageExpression()函數(shù)

library(Seurat)

table(Idents(seurat_obj_Bcell))
# B0   B1   B2   B3   B4   B5   B6   B7   B8
# 2573 2115 2093 1536 1355 1344 1005   92   74

cluster.averages <- AverageExpression(seurat_obj_Bcell, slot = 'data', return.seurat = TRUE)
# assays有三種模式:RNA, SCT, integrated
# -----------------------------------
dim(cluster.averages@assays$RNA@data)
# [1] 21972     9
head(cluster.averages@assays$RNA@data)
#             B0          B1          B2          B3          B4
# AL627309.1 0.004422988 0.003237424 0.001007899 0.004426136 0.001509931
# AL627309.5 0.015784045 0.026414631 0.032074415 0.023697387 0.006019230
# AP006222.2 0.000000000 0.000000000 0.000000000 0.000000000 0.000000000
# LINC01409  0.008551480 0.009181925 0.019694557 0.019493293 0.025692960
# FAM87B     0.001375785 0.002404144 0.001298187 0.001751876 0.000000000
# LINC01128  0.138134556 0.118331411 0.149791178 0.063821427 0.072906583
#              B5          B6         B7        B8
# AL627309.1 0.001075568 0.003654187 0.00000000 0.0000000
# AL627309.5 0.011408478 0.029097945 0.00000000 0.0000000
# AP006222.2 0.000000000 0.000000000 0.00000000 0.0000000
# LINC01409  0.013514985 0.003152831 0.06921692 0.0000000
# FAM87B     0.000000000 0.000000000 0.00000000 0.0000000
# LINC01128  0.054664322 0.153354677 0.02744229 0.1327957
write.csv(cluster.averages@assays$RNA@data, file = "cluster.averages_assays_RNA_data.csv")

#-----------------------------------
dim(cluster.averages@assays$SCT@data)
# [1] 16717     9

head(cluster.averages@assays$SCT@data)
# B0           B1          B2           B3           B4
# AL627309.1 0.0019413712 0.0009451797 0.000477669 0.0019512201 0.0007377352
# AL627309.5 0.0061991675 0.0117510165 0.011873804 0.0122939109 0.0029476808
# LINC01409  0.0031043874 0.0047169899 0.007141187 0.0116506172 0.0110092855
# FAM87B     0.0003885759 0.0009451797 0.000477669 0.0006508298 0.0000000000
# LINC01128  0.0551916324 0.0596507087 0.058458200 0.0351811146 0.0376583238
# LINC00115  0.0177200322 0.0150167064 0.019868203 0.0225307245 0.0204535984
# B5           B6         B7         B8
# AL627309.1 0.000743771 0.0009945302 0.00000000 0.00000000
# AL627309.5 0.006674107 0.0089153637 0.00000000 0.00000000
# LINC01409  0.005934736 0.0009945302 0.03208831 0.00000000
# FAM87B     0.000000000 0.0000000000 0.00000000 0.00000000
# LINC01128  0.023530497 0.0523375251 0.02150621 0.07796154
# LINC00115  0.011834458 0.0187289851 0.02150621 0.05264373
write.csv(cluster.averages@assays$SCT@data, file = "cluster.averages_assays_SCT_data.csv")

#-----------------------------------
dim(cluster.averages@assays$integrated@data)
# [1] 2000    9

head(cluster.averages@assays$integrated@data)
# B0         B1          B2           B3          B4
# LYZ      0.005967343 0.01146686 0.010868760 6.508555e-03 0.012815378
# S100A9   0.009264568 0.03849551 0.005806276 1.388812e-05 0.018757573
# S100A8   0.003377878 0.02276050 0.010335783 1.019810e-03 0.009345999
# IGKV3-15 1.374223462 0.96204743 1.358024467 1.397423e+00 1.096290152
# IGKV3-20 1.529314027 0.96532312 1.316039517 1.776763e+00 1.215281468
# IGKV3-11 1.290120895 0.57287190 1.234111953 9.728570e-01 1.339886382
# B5          B6           B7          B8
# LYZ      0.007843731 0.009692197 -0.005189707 0.002062152
# S100A9   0.006850602 0.008352554 -0.005198381 0.002006344
# S100A8   0.001704852 0.003578382 -0.004440559 0.015471037
# IGKV3-15 1.402736193 0.711017421  0.445023238 0.727528712
# IGKV3-20 1.456745150 0.853644113  1.736057719 1.514083481
# IGKV3-11 1.137085218 0.554071284  1.533300791 0.738172971
write.csv(cluster.averages@assays$integrated@data, file = "cluster.averages_assays_integrated_data.csv")

二、測試AverageExpression()源碼

從GitHub下載seurat源碼谚中,找到AverageExpression()函數(shù)的代碼渴杆,把該函數(shù)的調(diào)用函數(shù)代碼也找齊,運用自己的數(shù)據(jù)集宪塔,一步步執(zhí)行磁奖。我們的目的是計算基因在每個cluster(B0-B8)的平均表達值。

step1: 調(diào)用AverageExpression()

AverageExpression()調(diào)用內(nèi)部函數(shù)()蝌麸,把自身的參數(shù)傳遞給它点寥。

# step1: AverageExpression函數(shù)是傳遞變量給PseudobulkExpression
object <- seurat_obj_Bcell
pb.method = 'average'
assays = NULL
features = NULL
return.seurat = TRUE
group.by = 'ident'
add.ident = NULL
slot = 'data'
verbose = TRUE

PseudobulkExpression(
object = object,
pb.method = 'average',
assays = assays,
features = features,
return.seurat = return.seurat,
group.by = group.by,
add.ident = add.ident,
slot = slot,
verbose = verbose,
...
)
step2: PseudobulkExpression()

查看PseudobulkExpression()函數(shù),pb.method參數(shù)只能選'average' 或者'aggregate'方法来吩;aggregate方法先不考慮敢辩,只考慮average方法蔽莱。該函數(shù)代碼較多,只提取重要代碼和參數(shù)戚长。

操作1:獲取細胞的cluster矩陣信息data盗冷;即獲取每個細胞對應的cluster信息,‘sample1-AAACGGGCACCTTGTC’細胞的cluster為B4同廉。data為12187*1的矩陣仪糖,測試數(shù)據(jù)共12187個細胞。

assays
# [1] "RNA"        "integrated" "SCT"
data <- FetchData(object = object, vars = rev(x = group.by))
dim(data)
# [1] 12187     1
head(data)
# ident
# sample1-AAACGGGCACCTTGTC    B4
# sample1-AAACGGGGTAACGACG    B2
# sample1-AAAGATGGTACCGCTG    B6
# sample1-AAAGATGGTGACGCCT    B4
# sample1-AAAGTAGAGTATCGAA    B2
# sample1-AAAGTAGGTCTCATCC    B3
# data是每個細胞對應的cluster信息(B1-B8)迫肖,共12187個細胞锅劝;
# 去除為na的數(shù)據(jù);該案例無NA數(shù)據(jù)蟆湖;
data <- data[which(rowSums(x = is.na(x = data)) == 0), , drop = F]
dim(data)
# [1] 12187     1

操作2:cluster矩陣數(shù)據(jù)data轉(zhuǎn)成稀疏矩陣category.matrix故爵,列為cluster的列名(B0, B1, B2...B8),共9列。data將由12187*1的矩陣轉(zhuǎn)成12187*9的矩陣隅津。

library(Matrix)
category.matrix <- sparse.model.matrix(object = as.formula(
  object = paste0(
    '~0+',
    paste0(
      "data[,",
      1:length(x = group.by),
      "]",
      collapse = ":"
    )
  )
))
dim(category.matrix)
# [1] 12187     9
head(category.matrix)
# 6 x 9 sparse Matrix of class "dgCMatrix"
# data[, 1]B0 data[, 1]B1 data[, 1]B2 data[, 1]B3 data[, 1]B4 data[, 1]B5
# 1           .           .           .           .           1           .
# 2           .           .           1           .           .           .
# 3           .           .           .           .           .           .
# 4           .           .           .           .           1           .
# 5           .           .           1           .           .           .
# 6           .           .           .           1           .           .
# data[, 1]B6 data[, 1]B7 data[, 1]B8
# 1           .           .           .
# 2           .           .           .
# 3           1           .           .
# 4           .           .           .
# 5           .           .           .
# 6           .           .           .
# category.matrix是轉(zhuǎn)成一個稀疏矩陣诬垂;
# head(data)
# ident
# sample1-AAACGGGCACCTTGTC    B4
# sample1-AAACGGGGTAACGACG    B2
# sample1-AAAGATGGTACCGCTG    B6
# sample1-AAAGATGGTGACGCCT    B4
# sample1-AAAGTAGAGTATCGAA    B2
# sample1-AAAGTAGGTCTCATCC    B3
# 將12187*1的矩陣轉(zhuǎn)成12187*9的矩陣,
# 如sample1-AAACGGGCACCTTGTC為B4伦仍,即data[, 1]B4為1结窘,其余為0;
# 如sample1-AAACGGGCACCTTGTC為B2充蓝,即data[, 1]B2為1隧枫,其余為0;

#  對每列(B0-B8)求和谓苟,統(tǒng)計屬于該cluster的細胞總數(shù)
colsums <- colSums(x = category.matrix)
colsums
# data[, 1]B0 data[, 1]B1 data[, 1]B2 data[, 1]B3 data[, 1]B4 data[, 1]B5
# 2573        2115        2093        1536        1355        1344
# data[, 1]B6 data[, 1]B7 data[, 1]B8
# 1005          92          74
table(data$ident)
# B0   B1   B2   B3   B4   B5   B6   B7   B8
# 2573 2115 2093 1536 1355 1344 1005   92   74
# 數(shù)據(jù)過濾悠垛,如果某cluster沒有任一細胞,剔除該cluster
category.matrix <- category.matrix[, colsums > 0]
colsums <- colsums[colsums > 0]

操作3:稀疏矩陣category.matrix進行average操作娜谊,計算每個細胞在cluster的占比。例如斤讥,屬于B0這個cluster的細胞群有2573個細胞纱皆,那每個細胞的占比是1/2573=0.00038865。
想一想為什么要這么操作芭商?

if (pb.method == 'average') {
  category.matrix <- sweep(
    x = category.matrix,
    MARGIN = 2,
    STATS = colsums,
    FUN = "/")
}
1/colsums
# data[, 1]B0  data[, 1]B1  data[, 1]B2  data[, 1]B3  data[, 1]B4  data[, 1]B5
# 0.0003886514 0.0004728132 0.0004777831 0.0006510417 0.0007380074 0.0007440476
# data[, 1]B6  data[, 1]B7  data[, 1]B8
# 0.0009950249 0.0108695652 0.0135135135

操作4:默認slot = "data"派草,獲取歸一化之后每個細胞的基因表達矩陣,矩陣大小為21972*12187铛楣。

# 如果為"RNA"近迁,
data.use <- GetAssayData(
  object = object,
  assay = "RNA",
  slot = "data"
)
# data.use為一個21972*12187的基因表達矩陣,21972為基因個數(shù)簸州,12187為細胞個數(shù)鉴竭;slot = "data"表明是歸一化之后的基因表達矩陣歧譬;
dim(data.use)
# [1] 21972 12187
data.use[1:20,1:20]

操作5:如slot = "data",基因表達矩陣需進行expm1處理搏存。這個很關(guān)鍵瑰步。

if (slot[i] == 'data') {
  data.use <- expm1(x = data.use)
}
data.use[1:20,1:20]

問題1:為什么要進行expm1處理?

數(shù)據(jù)均一化的時候進行了log1p處理璧眠, expm1和log1p是相對的缩焦;

操作6:計算最終的每個細胞的基因在cluster的平均表達量;即基因表達矩陣data.use和細胞在cluster的占比矩陣category.matrix乘積责静。
兩個矩陣的乘積袁滥,要求第一個矩陣的行數(shù)data.use與第二個矩陣category.matrix列數(shù)相等。

data.return[[i]] <- as.matrix(x = (data.use %*% category.matrix))
image.png

image.png

問題2:為什么通過源碼推算的基因平均表達量矩陣和AverageExpression()計算的不完全一致灾螃?

猜測是category.matrix等矩陣小數(shù)點保留位數(shù)不一致题翻,導致后面幾位數(shù)不一致;


image.png

問題3:pb.method有'average' 或者'aggregate'方式睦焕,是否可以切換到aggregate方式:
否藐握,AverageExpression()沒有外置參數(shù),調(diào)用PseudobulkExpression()函數(shù)時垃喊,已設(shè)置為pb.method = 'average'猾普,不能切換成aggregate

image.png

問題4:回歸到最初的提問,AverageExpression()函數(shù)本谜,計算出來的結(jié)果與aggregate得到的均值不一樣初家?
嘗試進行expm1()處理;

group.by <- "recluster" 
mat <- as.data.frame(t(as.matrix(GetAssayData(object, assay = "RNA", slot = "data")))) 
mat <- expm1(x = mat) 
mat <- aggregate(mat, by=list(object@meta.data[[group.by]]), FUN="mean") 
rownames(mat) <- mat$Group.1 
mat <- t(mat[,-1]) 
head(mat)

比較expm1()處理后的aggregate得到的均值和AverageExpression()計算的均值乌助,是一樣的溜在;


image.png

通過上面的測試,AverageExpression()函數(shù)他托,計算出來的結(jié)果與aggregate得到的均值不一樣的原因是AverageExpression()對歸一化的表達矩陣進行了expm1()處理掖肋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赏参,隨后出現(xiàn)的幾起案子志笼,更是在濱河造成了極大的恐慌,老刑警劉巖把篓,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纫溃,死亡現(xiàn)場離奇詭異,居然都是意外死亡韧掩,警方通過查閱死者的電腦和手機紊浩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坊谁,你說我怎么就攤上這事费彼。” “怎么了呜袁?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵敌买,是天一觀的道長。 經(jīng)常有香客問我阶界,道長虹钮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任膘融,我火速辦了婚禮芙粱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氧映。我一直安慰自己春畔,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布岛都。 她就那樣靜靜地躺著律姨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臼疫。 梳的紋絲不亂的頭發(fā)上择份,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音烫堤,去河邊找鬼荣赶。 笑死,一個胖子當著我的面吹牛鸽斟,可吹牛的內(nèi)容都是我干的拔创。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼富蓄,長吁一口氣:“原來是場噩夢啊……” “哼剩燥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起立倍,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤躏吊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后帐萎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡胜卤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年疆导,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葛躏。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡澈段,死狀恐怖悠菜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情败富,我是刑警寧澤悔醋,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站兽叮,受9級特大地震影響芬骄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹦聪,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一账阻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泽本,春花似錦淘太、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赌莺,卻和暖如春冰抢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雄嚣。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工晒屎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缓升。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓鼓鲁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親港谊。 傳聞我的和親對象是個殘疾皇子骇吭,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容