---
title: "Comparison of WT vs KO AOM_DSS mouse model"
author: Anne Hartebrodt
date: "09.01.2024"
output:
html_document:
number_sections: true
---
The goal of this analysis is to elucidate if there is a change in cell type composition between the two conditions. We will use integrated clustering and differential abundance analysis.
knitr::opts_chunk$set(message = FALSE, warning = FALSE, echo=TRUE, include=TRUE)
require(scater)
require(scran)
require(Seurat)
require(stringr)
require(patchwork)
require(DAseq)
require(harmony)
require(data.table)
require(dplyr)
require(decoupleR)
library(dorothea)
library(dplyr)
library(tibble)
library(tidyr)
library(patchwork)
library(ggplot2)
library(pheatmap)
In order to use this notebook please note that you should create an
analysis folder with a subdirectory data. In the course of
the notebook execution a results folder with various subdirectories will
be created automatically and an html version of the notebook will be
created in the directory where you execute this notebook.
## Your fully qualified base directory.
analysis_dir<-'~/Documents/external_analyses/marc_stemmler/'
data_dir<-file.path(analysis_dir, 'data')
input_data<-file.path(data_dir, 'merged.rescaled2.RData')
marker_file<-file.path(data_dir, 'marker_genes.tsv.csv')
DASeq relies on python. Make sure to update the python environment accordingy.
python_env<-'~/anaconda3/envs/daseq2/bin/python3'
results.dir<-file.path(analysis_dir, 'results')
dir.create(results.dir)
sce.data<-get(load(input_data))
set.seed(12)
sce.seurat <-as.Seurat(sce.data, data = NULL)
genes<-rownames(sce.seurat@assays$originalexp)
genes<-as.character(sapply(genes, function(x) str_split(x, '--', simplify = T)[[1]]))
samples <-sce.seurat@assays$originalexp@data@Dimnames[[2]]
# Create a new seurat object using only the original counts.
x <-sce.seurat@assays$originalexp@counts
rownames(x)<-genes
sce.seurat<-CreateSeuratObject(counts = x)
sce.seurat$batch<-sce.data$batch
sce.seurat$condition<-sce.data$condition
qc.dir <- file.path(results.dir, 'qc')
dir.create(qc.dir)
sce.seurat[["percent.mt"]] <- PercentageFeatureSet(sce.seurat, pattern = "^mt-")
p<-VlnPlot(sce.seurat, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
ggsave(p, file = file.path(qc.dir, 'before_qc.pdf'), height = 10)
fwrite(as.data.table(table(sce.seurat@meta.data['batch'])), file = file.path(qc.dir, 'cells_before_qc.tsv'), sep = '\t')
# Subset and plot after qc
sce.seurat <- subset(sce.seurat, subset = nFeature_RNA > 100 & nFeature_RNA < 6000 & percent.mt < 10)
p<-VlnPlot(sce.seurat, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
ggsave(p, file = file.path(qc.dir, 'after_qc.pdf'), height = 10)
fwrite(as.data.table(table(sce.seurat@meta.data['batch'])), file = file.path(qc.dir, 'cells_after_qc.tsv'), sep = '\t')
p
Here, a standard Seurat preprocessing pipeline is run with Data normalization, Variable Feature Extraction, PCA, Neighborhood embedding and UMAP representation.
sce.seurat <- NormalizeData(object = sce.seurat)
sce.seurat <- FindVariableFeatures(object = sce.seurat)
sce.seurat <- ScaleData(object = sce.seurat)
sce.seurat <- RunPCA(object = sce.seurat)
sce.seurat <- FindNeighbors(object = sce.seurat, k.param = 20)
sce.seurat <- FindClusters(object = sce.seurat)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
##
## Number of nodes: 1473
## Number of edges: 41812
##
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8728
## Number of communities: 13
## Elapsed time: 0 seconds
sce.seurat <- RunUMAP(object = sce.seurat, dims = 1:50)
DimPlot(object = sce.seurat, reduction = "umap")
standard.plots<-DimPlot(object = sce.seurat, group.by = c('seurat_clusters', 'batch', 'condition'))
ggsave(standard.plots, file = file.path(qc.dir, 'embeddings_before_harmony.pdf'), width = 25, units = 'cm', height = 10)
standard.plots+ plot_layout(ncol = 1)
There is no massive batch effect in the data, if any, there is a slight problem width SCE.005/ cluster 3. Since the other analysis used batch integration it is used here as well to avoid to strong differences.
We use harmony to integrate the batches.
sce.seurat <- RunHarmony(sce.seurat, group.by.vars = "batch")
sce.seurat <- RunUMAP(sce.seurat, reduction = "harmony", dims = 1:30)
sce.seurat <- FindNeighbors(sce.seurat, reduction = "harmony", dims = 1:30) %>% FindClusters()
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
##
## Number of nodes: 1473
## Number of edges: 49120
##
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8656
## Number of communities: 12
## Elapsed time: 0 seconds
harmony.plot<-DimPlot(object = sce.seurat, group.by = c('seurat_clusters', 'batch', 'condition'))
ggsave(harmony.plot, file = file.path(qc.dir, 'embeddings_after_harmony.pdf'), width = 25, units = 'cm', height = 10)
harmony.plot+ plot_layout(ncol = 1)
In order to annotate the clusters to phenotypes from the literature, we use known gene sets and score the data using the ModuleScore function of Seurat. There are several gene sets in the literature, and we use several to see which one fits the best.
markers<-fread(marker_file, header=FALSE)
iCAF <- markers[V2 == 'iCAF']$V1
myCAF<- markers[V2 == 'myCAF']$V1
apCAF<- markers[V2 == 'apCAF']$V1
mCAF<- markers[V2 == 'mCAF']$V1
dCAF<- markers[V2 == 'dCAF']$V1
vCAF<- markers[V2 == 'vCAF']$V1
score<-list(iCAF, myCAF, apCAF, mCAF, dCAF, vCAF)
names(score)<-c('iCAF', 'myCAF', 'apCAF', 'mCAF', 'dCAF', 'vCAF')
sce.seurat <- AddModuleScore(
object = sce.seurat,
features = score,
name =c('iCAF', 'myCAF', 'apCAF', 'mCAF', 'dCAF', 'vCAF'),
ctrl = 100)
p1<- FeaturePlot(sce.seurat, c('iCAF1'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0) +ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")
p3<- FeaturePlot(sce.seurat, c('myCAF2'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0)+ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")+ coord_fixed(ratio = 1)
p4<- FeaturePlot(sce.seurat, c('apCAF3'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0)+ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")+ coord_fixed(ratio = 1)
p2<- FeaturePlot(sce.seurat, c('mCAF4'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0)+ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")+ coord_fixed(ratio = 1)
p5<- FeaturePlot(sce.seurat, c('dCAF5'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0)+ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")+ coord_fixed(ratio = 1)
p6<- FeaturePlot(sce.seurat, c('vCAF6'), pt.size= 3)+scale_color_gradient2(low = 'blue', mid = 'lightgrey',high = 'red', midpoint = 0)+ggtitle("")+xlab("")+ylab("")+ coord_fixed(ratio = 1)+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")
cellstates.dir<-file.path(results.dir, 'cellstates')
dir.create(cellstates.dir)
pp<-p1+p3+p4+p2+p5+p6+ plot_layout(ncol = 2)
ggsave(pp, file = file.path(cellstates.dir, 'cellstates.pdf'), height = 30, units = 'cm')
pp
pnames<-c('iCAF', 'myCAF', 'apCAF', 'mCAF', 'dCAF', 'vCAF')
i<-1
for (p in list(p1, p2, p3, p4, p5, p6)){
ggsave(p, file = file.path(cellstates.dir, paste0(pnames[i], '.pdf')), height = 8, width = 8)
ggsave(p, file = file.path(cellstates.dir, paste0(pnames[i], '.png')), height = 8, width = 8)
i<-i+1
}
In order to identify whether the proportion of different cell populations shift between WT and KO, we run differential abundance analysis using DASEQ. DASeq is able to identify groups of cells which contain systematically more cells of one of the conditions. Here, DAseq is set up such that low scores in the DASeq result correspond to higher number of WT cells and high scores indicate higher numbers of KO cells. The analysis here follows the standard procedure as indicated in the tutorial: https://klugerlab.github.io/DAseq/articles/tutorial.html
daseq.dir<-file.path(results.dir, 'daseq')
dir.create(daseq.dir)
sce.pca<-sce.seurat@reductions$pca@cell.embeddings
sce.labels<-rownames(sce.pca)
lables.WT<- rownames(sce.seurat@meta.data[sce.seurat$condition=='WT' ,])
lables.KO<- rownames(sce.seurat@meta.data[sce.seurat$condition=='KO' ,])
da_cells <- getDAcells(
X = sce.pca,
cell.labels = sce.labels,
labels.1 = lables.WT,
labels.2 = lables.KO,
k.vector = seq(15, 500, 50),
plot.embedding = sce.seurat@reductions$umap@cell.embeddings[,1:2]
)
## Calculating DA score vector.
## Running GLM.
## Test on random labels.
## Setting thresholds based on permutation
da_cells <- updateDAcells(
X = da_cells, pred.thres = c(-0.8,0.8),
plot.embedding = sce.seurat@reductions$umap@cell.embeddings[,1:2]
)
p<-da_cells$da.cells.plot
ggsave(p, file = file.path(daseq.dir, 'daseq_cells.pdf'))
da_regions <- getDAregion(
X = sce.pca,
da.cells = da_cells,
cell.labels = sce.labels,
labels.1 = lables.WT,
labels.2 = lables.KO,
resolution = 0.01,
min.cell = 15,
plot.embedding = sce.seurat@reductions$umap@cell.embeddings[,1:2],
size= 3
)
## Removing 4 DA regions with cells < 15.
tab<-da_regions$DA.stat
fwrite(tab, file.path(daseq.dir, 'daseq_stats.tsv'))
tab
## DA.score pval.wilcoxon pval.ttest
## [1,] 0.9351875 5.369764e-14 7.701717e-15
## [2,] 1.0000000 1.347801e-12 2.272813e-13
## [3,] -1.0000000 5.623935e-06 1.166155e-05
There are 3 regions which show a statistical enrichment of either WT or KO cell types.
okabe <- c("grey", "red", "black", "blue")
pp<-da_regions$da.region.plot+scale_color_manual (values = okabe)+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)
pp
ggsave(pp, file = file.path(daseq.dir, 'daseq_regions_wiht_number.pdf'), height = 8, width = 9)
ggsave(pp, file = file.path(daseq.dir, 'daseq_regions_wiht_number.png'), height = 8, width = 9, bg = "white")
pp<-pp+scale_color_manual(labels = c("neutral", "KO", "KO", 'WT'), values = okabe)+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank())+ coord_fixed(ratio = 1)
ggsave(pp, file = file.path(daseq.dir, 'daseq_regions_wiht_label.pdf'), height = 8, width = 9)
pp
We see that the black KO region corresponds to a cluster which contains mainly M005 cells. It is hard to evaluate whether this group of cells represents an artifact or a genuine discovery. Here, the choice was made to disregard this cluster from the analysis, because it may not represent a generalizable discovery for all KO mice.
Find representative genes for the regions.
STG_markers <- STGmarkerFinder(
X = sce.seurat@assays$RNA@data,
da.regions = da_regions,
lambda = 1.5, n.runs = 5, return.model = T,
python.use = python_env,
GPU = ''
)
## num_samples : 114
## Epoch: 1000 train loss=1.046695828 valid loss= 1.043797851 valid acc= 1.000000000
## train reg_fs: 0.9917962551116943
## Epoch: 2000 train loss=0.974549890 valid loss= 0.975865960 valid acc= 1.000000000
## train reg_fs: 0.9371622800827026
## Epoch: 3000 train loss=0.908763587 valid loss= 0.919636726 valid acc= 1.000000000
## train reg_fs: 0.8771899938583374
## Optimization Finished!
## test loss: 0.9196367263793945, test acc: 1.0
## 716
## num_samples : 114
## Epoch: 1000 train loss=0.844590425 valid loss= 0.885044634 valid acc= 1.000000000
## train reg_fs: 0.8142205476760864
## Epoch: 2000 train loss=0.765868723 valid loss= 0.827748299 valid acc= 0.944444418
## train reg_fs: 0.7495274543762207
## Epoch: 3000 train loss=0.701694310 valid loss= 0.760835052 valid acc= 1.000000000
## train reg_fs: 0.6849058866500854
## Optimization Finished!
## test loss: 0.7608350515365601, test acc: 1.0
## 97
## num_samples : 114
## Epoch: 1000 train loss=0.651630700 valid loss= 0.665259421 valid acc= 1.000000000
## train reg_fs: 0.6218627691268921
## Epoch: 2000 train loss=0.582408190 valid loss= 0.606567800 valid acc= 1.000000000
## train reg_fs: 0.561877965927124
## Epoch: 3000 train loss=0.519773245 valid loss= 0.553079665 valid acc= 1.000000000
## train reg_fs: 0.5060113668441772
## Optimization Finished!
## test loss: 0.5530796647071838, test acc: 1.0
## 65
## num_samples : 114
## Epoch: 1000 train loss=0.501071990 valid loss= 0.539566278 valid acc= 0.944444418
## train reg_fs: 0.4571804702281952
## Epoch: 2000 train loss=0.467387408 valid loss= 0.486466348 valid acc= 0.944444418
## train reg_fs: 0.41316932439804077
## Epoch: 3000 train loss=0.397382945 valid loss= 0.437929630 valid acc= 0.944444418
## train reg_fs: 0.3737944960594177
## Optimization Finished!
## test loss: 0.437929630279541, test acc: 0.9444444179534912
## 54
## num_samples : 114
## Epoch: 1000 train loss=0.351103395 valid loss= 0.373511761 valid acc= 1.000000000
## train reg_fs: 0.33759021759033203
## Epoch: 2000 train loss=0.318421364 valid loss= 0.340284646 valid acc= 1.000000000
## train reg_fs: 0.30549705028533936
## Epoch: 3000 train loss=0.287361622 valid loss= 0.312105000 valid acc= 1.000000000
## train reg_fs: 0.2773233950138092
## Optimization Finished!
## test loss: 0.3121050000190735, test acc: 1.0
## 48
## num_samples : 84
## Epoch: 1000 train loss=1.000762701 valid loss= 1.001584053 valid acc= 1.000000000
## train reg_fs: 0.987221360206604
## Epoch: 2000 train loss=0.936720610 valid loss= 0.943250418 valid acc= 1.000000000
## train reg_fs: 0.9279508590698242
## Epoch: 3000 train loss=0.872979283 valid loss= 0.901977658 valid acc= 1.000000000
## train reg_fs: 0.863950788974762
## Optimization Finished!
## test loss: 0.9019776582717896, test acc: 1.0
## 536
## num_samples : 84
## Epoch: 1000 train loss=0.813439727 valid loss= 0.989051878 valid acc= 0.949999988
## train reg_fs: 0.7984195947647095
## Epoch: 2000 train loss=0.739849627 valid loss= 1.161535978 valid acc= 0.750000000
## train reg_fs: 0.7314081788063049
## Epoch: 3000 train loss=0.672549307 valid loss= 1.184564590 valid acc= 0.600000024
## train reg_fs: 0.6649122834205627
## Optimization Finished!
## test loss: 1.1845645904541016, test acc: 0.6000000238418579
## 49
## num_samples : 84
## Epoch: 1000 train loss=0.616423845 valid loss= 1.061165094 valid acc= 0.649999976
## train reg_fs: 0.6004747152328491
## Epoch: 2000 train loss=0.549860060 valid loss= 0.878835440 valid acc= 0.800000012
## train reg_fs: 0.5398499965667725
## Epoch: 3000 train loss=0.494013548 valid loss= 0.721590817 valid acc= 0.949999988
## train reg_fs: 0.4839315712451935
## Optimization Finished!
## test loss: 0.7215908169746399, test acc: 0.949999988079071
## 26
## num_samples : 84
## Epoch: 1000 train loss=0.442976654 valid loss= 0.619562447 valid acc= 0.949999988
## train reg_fs: 0.4334219992160797
## Epoch: 2000 train loss=0.394765377 valid loss= 0.523002207 valid acc= 0.949999988
## train reg_fs: 0.3881967067718506
## Epoch: 3000 train loss=0.354995072 valid loss= 0.440699160 valid acc= 0.949999988
## train reg_fs: 0.3481259047985077
## Optimization Finished!
## test loss: 0.44069916009902954, test acc: 0.949999988079071
## 22
## num_samples : 84
## Epoch: 1000 train loss=0.322823763 valid loss= 0.374619067 valid acc= 1.000000000
## train reg_fs: 0.31274858117103577
## Epoch: 2000 train loss=0.289415061 valid loss= 0.327162355 valid acc= 1.000000000
## train reg_fs: 0.2817081809043884
## Epoch: 3000 train loss=0.261621624 valid loss= 0.288426757 valid acc= 1.000000000
## train reg_fs: 0.2545800507068634
## Optimization Finished!
## test loss: 0.2884267568588257, test acc: 1.0
## 20
## num_samples : 32
## Epoch: 1000 train loss=1.034658551 valid loss= 1.159772754 valid acc= 1.000000000
## train reg_fs: 0.9919085502624512
## Epoch: 2000 train loss=0.953719437 valid loss= 1.069233060 valid acc= 1.000000000
## train reg_fs: 0.9354907274246216
## Epoch: 3000 train loss=0.892450809 valid loss= 1.001452565 valid acc= 1.000000000
## train reg_fs: 0.8736256957054138
## Optimization Finished!
## test loss: 1.0014525651931763, test acc: 1.0
## 737
## num_samples : 32
## Epoch: 1000 train loss=0.823999465 valid loss= 1.149300575 valid acc= 0.833333313
## train reg_fs: 0.8098279237747192
## Epoch: 2000 train loss=0.754330635 valid loss= 1.132464886 valid acc= 0.833333313
## train reg_fs: 0.7429871559143066
## Epoch: 3000 train loss=0.683510005 valid loss= 1.102321982 valid acc= 0.833333313
## train reg_fs: 0.6761536598205566
## Optimization Finished!
## test loss: 1.102321982383728, test acc: 0.8333333134651184
## 74
## num_samples : 32
## Epoch: 1000 train loss=0.642935932 valid loss= 0.885854781 valid acc= 0.833333313
## train reg_fs: 0.6176947951316833
## Epoch: 2000 train loss=0.595218062 valid loss= 0.835131049 valid acc= 0.833333313
## train reg_fs: 0.5611858367919922
## Epoch: 3000 train loss=0.531115532 valid loss= 0.771598816 valid acc= 0.833333313
## train reg_fs: 0.5077548027038574
## Optimization Finished!
## test loss: 0.7715988159179688, test acc: 0.8333333134651184
## 52
## num_samples : 32
## Epoch: 1000 train loss=0.469875276 valid loss= 0.703926504 valid acc= 0.833333313
## train reg_fs: 0.4567704200744629
## Epoch: 2000 train loss=0.422494829 valid loss= 0.654970527 valid acc= 0.833333313
## train reg_fs: 0.4104986786842346
## Epoch: 3000 train loss=0.379862428 valid loss= 0.603472531 valid acc= 0.833333313
## train reg_fs: 0.36914631724357605
## Optimization Finished!
## test loss: 0.6034725308418274, test acc: 0.8333333134651184
## 41
## num_samples : 32
## Epoch: 1000 train loss=0.339740425 valid loss= 0.525905669 valid acc= 0.833333313
## train reg_fs: 0.3319685161113739
## Epoch: 2000 train loss=0.304611951 valid loss= 0.483825505 valid acc= 0.833333313
## train reg_fs: 0.29925212264060974
## Epoch: 3000 train loss=0.276807666 valid loss= 0.446785033 valid acc= 0.833333313
## train reg_fs: 0.27059632539749146
## Optimization Finished!
## test loss: 0.446785032749176, test acc: 0.8333333134651184
## 38
dp<-DimPlot(sce.seurat, split.by = 'batch' , ncol = 3)
ggsave(dp, file = file.path(daseq.dir, 'batches.split.pdf'))
label<-function(x, threshold){
if (x>threshold){
return ('up')
}
else if(x< -threshold) {
return ('down')
}
else{
return('neutral')
}
}
sce.seurat$da.pred<-da_cells$da.pred
sce.seurat$da.pred.bin<-round(da_cells$da.pred)
sce.seurat$da.pred.bin.label<-sapply(sce.seurat$da.pred, function(x) label(x, 0.8))
da_clusters<-list(c(0), c(8), c(7,9))
for (i in 1:length(STG_markers$da.markers)){
region_2<-as.data.table(STG_markers$da.markers[[as.character(i)]])
region_2<-region_2[p_value<0.01]
print(region_2)
fwrite(region_2, file = file.path(daseq.dir, paste0('region_', i,'.tsv')))
Idents(sce.seurat)<-'seurat_clusters'
sce.sub<-subset(sce.seurat, subset = seurat_clusters %in% da_clusters[[i]] )
pv<-VlnPlot(sce.sub, features = region_2[1:10]$gene , group.by ='da.pred.bin.label' )
ggsave(pv, file = file.path(daseq.dir, paste0('top_region_', i,'.pdf')), width = 20, height = 30, units = 'cm')
}
## gene avg_logFC p_value
## 1: Apod 3.6087416 4.400616e-61
## 2: Osr2 3.3450601 3.869057e-56
## 3: Gas1 2.8932365 2.630517e-54
## 4: Fst 3.2248433 6.781890e-54
## 5: Ctla2a 4.5678976 9.968275e-50
## ---
## 306: C1d 0.9995466 7.453457e-03
## 307: Gstm5 1.0829472 8.089937e-03
## 308: Blvrb 1.2124816 8.273648e-03
## 309: Glud1 0.4116755 8.820157e-03
## 310: Strn3 0.4098708 9.151682e-03
## gene avg_logFC p_value
## 1: Trim29 6.44231783 9.611054e-227
## 2: Pkp1 5.49426401 6.880149e-190
## 3: Celsr2 5.80215778 5.733870e-186
## 4: Dsc3 5.18794218 8.509447e-176
## 5: 1190003J15Rik 5.61830914 4.003599e-172
## ---
## 466: Ppp3ca 0.34634591 6.926656e-03
## 467: Tpm2 0.08361507 7.070530e-03
## 468: Junb -0.22190389 7.584926e-03
## 469: Anxa2 -0.19999891 8.417456e-03
## 470: Rn45s -0.10410975 9.372423e-03
## gene avg_logFC p_value
## 1: Mmp10 3.2565049 1.300282e-25
## 2: Col6a4 3.2625322 1.086675e-20
## 3: Fbn2 3.7727421 1.072979e-18
## 4: Foxl1 3.5903656 2.891393e-18
## 5: Inhba 3.0842734 7.246118e-18
## ---
## 187: Pim1 -1.4133427 8.787636e-03
## 188: Ndufs3 1.2077530 9.362957e-03
## 189: Acta2 0.7538293 9.619177e-03
## 190: Eif3a 0.6812234 9.756517e-03
## 191: Cox6a1 0.5890282 9.906662e-03
mdf<-as.data.table(sce.seurat@meta.data)
summary.da<- mdf[, .N, by = c( 'seurat_clusters','condition', 'da.pred.bin.label')] %>% arrange(seurat_clusters)
summary.da<-as.data.table(summary.da)
fwrite(summary.da, file.path(daseq.dir, 'summary_daseq.tsv'), sep = '\t')
To get some general understanding we run a differential analysis of all WT vs all KO cells.
de.dir<-file.path(results.dir, 'deg')
dir.create(de.dir)
Idents(sce.seurat)<-'condition'
m<-FindMarkers(sce.seurat, ident.1 = 'WT', ident.2 = 'KO')
genes<-rownames(m)
m<-as.data.table(m)
m$gene<-genes
fwrite(m[p_val_adj<0.01], file = file.path(de.dir, 'DEG_WT_KO_p0.01.tsv'), sep = '\t')
vln<-VlnPlot(sce.seurat, features = m$gene[1:10])
ggsave(vln, file = file.path(de.dir, 'vln_plots_top10.pdf'))
In order to characterize cell states further, it is useful to assign tentative labels to each group, even if a categorical cell type classification/definition most likely does not reflect reality. Based on the cell scores above, we used the mcaf, icaf, apcaf definition also used by Elyada et. al to characterize our cells. Additionally a cluster of vCAF cells has been identified.
##############################################################################
## 6. grouped UMAP
numbered.umap<-DimPlot(sce.seurat, group.by = 'seurat_clusters', label = TRUE)
numbered.umap
ggsave(numbered.umap, file = file.path(de.dir, 'umap_with_number.pdf'))
numbered.umap<-DimPlot(sce.seurat, group.by = 'condition', label = TRUE)
numbered.umap
ggsave(numbered.umap, file = file.path(de.dir, 'umap_with_condition.pdf'))
## Assign labels.
sce.seurat$phenotype<-recode(sce.seurat$seurat_clusters, '0' = 'mCAF-iCAF', '1' = 'apCAF', '2'= 'apCAF', '3' = 'mCAF-iCAF', '4' = 'vCAF', '5' = 'mCAF-iCAF', '6' = 'apCAF' ,'7' = 'mCAF-iCAF', '8'='??-0', '9'='mCAF-iCAF', '10' = '??-1', '11' = 'apCAF')
sce.seurat$phenotype_new<-recode(sce.seurat$seurat_clusters, '0' = 'iCAF1', '1' = 'apCAF', '2'= 'apCAF', '3' = 'iCAF2', '4' = 'vCAF', '5' = 'i/myCAF', '6' = 'apCAF' ,'7' = 'myCAF', '8'='ND.3', '9'='myCAF', '10' = 'ND.1', '11' = 'ND.2')
##Plot
labelle.umap<-DimPlot(sce.seurat, group.by = 'phenotype', label = TRUE)
ggsave(labelle.umap, file = file.path(de.dir, 'labelled_umap.pdf'))
labelle.umap<-DimPlot(sce.seurat, group.by = 'phenotype_new', label = TRUE, label.size = 3, label.box = T, repel = T, pt.size = 3)+xlab("")+ylab("")+theme(axis.text = element_blank(), axis.ticks = element_blank())+ggtitle("")+xlab("")+ylab("")+theme(axis.ticks = element_blank(), axis.text = element_blank(), aspect.ratio = 1)+ guides(color="none")
labelle.umap
ggsave(labelle.umap, file = file.path(de.dir, 'labelled_umap.pdf'), height = 8, width = 8)
ggsave(labelle.umap, file = file.path(de.dir, 'labelled_umap.png'), height = 8, width = 8)
The ModuleScores calculated above shown for each of the cell phenotype groups.
Idents(sce.seurat)<-'phenotype_new'
icaf1.score<-VlnPlot(sce.seurat, features = 'iCAF1', idents = c('iCAF1', 'iCAF2', 'i/myCAF','myCAF'), split.by = 'condition')+ggtitle('iCAF scores in phenotype clusters')+xlab('')
icaf1.score
ggsave(icaf1.score, file = file.path(cellstates.dir, 'iCAF_scores_in_pops.pdf'), width = 30, height = 10, units = 'cm')
Idents(sce.seurat)<-'phenotype_new'
mycaf.score<-VlnPlot(sce.seurat, features = 'myCAF2', idents = c('iCAF1', 'iCAF2', 'i/myCAF','myCAF'), split.by = 'condition') +ggtitle('myCAF scores in phenotype clusters')+xlab('')
mycaf.score
ggsave(mycaf.score, file = file.path(cellstates.dir, 'myCAF_scores_in_pops.pdf'), width = 30, height = 10, units = 'cm')
meta<-as.data.table(sce.seurat@meta.data)
for (g in list(c(7,9), c(3,5), c(0))){
for(phe in c('WT', 'KO')){
co<-cor.test(meta[condition == phe & seurat_clusters %in% g]$iCAF1, meta[condition == phe & seurat_clusters %in% g]$myCAF2)
chars<-capture.output(co)
fileConn<-file(file.path(cellstates.dir, paste0('corr.', paste(g, collapse = ''), '.', phe, '.txt')))
writeLines(chars, con =fileConn )
close(fileConn)
}
}
scatter<-ggplot(meta, aes(iCAF1, myCAF2))+geom_point()+facet_wrap(c("phenotype_new", "condition"), scales = 'free')
ggsave(scatter, file = file.path(cellstates.dir, 'scatter_correlations.pdf'), height = 30, width = 30, units = 'cm')
Idents(sce.seurat)<-'seurat_clusters'
icaf.score<-VlnPlot(sce.seurat, features = 'iCAF1', split.by = 'condition')+ggtitle('iCAF score by Leiden clusters')
ggsave(icaf.score, file = file.path(cellstates.dir, 'iCAF_scores_by_seurat.pdf'))
Idents(sce.seurat)<-'phenotype_new'
icaf.score<-VlnPlot(sce.seurat, features = 'iCAF1', split.by = 'condition')+ggtitle('iCAF score by Phenotype clusters')
icaf.score
ggsave(icaf.score, file = file.path(cellstates.dir, 'iCAF_scores_by_phenotype.pdf'))
summary_table<-table(sce.seurat@meta.data[, c("condition", "phenotype_new")])
fwrite(summary_table, file = file.path(de.dir, 'cells_per_pop.tsv'), sep= '\t')
Idents(sce.seurat)<-'phenotype_new'
mm<-FindMarkers(sce.seurat, ident.1 = 'iCAF1', ident.2 = c('myCAF', 'i/myCAF', 'iCAF2'))
g<-rownames(mm)
mm<-as.data.table(mm)
mm$genes<-g
fwrite(mm, file = file.path(de.dir, 'cluster0_vs_restofmCAFiCAF.tsv'), sep = '\t')
Idents(sce.seurat)<-'phenotype_new'
mm<-FindMarkers(sce.seurat, ident.1 = 'i/myCAF' , ident.2 = c('myCAF'))
g<-rownames(mm)
mm<-as.data.table(mm)
mm$genes<-g
fwrite(mm, file = file.path(de.dir, 'clusterimyCAF_vs_clustermyCAF.tsv'), sep = '\t')
all_genes<-c()
Idents(sce.seurat)<-'phenotype_new'
phenotypes<-c('myCAF', 'i/myCAF', 'iCAF2', 'vCAF', 'iCAF1', 'apCAF')
for (pop in phenotypes){
sub.seurat<-subset(sce.seurat, subset = phenotype_new == pop)
Idents(sub.seurat)<-'condition'
m<-FindMarkers(sub.seurat, ident.1 = 'WT', ident.2 = 'KO')
genes<-rownames(m)
m<-as.data.table(m)
m$genes<-genes
m<-m[p_val_adj < 0.05]
pop <- gsub('/', '_', pop)
fwrite(m, file = file.path(de.dir, paste0(pop, 'DEG_WT_KO_p0.05.tsv')), sep = '\t')
sub.dir<-file.path(de.dir, pop)
dir.create(sub.dir)
for(g in m$genes){
pl<-VlnPlot(sub.seurat, features = g)
ggsave(pl, file = file.path(sub.dir, paste0(g, '.pdf')))
}
all_genes<-c(all_genes, m$genes)
}
saveRDS(sce.seurat, file = file.path(results.dir, 'seurat.Rds'), compress = F)
sce.seurat <- readRDS(file.path(results.dir, 'seurat.Rds'))
tf.dir<-file.path(results.dir, 'tf-target')
dir.create(tf.dir)
net <- get_collectri(organism='mouse', split_complexes=FALSE)
fwrite(as.data.table(net), file = file.path(tf.dir, 'collectri.tsv'), sep = '\t')
net1<- net %>% filter(source =='Zeb1')
net2<- net %>% filter(source %in% net1$target)
apcaf.de<-fread(file.path(de.dir, 'apCAFDEG_WT_KO_p0.05.tsv'))
vcaf.de<-fread(file.path(de.dir, 'vCAFDEG_WT_KO_p0.05.tsv'))
icafmcaf.de.0<-fread(file.path(de.dir, 'iCAF1DEG_WT_KO_p0.05.tsv'))
icafmcaf.de.35<-fread(file.path(de.dir, 'i_myCAFDEG_WT_KO_p0.05.tsv'))
icafmcaf.de.79<-fread(file.path(de.dir, 'myCAFDEG_WT_KO_p0.05.tsv'))
icafmcaf.de.1<-fread(file.path(de.dir, 'iCAF2DEG_WT_KO_p0.05.tsv'))
intersect(net1$target, apcaf.de$genes)
## character(0)
intersect(net1$target, icafmcaf.de.0$genes)
## [1] "Cdh13"
p.prim<-VlnPlot(sce.seurat, features =intersect(net1$target, icafmcaf.de.0$genes), split.by = 'condition')
ggsave(p.prim, file = file.path(tf.dir, 'primary_targets_in_iCAFmCAF0.pdf'))
intersect(net1$target, vcaf.de$genes)
## character(0)
intersect(net2$target, apcaf.de$genes)
## character(0)
intersect(net2$target, icafmcaf.de.0$genes)
## [1] "C3" "Egr1" "Cd44" "Fos" "Krt14" "Zeb1"
## [7] "Dhcr24" "Des" "Fosb" "Lcn2" "Serpinh1" "Nt5e"
## [13] "Hk2"
intersect(net2$target, icafmcaf.de.1$genes)
## character(0)
intersect(net2$target, icafmcaf.de.35$genes)
## character(0)
intersect(net2$target, icafmcaf.de.79$genes)
## character(0)
intersect(net2$target, vcaf.de$genes)
## [1] "S100a6"
targets.targets.zeb1.icaf<-VlnPlot(sce.seurat, features = intersect(net2$target, icafmcaf.de.0$genes), split.by = 'condition')
ggsave(targets.targets.zeb1.icaf, file = file.path(tf.dir, 'targets_of_targets_of_zeb1_icaf_mcaf_0.pdf'), height = 60, width = 40, units = 'cm', limitsize = F)
targets.targets.zeb1.vacf<-VlnPlot(sce.seurat, features = intersect(net2$target, vcaf.de$genes), split.by = 'condition')
ggsave(targets.targets.zeb1.vacf, file = file.path(tf.dir, 'targets_of_targets_of_zeb1_vCAF.pdf'), height = 60, width = 40, units = 'cm', limitsize = F)
sessionInfo()
## R version 4.1.2 (2021-11-01)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 22.04.3 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=de_DE.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=de_DE.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=de_DE.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] pheatmap_1.0.12 tidyr_1.3.0
## [3] tibble_3.2.1 dorothea_1.6.0
## [5] decoupleR_2.7.1 dplyr_1.1.3
## [7] data.table_1.14.8 harmony_1.1.0
## [9] Rcpp_1.0.11 DAseq_1.0.0
## [11] patchwork_1.1.3 stringr_1.5.0
## [13] SeuratObject_4.1.4 Seurat_4.4.0
## [15] scran_1.22.1 scater_1.22.0
## [17] ggplot2_3.4.4 scuttle_1.4.0
## [19] SingleCellExperiment_1.16.0 SummarizedExperiment_1.24.0
## [21] Biobase_2.54.0 GenomicRanges_1.46.1
## [23] GenomeInfoDb_1.30.1 IRanges_2.28.0
## [25] S4Vectors_0.32.4 BiocGenerics_0.40.0
## [27] MatrixGenerics_1.6.0 matrixStats_1.0.0
##
## loaded via a namespace (and not attached):
## [1] rappdirs_0.3.3 scattermore_1.2
## [3] ModelMetrics_1.2.2.2 ragg_1.2.6
## [5] bit64_4.0.5 knitr_1.44
## [7] irlba_2.3.5.1 DelayedArray_0.20.0
## [9] rpart_4.1.16 hardhat_1.3.0
## [11] RCurl_1.98-1.12 generics_0.1.3
## [13] ScaledMatrix_1.2.0 RhpcBLASctl_0.23-42
## [15] cowplot_1.1.1 RANN_2.6.1
## [17] proxy_0.4-27 future_1.33.0
## [19] bit_4.0.5 tzdb_0.4.0
## [21] spatstat.data_3.0-1 xml2_1.3.5
## [23] lubridate_1.9.3 httpuv_1.6.11
## [25] viridis_0.6.4 gower_1.0.1
## [27] xfun_0.40 hms_1.1.3
## [29] jquerylib_0.1.4 evaluate_0.22
## [31] promises_1.2.1 fansi_1.0.5
## [33] progress_1.2.2 readxl_1.4.3
## [35] igraph_1.5.1 htmlwidgets_1.6.2
## [37] spatstat.geom_3.2-5 purrr_1.0.2
## [39] ellipsis_0.3.2 selectr_0.4-2
## [41] backports_1.4.1 deldir_1.0-9
## [43] sparseMatrixStats_1.6.0 vctrs_0.6.4
## [45] ROCR_1.0-11 abind_1.4-5
## [47] caret_6.0-94 cachem_1.0.8
## [49] withr_2.5.1 progressr_0.14.0
## [51] checkmate_2.3.0 vroom_1.6.4
## [53] sctransform_0.4.0 prettyunits_1.2.0
## [55] goftest_1.2-3 cluster_2.1.2
## [57] lazyeval_0.2.2 crayon_1.5.2
## [59] spatstat.explore_3.2-3 glmnet_4.1-8
## [61] edgeR_3.36.0 recipes_1.0.8
## [63] pkgconfig_2.0.3 labeling_0.4.3
## [65] nlme_3.1-155 vipor_0.4.5
## [67] nnet_7.3-17 rlang_1.1.1
## [69] globals_0.16.2 lifecycle_1.0.3
## [71] miniUI_0.1.1.1 rsvd_1.0.5
## [73] cellranger_1.1.0 polyclip_1.10-6
## [75] lmtest_0.9-40 Matrix_1.6-1.1
## [77] zoo_1.8-12 beeswarm_0.4.0
## [79] ggridges_0.5.4 png_0.1-8
## [81] viridisLite_0.4.2 bitops_1.0-7
## [83] KernSmooth_2.23-20 pROC_1.18.4
## [85] DelayedMatrixStats_1.16.0 shape_1.4.6
## [87] parallelly_1.36.0 spatstat.random_3.1-6
## [89] readr_2.1.4 beachmat_2.10.0
## [91] scales_1.2.1 magrittr_2.0.3
## [93] plyr_1.8.9 ica_1.0-3
## [95] zlibbioc_1.40.0 compiler_4.1.2
## [97] dqrng_0.3.1 RColorBrewer_1.1-3
## [99] fitdistrplus_1.1-11 cli_3.6.1
## [101] XVector_0.34.0 listenv_0.9.0
## [103] pbapply_1.7-2 MASS_7.3-55
## [105] tidyselect_1.2.0 stringi_1.7.12
## [107] textshaping_0.3.7 yaml_2.3.7
## [109] BiocSingular_1.10.0 locfit_1.5-9.8
## [111] ggrepel_0.9.4 grid_4.1.2
## [113] sass_0.4.7 bcellViper_1.30.0
## [115] tools_4.1.2 timechange_0.2.0
## [117] future.apply_1.11.0 parallel_4.1.2
## [119] rstudioapi_0.15.0 bluster_1.4.0
## [121] foreach_1.5.2 metapod_1.2.0
## [123] gridExtra_2.3 prodlim_2023.08.28
## [125] farver_2.1.1 Rtsne_0.16
## [127] digest_0.6.33 shiny_1.7.5.1
## [129] lava_1.7.2.1 later_1.3.1
## [131] RcppAnnoy_0.0.21 httr_1.4.7
## [133] colorspace_2.1-0 rvest_1.0.3
## [135] tensor_1.5 reticulate_1.34.0
## [137] splines_4.1.2 uwot_0.1.16
## [139] statmod_1.5.0 OmnipathR_3.11.1
## [141] spatstat.utils_3.0-3 sp_2.1-1
## [143] plotly_4.10.2 systemfonts_1.0.5
## [145] xtable_1.8-4 jsonlite_1.8.7
## [147] timeDate_4022.108 ipred_0.9-14
## [149] R6_2.5.1 pillar_1.9.0
## [151] htmltools_0.5.6.1 mime_0.12
## [153] glue_1.6.2 fastmap_1.1.1
## [155] BiocParallel_1.28.3 BiocNeighbors_1.12.0
## [157] class_7.3-20 codetools_0.2-18
## [159] utf8_1.2.3 lattice_0.20-45
## [161] bslib_0.5.1 spatstat.sparse_3.0-2
## [163] logger_0.2.2 curl_5.1.0
## [165] ggbeeswarm_0.7.2 leiden_0.4.3
## [167] survival_3.2-13 limma_3.50.3
## [169] rmarkdown_2.25 munsell_0.5.0
## [171] e1071_1.7-13 GenomeInfoDbData_1.2.7
## [173] iterators_1.0.14 reshape2_1.4.4
## [175] gtable_0.3.4