STUtility || 空間轉錄組多樣本分析框架(一)

應用空間統計學分析空間表達數據
空間信息在空間轉錄組中的運用
Giotto|| 空間表達數據分析工具箱
SPOTlight || 用NMF解卷積空間表達數據
空間轉錄組教程|| stLearn :空間軌跡推斷
Seurat 新版教程:分析空間轉錄組數據
單細胞轉錄組數據分析|| scanpy教程:空間轉錄組數據分析
10X Visium:空間轉錄組樣本製備到數據分析
定量免疫浸潤在單細胞研究中的應用

空間轉錄組學是一種通過結合基因表達數據和顯微圖像數據來可視化和定量分析組織切片中轉錄組定量的方法。在前幾期的文章中,我們主要講述的是單個空間轉錄組樣本的分析,今天要講的是如何分析多張切片。處理過單細胞轉錄組的同學不會忘記,多樣本分析和單樣本是多麼的不同。在空間這裏關鍵的一點是多個圖像的處理(對齊)。STUtility的開發者Ludvig Larsson和Joseph Bergenstrahle是Joakim Lundebergs教授團隊的博士生,該團隊是空間轉錄組技術(ST)的最初發明者,後來被10X Genomics收購。團隊工作室位於瑞典斯德哥爾摩的生命科學實驗室(SciLifeLab)。爲了給大家一個宏觀的視角,來看看人家的實驗室是怎樣的:

開發STUtility的目標是分析多個空轉切片。與所有生物數據一樣,使用多個樣本增加了分析的力量,同時減少不確定性的影響。這裏,我們將展示如何輸入多個切片,以期查看諸如批次效應和缺失數據等情況以及應用已有的方法糾正這些情況,獲得數據的整體印象,並以各種方式進行更深入的分析。

我們已經廣泛嘗試了處理ST(空轉)數據的不同方法和工作流程。雖然條條大路通羅馬,但截止到撰寫本文時,我們發現Seurat方法最適合這種類型的數據。Seurat是一個專爲單細胞RNAseq數據設計的R包。顯然,這偏離了ST技術目前產生的數據,因爲陣列上的分辨率意味着每個捕獲點由源自多個細胞的轉錄本組成。然而,ST數據的特徵在很大程度上與scRNAseq相似(我們會在下一篇文章中用數據說明之)。注意,STUtility包依賴Seurat v3.0或更高版本。

本節主要內容有:

  • 多切片數據讀取
  • 空轉數據質控
  • 圖像對齊(旋轉,切割)
  • 繪製感興趣的區域
  • 空間數據3D可視化

我們載入R包和10X的空轉數據:

library(STutility)
library(SeuratData)
library(Seurat)
library(tidyverse)
df <- data.frame(samples  =c("E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_1_spatial\\filtered_feature_bc_matrix.h5",
                               'E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_2\\filtered_feature_bc_matrix.h5'),
                   spotfiles = c("E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_1_spatial\\spatial\\tissue_positions_list.csv",
                                 'E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_2\\spatial\\tissue_positions_list.csv'),
                   imgs  = c("E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_1_spatial\\spatial\\tissue_hires_image.png",
                             'E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_2\\spatial\\tissue_hires_image.png'),
                   json = c("E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_1_spatial\\spatial\\scalefactors_json.json",
                            'E:\\learnscanpy\\data\\V1_Breast_Cancer_Block_A_Section_2\\spatial\\scalefactors_json.json'))
df

和Seurat的思想很像,其實很多表達量分析的函數也是直接借用Seurat的。

?InputFromTable # 依然會過濾一些
se <- InputFromTable(infotable = df, 
                     min.gene.count = 100, 
                     min.gene.spots = 5,
                     min.spot.count = 500,
                     platform =  "Visium")

查看數據對象結構,儼然一個Seurat對象:

se
An object of class Seurat 
16273 features across 7798 samples within 1 assay 
Active assay: RNA (16273 features, 0 variable features)
> head([email protected])
                        orig.ident nCount_RNA nFeature_RNA id  labels
CAGGATCCGCCCGACC-1_1 SeuratProject       5406         2555  1 Default
CACGATTGGTCGTTAA-1_1 SeuratProject       5740         2622  2 Default
GGTTGTATCGTGAAAT-1_1 SeuratProject       7272         3165  3 Default
TCTTATGGGTAGTACC-1_1 SeuratProject       4208         2081  4 Default
TACAAGCTGTTCACTG-1_1 SeuratProject       8111         3276  5 Default
GTATCTTGTTGCTCAC-1_1 SeuratProject       7407         3118  6 Default
head(se@[email protected])
                      x y adj_x adj_y  pixel_x  pixel_y sample original_x original_y
CAGGATCCGCCCGACC-1_1 49 1    49     1 842.6568 353.3828      1   842.6568   353.3828
CACGATTGGTCGTTAA-1_1 50 0    50     0 853.9604 333.8284      1   853.9604   333.8284
GGTTGTATCGTGAAAT-1_1 51 1    51     1 865.1815 353.4653      1   865.1815   353.4653
TCTTATGGGTAGTACC-1_1 52 0    52     0 876.4851 333.9109      1   876.4851   333.9109
TACAAGCTGTTCACTG-1_1 53 1    53     1 887.7063 353.4653      1   887.7063   353.4653
GTATCTTGTTGCTCAC-1_1 54 0    54     0 899.0099 333.9109      1   899.0099   333.9109

查看數據分佈:

library(foreach)
library(parallel)
plt <- function(i){
    print(ggplot() +
              geom_histogram(data = se[[]], aes(get(i)), fill = "red", alpha = 0.7, color = "gray", bins = 50) +
              Seurat::DarkTheme() +
              ggtitle(paste0("Total", i , "  per spots")))
    
}

pl = list()
pl1 <- foreach::foreach(i = c('nFeature_RNA','nCount_RNA'),.packages = c("Seurat","ggplot2"))  %dopar% plt(i)

gene_attr <- data.frame(nUMI = Matrix::rowSums(se@assays$RNA@counts), 
                        nSpots = Matrix::rowSums(se@assays$RNA@counts > 0))

plt2 <- function(i){
    print(ggplot() +
              geom_histogram(data = gene_attr, aes(get(i)), fill = "red", alpha = 0.7, color = "gray", bins = 50) +
              Seurat::DarkTheme() +
              ggtitle(paste0("Total", i , "  per gene")))
    
}

pl2 <- foreach::foreach(i = c('nUMI','nSpots'),.packages = c("Seurat","ggplot2"))  %dopar% plt2(i)


cowplot::plot_grid(plotlist = c(pl1,pl2))

線粒體核糖體基因等質量指標:

# Collect all genes coded on the mitochondrial genome
mt.genes <- grep(pattern = "^MT-", x = rownames(se), value = TRUE)
se$percent.mito <- (Matrix::colSums(se@assays$RNA@counts[mt.genes, ])/Matrix::colSums(se@assays$RNA@counts))*100

# Collect all genes coding for ribosomal proteins
rp.genes <- grep(pattern = "^RPL|^RPS", x = rownames(se), value = TRUE)
se$percent.ribo <- (Matrix::colSums(se@assays$RNA@counts[rp.genes, ])/Matrix::colSums(se@assays$RNA@counts))*100

 head([email protected])
                        orig.ident nCount_RNA nFeature_RNA id  labels percent.mito percent.ribo
CAGGATCCGCCCGACC-1_1 SeuratProject       5406         2555  1 Default     9.507954    10.229375
CACGATTGGTCGTTAA-1_1 SeuratProject       5740         2622  2 Default     8.832753    11.672474
GGTTGTATCGTGAAAT-1_1 SeuratProject       7272         3165  3 Default     8.044554    10.588559
TCTTATGGGTAGTACC-1_1 SeuratProject       4208         2081  4 Default     8.911597    11.644487
TACAAGCTGTTCACTG-1_1 SeuratProject       8111         3276  5 Default    10.393293     9.517939
GTATCTTGTTGCTCAC-1_1 SeuratProject       7407         3118  6 Default     9.693533    10.476576

p1 <- map(c('percent.mito','percent.ribo') ,function(x) ST.FeaturePlot(se, features = x, dark.theme = TRUE, cols = c("dark blue", "cyan", "yellow", "red", "dark red")))
cowplot::plot_grid(plotlist = c(p1))


這裏來了一個分析空轉數據值得思考的問題:在空間上去掉一個Sopt意味着什麼?

# Keep spots with more than 500 unique genes and less than 10% mitochondrial content
se.subset <- SubsetSTData(se, expression = nFeature_RNA > 500 & nCount_RNA < 20000 & percent.mito < 10)
cat("Spots removed: ", ncol(se) - ncol(se.subset), "\n")

Spots removed:  3617 

p2 <- ST.FeaturePlot(se.subset, features = c("nFeature_RNA",'nCount_RNA','percent.mito','percent.ribo'), dark.theme = T,
               cols = c("black", "blue", "cyan", "yellow", "red", "darkred"))+ ggtitle(" subset ") 

p3  <- ST.FeaturePlot(se, features = c("nFeature_RNA",'nCount_RNA','percent.mito','percent.ribo'), dark.theme = T,
                     cols = c("black", "blue", "cyan", "yellow", "red", "darkred")) + ggtitle("no subset ")


cowplot::plot_grid(p2,p3)

這上面可能看的不清楚,我用PPT組合了另一個項目的(也是10X的官方數據),subset前後的圖像,可以感受到數量與質量的矛盾:考慮到一個切片3000多個spot就要好幾萬,這樣貿然去掉是不是不太好? 所以在我們的分析中需要慎重考慮,另外需要注意的是我們的spot 並不是一個細胞。

其實不好的亞羣,說到底還是該亞羣的基因不make sense。如果你有很好的理由去除掉某種類型的基因,這也很容易做到。例如,您可能希望在數據集中只保留蛋白質編碼基因。這裏我們作爲演示,做一下,我們的宗旨還是不先入爲主地去除基因和細胞。

ensids <- read.table(file = list.files(system.file("extdata", package = "STutility"), full.names = T, pattern = "mouse_genes"), header = T, sep = "\t", stringsAsFactors = F)
 # 注意這裏是小鼠的不是人,這裏我們演示瞭如何使用預定義的covernersion表將Seurat對象劃分爲子集,只包含蛋白質編碼基因,但是您也可以從其他地方獲得這一信息,例如bioMart。
head(ensids)
               gene_id            gene_type     gene_name
1 ENSMUSG00000102693.1                  TEC 4933401J01Rik
2 ENSMUSG00000064842.1                snRNA       Gm26206
3 ENSMUSG00000051951.5       protein_coding          Xkr4
4 ENSMUSG00000102851.1 processed_pseudogene       Gm18956
5 ENSMUSG00000103377.1                  TEC       Gm37180
6 ENSMUSG00000104017.1                  TEC       Gm37363

# Print available biotypes
unique(ensids$gene_type)
keep.genes <- subset(ensids, gene_type %in% "protein_coding")$gene_name
keep.genes
# Subset Seurat object
se.subset <- se[intersect(rownames(se), keep.genes), ]

cat("Number of genes removed : ", nrow(se) - nrow(se.subset), "\n")
圖像處理

下面我認爲是STUtility 的核心了:方便地操縱我們的圖像。在看到STUtility之前,我們可能不知道原來圖像還可以這樣處理。創建Seurat對象之後,我們就可以從infoTable中提供的文件路徑中加載H&E映像。LoadImages()函數允許您將圖像加載到Seurat對象中,並自動保存每個圖像的壓縮版本,您可以用於繪圖。

載入圖像

se <- LoadImages(se, time.resolve = F, verbose = T)
ImagePlot(se, method = "raster", darken = TRUE, type = "raw")

我們可以畫一個marker看看:

FeatureOverlay(se, 
                sampleids = 1:2,
               features = "CD27", 
               pt.size = 1.5,
               cols = c("dark blue", "cyan", "yellow", "red", "dark red"), 
               dark.theme = T, 
               type = "raw",ncols.samples = 2)

出於可視化目的,另一個有用的特性是屏蔽HE圖像的背景。函數MaskImages()可以移除背景,目前對於邊界清晰的組織來說效果很好。掩蔽是一個重要的問題,由於染色,切片等原因,在某些組織類型中它可能會失敗。如果發生這種情況,你可以嘗試修改MaskImages()中的參數,或者創建你自己的屏蔽函數,看看你是否可以獲得更好的結果。

所提供的掩蔽(masking)算法使用SLIC方法,屬於簡單的線性迭代聚類(更多信息,參見https://www.r-bloggers.com/superpixels-in-imager/)。該算法根據圖像平面上的顏色相似度和接近度對像素進行聚類,生成超像素。在對HE圖像運行SLIC方法之前,我們注意到一些預處理可以顯著改善掩蔽。

除了剛纔看到的指未經任何修改的原始圖像( method = "raster"),還有以下四種:

se <- MaskImages(object = se)
ImagePlot(se, ncols = 2, method = "raster", type = "masked", darken = T) # Masked image
ImagePlot(se, ncols = 2, method = "raster", type = "masked.masks") # Mask

自動對齊方法(AlignImages())首先嚐試從每個圖像中檢測組織的邊緣。默認情況下將第一個圖像(reference.index = 1)作爲參考,但是你可以用reference.index參數指定哪個圖像作爲參考。然後對於每幅圖像,學習一個變換矩陣,來映射座標到參考圖像。這種對齊方法有時會失敗,在這種情況下,你可以使用ManualAlignImages()函數手動對齊圖像。在以下情況下通常需要這樣做:(1)組織比圖像背景大,因此部分組織在幀邊緣之外,(2)組織具有對稱的形狀(例如,如果組織是圓形,使用組織邊緣將很難找到最佳對齊),(3)masking 失敗。

實際的轉換是使用imager R包中的imwarp()函數使用“反向”轉換策略完成的。這種方法確保每個像素都是使用線性插值繪製,注意,這樣對齊後的圖像將經歷一些質量損失。

se <- se %>% MaskImages() %>% AlignImages()
ImagePlot(se, method = "raster", darken = TRUE, type = "processed", ncols = 2)
ImagePlot(se, method = "raster", darken = TRUE, type = "processed.masks", ncols = 2)

圖像的旋轉。因爲切片的時候或者貼玻片的時候可能並沒對其,這時候就可以用這個來做了。

transforms <- list("2" = list("angle" = 90))
se.rotate90 <- WarpImages(se, transforms)
ImagePlot(se.rotate90, method = "raster", darken = T)
transforms <- list("2" = list("mirror.x" = T))
se.mirrorx <- WarpImages(se, transforms)
ImagePlot(se.mirrorx, method = "raster", darken = T)
transforms <- list("2" = list("mirror.y" = T))
se.mirrory <- WarpImages(se, transforms)
ImagePlot(se.mirrory, method = "raster", darken = T)

旋轉後的對象可以用來繪製基因表達,分羣等。

FeatureOverlay(se.mirrorx, features = "CD27", 
               sampleids = 1:2,
               cols = c("dark blue", "cyan", "yellow", "red", "dark red"),
               method = "raster",
               dark.theme = T, ncols.samples = 2)
手動繪製感興趣的區域

在STUtility中包含了一個用於手動註釋的shiny應用程序。它允許用戶選擇並給出一個/幾個特定的捕捉點標籤,這可以用於可視化或DEA目的。默認情況下,應用程序將以瀏覽器模式打開。註釋完成後,只需關閉瀏覽器窗口並返回R。這裏我們不再演示。

se <- ManualAnnotation(se)
3D 可視化

STutility允許通過使用Create3DStack()函數在對齊後的HE圖像中檢測到的(細胞)核來實現特徵的3D可視化。座標是根據顏色強度提取的,因爲核的顏色通常比周圍組織的顏色深。這不是一個精確的細胞分割,但它可以很好地捕捉到細胞核的密度,從而確定各種形態結構。

一旦核座標從每個對齊的部分被提取出來,一個z值將被分配到每個部分來創建一個3D堆棧。特徵值可以在堆棧的各個點上插值,然後通過將值映射到一個顏色尺度來實現3D可視化。下面是一些標準,必須滿足:

  • 切片必須來自相同的組織類型,每個切片的形態必須相似 (切的方向要一致)
  • 圖像必須是對齊的,也就是說你必須先運行LoadImages(), MaskImages()和AlignImages()(或者ManualAlignImages())
  • 圖片必須以高於默認400像素的分辨率加載進來。如果圖像低於400像素,Create3DStack()會自動重新加載更高分辨率的圖像,或者在運行Create3DStack()之前可以運行SwitchResolution()來重新加載更高分辨率的圖像。
  • 細胞分割是基於顏色強度的,因此圖像中有任何的噪音如毛髮,褶皺,氣泡或灰塵等將會帶來影響,同時切片厚度和染色不均勻也會影響分割效果。
  • 我們假定組織切片已用蘇木精和伊紅染色
se <- Create3DStack(se)
stack_3d <- setNames(GetStaffli(se)@scatter.data, c("x", "y", "z", "grid.cell"))

ggplot(stack_3d, aes(x, 2e3 - y)) +
    geom_point(size = 0.1, color = "lightgray") +
    facet_wrap(~z, ncol = 1) +
    theme_void() +
    theme(plot.background = element_rect(fill = "black"), 
          plot.title = element_text(colour = "white"), 
          legend.text = element_text(colour = "white"))

ggplot(interpolated.data, aes(x, 2e3 - y, color = val)) +
    geom_point(size = 0.1) +
    facet_wrap(~z, ncol = 2) +
    theme_void() +
    ggtitle("CD27") +
    scale_color_gradientn(colours = c("black", "dark blue", "cyan", "yellow", "red", "dark red")) +
    theme(plot.background = element_rect(fill = "black"), 
          plot.title = element_text(colour = "white"), 
          legend.text = element_text(colour = "white"))

下面是多張切片對齊後的空間表達,這是一張由plotly繪製的3D交互動圖:

FeaturePlot3D(se, features = "CD27", dark.theme = TRUE, pt.size = 0.6, pts.downsample = 5e4)

更多分析可以看:STUtility || 空間轉錄組多樣本分析框架(二)


Bergenstråhle, J., Larsson, L. & Lundeberg, J. Seamless integration of image and molecular analysis for spatial transcriptomics workflows. BMC Genomics 21, 482 (2020). https://doi.org/10.1186/s12864-020-06832-3
https://rdcu.be/ccSyI
https://ludvigla.github.io/STUtility_web_site/index.html
https://www.spatialresearch.org/
https://support.10xgenomics.com/spatial-gene-expression/datasets

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章