library(tidyverse)
library(matrixStats)
library(patchwork)
library(RColorBrewer)
library(gridExtra)
library(tximport)
library(DESeq2)
library(apeglm)
library(sva)
library(umap)
library(Rtsne)
library(limma)
library(ConsensusClusterPlus)
library(pheatmap)
library(ggrepel)
library(data.table)
library(survival)
library(survminer)
library(clusterProfiler)
library(msigdbr)
library(fgsea)
library(ggridges)
library(enrichplot)
library(BiocParallel)
# Unsupervised Analysis Functions-----------------------------------------------
plot_pca <- function(matriz, variables, objeto_vsd, continua = FALSE) {
pca_res <- prcomp(t(matriz), scale. = FALSE)
df_plot <- as.data.frame(pca_res$x)
metadatos <- as.data.frame(colData(objeto_vsd)[colnames(matriz), ])
df_plot <- cbind(df_plot, metadatos)
percentVar <- round(100 * (pca_res$sdev^2 / sum(pca_res$sdev^2)))
p <- ggplot(df_plot, aes(PC1, PC2, color = .data[[variables]])) +
geom_point(size = 3, alpha = 0.8) +
labs(x = paste0("PC1: ", percentVar[1], "%"),
y = paste0("PC2: ", percentVar[2], "%"),
color = variables) +
theme_bw() +
theme(
axis.title = element_text(face = "bold", size = 12),
axis.text = element_text(size = 12, color = "black"),
legend.position = "top",
legend.title = element_text(face = "bold", size = 12),
legend.text = element_text(size = 12)
)
if (continua) {
p +
scale_color_viridis_c(name = variables) +
theme(legend.position = "right")
} else {
p + scale_color_brewer(palette = "Set1")
}
}
consensus_clustering <- function(matriz, objeto_vsd, Tejido,k)
{
genes_mad <- rowMads(matriz)
mat_cc <- matriz[genes_mad > quantile(genes_mad, 0.75), ]
mat_cc_scaled <- t(scale(t(mat_cc)))
cat( nrow(mat_cc), "genes retendios \n")
results <- ConsensusClusterPlus(mat_cc_scaled, maxK = 7, reps = 1000,
pItem = 0.8, pFeature = 1, clusterAlg = "hc",
distance = "pearson", plot = "png",title = paste0("CC_", Tejido), seed = 123)
vars <- intersect(c("Subtipo", "proliferation_score"), colnames(colData(objeto_vsd)))
ann_col <- as.data.frame(colData(objeto_vsd)[, vars, drop = FALSE])
colnames(ann_col)[colnames(ann_col) == "proliferation_score"] <- "Proliferación"
cm <- results[[k]]$consensusMatrix
colnames(cm) <- rownames(cm) <- colnames(mat_cc)
ann_colors <- list(
Subtipo = c(LCMc = "red", LCMng = "blue", Indeterminado = "lightgray"),
Proliferación = c("white", "darkcyan")
)
p <- pheatmap(cm,
annotation_col = ann_col, annotation_colors = ann_colors,
show_rownames = FALSE,fontsize_row = 10, show_colnames = FALSE, annotation_names_col = FALSE,silent = TRUE)
return(list(plot = p, res= results))
}
# DEA functions------------------------------------------------------------------
plot_volcano <- function(resLFC_df, top_n , lfc_cut = 0.5, padj_cut = 0.1)
{
deg_clean <- resLFC_df %>%
mutate(
diff_exp = case_when(
log2FoldChange > lfc_cut & padj < padj_cut ~ "Sobreexpresados",
log2FoldChange < -lfc_cut & padj < padj_cut ~ "Infraexpresados",
.default = "NS"
)
)
top_labels <- bind_rows(
filter(deg_clean, diff_exp == "Sobreexpresados") %>% slice_min(padj, n = top_n),
filter(deg_clean, diff_exp == "Infraexpresados") %>% slice_min(padj, n = top_n)
)
p <- ggplot(deg_clean, aes (x = log2FoldChange,y = -log10(padj),color = diff_exp)) +
geom_point(alpha = 0.5, size = 1) +
geom_vline(xintercept = c(-lfc_cut, lfc_cut),linetype = "dashed", color = "grey60") +
geom_hline(yintercept = -log10(padj_cut), linetype = "dashed", color = "grey60") +
scale_color_manual(values = c("Sobreexpresados" = "red", "Infraexpresados" = "blue","NS" = "grey80")) +
labs(
x = expression(log[2] ~ "Fold Change"),
y = expression(-log[10] ~ "(adj. p-value)"),
color = ""
) +
geom_text(data=top_labels,
aes(label = gene_label),
size = 3, color = "black",
vjust = -0.5,
check_overlap = TRUE
) +
theme_classic() + theme(legend.position = "top",legend.text = element_text(size=10))
return(p)
}
# Heatmap
plot_combined_heatmap <- function(contrast_results,
vsd,
dds,
top_n,
lfc_threshold = 0.5,
padj_threshold = 0.1,
cont_filter = FALSE) {
stopifnot(
is.list(contrast_results),
length(contrast_results) >= 1,
top_n >= 1
)
# Match the number of contrasts
cont_filter <- rep_len(cont_filter, length(contrast_results))
# Strip Ensembl version suffix
strip_version <- function(ids) sub("\\..*", "", ids)
# Select the top up- and down-regulated genes of a single contrast
select_top_genes <- function(results_df, n_genes, apply_cont_filter) {
if (is.null(results_df) || nrow(results_df) == 0) {
return(character(0))
}
classified <- results_df %>%
dplyr::mutate(
regulation = dplyr::case_when(
log2FoldChange > lfc_threshold & padj < padj_threshold ~ "up",
log2FoldChange < -lfc_threshold & padj < padj_threshold ~ "down",
.default = "ns"
)
)
if (apply_cont_filter) {
classified <- dplyr::filter(classified, regulation != "ns")
}
top_for_direction <- function(direction) {
classified %>%
dplyr::filter(regulation == direction) %>%
dplyr::slice_min(padj, n = n_genes, with_ties = FALSE) %>%
dplyr::pull(Gene.stable.ID)
}
strip_version(unique(c(top_for_direction("up"), top_for_direction("down"))))
}
# Gene selection across all contrasts
selected_genes <- purrr::imap(contrast_results, function(df, i) {
select_top_genes(df, top_n, cont_filter[i])
}) %>%
unlist() %>%
unique()
# Expression matrix, with version-stripped row names for clean matching
expression_matrix <- SummarizedExperiment::assay(vsd)
rownames(expression_matrix) <- strip_version(rownames(expression_matrix))
selected_genes <- selected_genes[selected_genes %in% rownames(expression_matrix)]
message(sprintf("Genes mapped to the heatmap: %d", length(selected_genes)))
if (length(selected_genes) == 0) {
stop("No matching genes found")
}
heatmap_matrix <- expression_matrix[selected_genes, ]
# Map Ensembl IDs to readable gene labels, pooling all contrasts
label_lookup <- contrast_results %>%
purrr::map(~ stats::setNames(.x$gene_label, strip_version(.x$Gene.stable.ID))) %>%
unlist()
label_lookup <- label_lookup[!duplicated(names(label_lookup))]
gene_labels <- label_lookup[selected_genes]
missing_labels <- is.na(gene_labels) | gene_labels == ""
gene_labels[missing_labels] <- selected_genes[missing_labels]
rownames(heatmap_matrix) <- gene_labels
column_annotation <- as.data.frame(
SummarizedExperiment::colData(dds)[, c("Cluster", "Subtipo"), drop = FALSE]
)
# Order samples by cluster instead of clustering the columns
cluster_order <- order(column_annotation$Cluster)
heatmap_matrix <- heatmap_matrix[, cluster_order]
column_annotation <- column_annotation[cluster_order, , drop = FALSE]
pheatmap::pheatmap(
heatmap_matrix,
cluster_rows = TRUE,
cluster_cols = FALSE,
show_rownames = TRUE,
show_colnames = FALSE,
fontsize_row = 8,
fontsize_col = 8,
annotation_col = column_annotation,
scale = "row",
color = colorRampPalette(c("blue", "white", "red"))(100),
border_color = NA
)
}
# GSEA functions---------------------------------------------------------------
run_gsea <- function(df_res, gene_sets_db) {
# Pre-ranked
gene_list <- df_res %>%
filter(!is.na(pvalue)) %>%
mutate(rank = -log10(pvalue) * sign(log2FoldChange)) %>%
arrange(desc(abs(rank))) %>%
distinct(Gene.stable.ID, .keep_all = TRUE) %>%
arrange(desc(rank)) %>%
select(Gene.stable.ID, rank) %>%
deframe()
gse_res <- GSEA(
geneList = gene_list,
TERM2GENE = gene_sets_db,
minGSSize = 10,
maxGSSize = 250,
pvalueCutoff = 0.05,
BPPARAM = SerialParam()
)
return(gse_res)
}
plot_gsea_dotplot <- function(gse_obj, show_n = 5, plot_title = "") {
if (nrow(as.data.frame(gse_obj)) == 0) {
return(NULL)
}
p <- dotplot(gse_obj, showCategory = show_n, split = ".sign", font.size = 10, title = plot_title) +
facet_grid(. ~ .sign) +
theme(plot.title = element_text(face = "bold", hjust = 0.5)) +
scale_y_discrete(labels = function(x) {
x %>%
stringr::str_replace_all("^(REACTOME_|WP_|HALLMARK_)", "") %>%
stringr::str_replace_all("_", " ") %>%
stringr::str_wrap(width = 44)
})
return(p)
}
################################################################################
# Import metadata
################################################################################
load("info.Paula.v2.RData")
sample.info <- sample.info %>%
dplyr::rename(Subtipo=RNAseq_subtype ,Pureza=Purity,Tejido=Tissue, Centro_Secuenciacion=SequencingCenter ) %>%
mutate(Date.of.sample = iconv(Date.of.sample, from = "", to = "UTF-8", sub = "")) %>%
mutate(Centro_Secuenciacion = fct_other(Centro_Secuenciacion, keep = "IDIBAPS", other_level = "Otros")) %>%
mutate(Pureza = replace_na(Pureza, "Desconocida")) %>%
mutate(across(c(-RIN, -Date.of.sample), as.factor)) %>%
drop_na(Tejido,Subtipo)
summary(sample.info)
## Sample Mcode Date.of.sample Centro_Secuenciacion
## M001 : 1 M389 : 6 Length:191 IDIBAPS:176
## M001-T2: 1 M401 : 4 Class :character Otros : 15
## M003 : 1 M032 : 3 Mode :character
## M004 : 1 M432 : 3
## M006 : 1 M001 : 2
## M007 : 1 M014 : 2
## (Other):185 (Other):171
## SequencingCenterProjecteID Sequencer RNAseqDoneBy
## Projecte_22_0496_NGS_EC:42 CNAG_Illumina: 12 BG: 18
## Projecte_25_0162 :42 NextSeq2000 :176 BP: 3
## Projecte_22_0363_NGS_EC:24 Other : 3 CL:158
## Projecte_v376 :15 FN: 12
## CLL_55 :12
## (Other) :53
## NA's : 3
## RIN Material Pureza Tejido Subytpe
## Min. : 2.900 Cryotube: 21 Desconocida: 19 colon : 1 cMCL :84
## 1st Qu.: 6.150 FFPE? : 1 sorted : 68 LN : 62 nnMCL :32
## Median : 7.600 Fresh :123 unsorted :104 Normal: 0 unclass: 4
## Mean : 7.335 OCT : 46 PB :127 NA's :71
## 3rd Qu.: 8.900 Spleen: 1
## Max. :10.000
##
## Subtipo
## cMCL :130
## nnMCL : 53
## Undetermined: 8
##
##
##
##
sample.info$RIN_cat <- cut(
sample.info$RIN,
breaks = c(1, 5, 8, 10),
labels = c("Degradado", "Parcialmente degradado", "Integro")
)
# Remove technical replicates and longitudinal samples + RNA highly degraded samples | RIN_cat == "RNA highly degraded")
samples_excluded <- sample.info %>%
filter(str_detect(Sample, "[_-]")| RIN_cat == "Degradado")%>%
select(Sample,RIN_cat) %>%
distinct()
# Remove technical replicates and longitudinal samples
df <- sample.info %>%
filter(!Sample %in% samples_excluded$Sample) %>%
droplevels()
df$Subtipo<-factor(df$Subtipo,levels = c("cMCL","nnMCL","Undetermined"),labels=c("LCMc","LCMng","Indeterminado"))
df$Material<-factor(df$Material,levels = c("Cryotube","FFPE?","Fresh","OCT"),labels=c("Criotubo","FFPE","Fresco","OCT"))
df$Tejido<-factor(df$Tejido,levels = c("LN","PB","Spleen"),labels=c("TL","SP","Bazo"))
df$Pureza<-factor(df$Pureza,levels = c("Desconocida","sorted","unsorted"),labels=c("Desconocida","Purificada","No purificada"))
summary(df)
## Sample Mcode Date.of.sample Centro_Secuenciacion
## M001 : 1 M001 : 1 Length:135 IDIBAPS:124
## M003 : 1 M003 : 1 Class :character Otros : 11
## M004 : 1 M004 : 1 Mode :character
## M006 : 1 M006 : 1
## M007 : 1 M007 : 1
## M008 : 1 M008 : 1
## (Other):129 (Other):129
## SequencingCenterProjecteID Sequencer RNAseqDoneBy
## Projecte_25_0162 :34 CNAG_Illumina: 8 BG: 16
## Projecte_22_0363_NGS_EC:23 NextSeq2000 :124 BP: 3
## Projecte_22_0496_NGS_EC:20 Other : 3 CL:108
## Projecte_v346 :10 FN: 8
## Projecte_v376 :10
## (Other) :35
## NA's : 3
## RIN Material Pureza Tejido Subytpe
## Min. :5.10 Criotubo:19 Desconocida :12 TL :41 cMCL :55
## 1st Qu.:6.85 FFPE : 1 Purificada :41 SP :93 nnMCL :19
## Median :8.00 Fresco :83 No purificada:82 Bazo: 1 unclass: 3
## Mean :7.75 OCT :32 NA's :58
## 3rd Qu.:8.90
## Max. :9.70
##
## Subtipo RIN_cat
## LCMc :94 Parcialmente degradado:69
## LCMng :36 Integro :66
## Indeterminado: 5
##
##
##
##
################################################################################
# Import counts and gene annotation
################################################################################
# Build paths to kallisto abundance files
files <- list.files("kallistos_tsv", full.names = TRUE)
names(files) <- gsub("_abundance\\.tsv","", basename(files))
# Filter active samples
files <- files[rownames(df)]
# Load Ensembl gene annotation
gene_annotation <- read.delim("Homo_sapiens.GRCh38_genes_transcripts_release-105.txt")
gene_names <- gene_annotation %>%
select(Gene.stable.ID, HGNC.symbol) %>%
distinct() %>%
mutate(gene_label = ifelse(HGNC.symbol == "" | is.na(HGNC.symbol),
Gene.stable.ID, HGNC.symbol))
tx2gene <- gene_annotation %>%
select(Transcript.stable.ID.version, Gene.stable.ID) %>%
distinct()
# Import transcript counts and aggregate to gene level
txi.kallisto <- tximport(files, type = "kallisto", tx2gene = tx2gene)
################################################################################
# Pre-processing of counts
################################################################################
# Filter metadata to match available counts
all(rownames(df) == colnames(txi.kallisto$counts))
## [1] TRUE
# Build general DESeqDataSet
dds <- DESeqDataSetFromTximport(txi.kallisto, colData = df, design = ~ 1)
dds
## class: DESeqDataSet
## dim: 66892 135
## metadata(1): version
## assays(2): counts avgTxLength
## rownames(66892): ENSG00000000003 ENSG00000000005 ... ENSG00000289643
## ENSG00000289644
## rowData names(0):
## colnames(135): M001 M003 ... M545 M546
## colData names(14): Sample Mcode ... Subtipo RIN_cat
# Pre-filtering
# keep only rows that have a count of at least 10 for a minimal number of samples
smallestGroupSize <- min(table(df$Subtipo))
keep <- rowSums(counts(dds) >= 10) >= smallestGroupSize
dds <- dds[keep,]
# Variance stabilizing transformation for unsupervised analysis
vsd <- vst(dds, blind = TRUE)
mat_vsd <-assay(vsd)
# Split dds---------------------------------------------------------------------
dds_SP <- dds[, dds$Tejido == "SP"]
smallestGroupSize_SP <- min(table(dds_SP$Subtipo))
keep_SP <- rowSums(counts(dds_SP) >= 10) >= smallestGroupSize_SP
dds_SP <- dds_SP[keep_SP,]
dds_SP <- estimateSizeFactors(dds_SP)
vsd_SP <- vst(dds_SP, blind =TRUE)
mat_vsd_SP <- assay(vsd_SP)
dds_TL <- dds[, dds$Tejido %in% c("TL","Bazo")]
smallestGroupSize_TL <- min(table(dds_TL$Subtipo))
keep_TL <- rowSums(counts(dds_TL) >= 10) >= smallestGroupSize_TL
dds_TL <- dds_TL [keep_TL,]
dds_TL <- estimateSizeFactors(dds_TL)
vsd_TL<- vst(dds_TL, blind = TRUE)
mat_vsd_TL <- assay(vsd_TL )
################################################################################
# Proliferation score calculate
################################################################################
str(proliferation.signature)
## Named num [1:17] -1 -1 -1 -1 1 1 1 1 1 1 ...
## - attr(*, "names")= chr [1:17] "GLIPR1" "ZDHHC21" "FMNL3" "SPG3A" ...
# gene weights
prolif.weights <- c(GLIPR1 = -29.91, ZDHHC21 = -23.47, FMNL3 = -21.46, ATL1 = -19.64,
ZWINT = 5.41, FAM83D = 5.92, CCNB2 = 6.01, E2F2 = 6.02, H2AX = 6.08,
KIF2C = 6.19, CDC20 = 6.35, CDKN3 = 6.4, NCAPG = 6.44, TOP2A = 6.46,
ESPL1 = 6.5, FOXM1 = 6.55, MKI67 = 6.65)
# Ensembl IDs mapping for signature
sig_ensembl <- gene_names %>%
filter(gene_label %in% names(prolif.weights)) %>%
select(Gene.stable.ID, gene_label)%>%
distinct(gene_label, .keep_all = TRUE) # E2EF replicate:ENSG00000282899 ENSG00000007968
v_weights <- prolif.weights[sig_ensembl$gene_label]
names(v_weights) <- sig_ensembl$Gene.stable.ID
# Multiply each gene by its corresponding weight
prolif_score_SP <- sweep(x = mat_vsd_SP[names(v_weights),],
MARGIN = 1,
STATS = v_weights,
FUN = "*")
# Add proliferation score to sample metadata
vsd_SP$Proliferacion <- colSums(prolif_score_SP)
dds_SP$Proliferacion <- vsd_SP$Proliferacion
prolif_score_TL <- sweep(x = mat_vsd_TL [names(v_weights),],
MARGIN = 1,
STATS = v_weights,
FUN = "*")
# Add proliferation score to sample metadata SP
vsd_TL$Proliferacion<- colSums(prolif_score_TL)
dds_TL$Proliferacion<- vsd_TL$Proliferacion
data_mki67_SP <- plotCounts(dds_SP,
gene = "ENSG00000148773",
intgroup = "Subtipo",
returnData = TRUE)
a<-ggplot(data_mki67_SP,
aes(x = Subtipo, y = count, fill = Subtipo)) +
geom_boxplot(outlier.shape = NA, alpha = 0.4) +
geom_jitter(width = 0.2, size = 3, aes(color = Subtipo)) +
scale_y_log10() +
theme_minimal() +
labs(
title = "Correlación subtipo y gen MKI67 en sangre périferica",
x = "Subtype", y = "Normalized Counts (log10)")
data_mki67_TL <- plotCounts(dds_TL ,
gene = "ENSG00000148773",
intgroup = "Subtipo",
returnData = TRUE)
b<-ggplot(data_mki67_TL ,
aes(x = Subtipo, y = count, fill = Subtipo)) +
geom_boxplot(outlier.shape = NA, alpha = 0.4) +
geom_jitter(width = 0.2, size = 3, aes(color = Subtipo)) +
scale_y_log10() +
theme_minimal() +
labs(
title = "Correlación subtipo y gen MKI67 en tejido linfoide secundario",
x = "Subtype", y = "Normalized Counts (log10)")
a+b

################################################################################
# Ánalisis exploratorio inicial
################################################################################
vars <- c("Tejido","Subtipo","Centro_Secuenciacion","Material","Pureza","RIN_cat")
# PCA global
p_before <- lapply(vars, function(v) plot_pca(mat_vsd, v, vsd))
wrap_plots(p_before, ncol = 2) + plot_annotation(tag_levels = "A")

# PCA según Tejido
vars <- (vars [-1])
TL_before <- lapply(vars, function(v) plot_pca(mat_vsd_TL, v, vsd_TL))
pca_prolif <- plot_pca(mat_vsd_TL, "Proliferacion", vsd_TL, continua=TRUE)
TL_before[[6]] <- pca_prolif
wrap_plots(TL_before, ncol = 2) + plot_annotation(tag_levels = "A")

SP_before <- lapply(vars, function(v) plot_pca(mat_vsd_SP, v, vsd_SP))
pca_prolif <- plot_pca(mat_vsd_SP, "Proliferacion", vsd_SP, continua=TRUE)
SP_before[[6]] <- pca_prolif
wrap_plots(SP_before, ncol = 2) + plot_annotation(tag_levels = "A")

# UMAP SP ---------------------------------------------------------------------------
umap_result <- umap(t(mat_vsd_SP))
umap_df <- as.data.frame(umap_result$layout) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza = vsd_SP$Pureza,
Centro = vsd_SP$Centro_Secuenciacion)
a <- ggplot(umap_df, aes(x =V1, y = V2, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = "UMAP1", y = "UMAP2") +
theme_classic()
# t-SNE SP ---------------------------------------------------------------------
tsne_result <- Rtsne(t(mat_vsd_SP), perplexity = 10)
tsne_df <- as.data.frame(tsne_result$Y) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza = vsd_SP$Pureza)
b <- ggplot(tsne_df, aes(x = V1, y = V2, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = "tSNE1", y = "tSNE2") +
theme_classic()
# MDS SP -------------------------------------------------------------------------
mds <- plotMDS(mat_vsd_SP, gene.selection = "pairwise", plot = FALSE)
mds_df <- data.frame(X = mds$x, Y = mds$y) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza = vsd_SP$Pureza)
per <- mds$var.explained * 100
c <- ggplot(mds_df, aes(X, Y, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = paste0("MDS1 (", round(per[1], 1), "%)"),
y = paste0("MDS2 (", round(per[2], 1), "%)")) +
theme_classic()
# Representación conjunta de los plots
(a + b + c) +
plot_layout(ncol = 3, guides = "collect") +
plot_annotation(tag_levels = "A") &
theme_bw() &
theme(legend.position = "bottom",
axis.title = element_text(face = "bold", size = 14),
axis.text = element_text(size = 12, color="black"),
legend.title = element_text(size=14, face = "bold"),
legend.text = element_text(size=12))

################################################################################
# Análisis de Variables Subrrogadas y correción de facores latentes y efecto de lote
################################################################################
# Definición del modelo para TL
mod_TL <- model.matrix(~ Subtipo + Proliferacion, data = colData(vsd_TL))
mod0_TL <- model.matrix(~ 1, data = colData(vsd_TL))
n_sv_TL <- num.sv(mat_vsd_TL, mod_TL, method = "leek", seed = 123)
svobj_TL <- svaseq(mat_vsd_TL, mod_TL, mod0_TL ,n.sv=n_sv_TL )
## Number of significant surrogate variables is: 1
## Iteration (out of 5 ):1 2 3 4 5
# Correlación SVs con los metadatos
vars2 <- setdiff(c(vars, "RIN", "Proliferacion"), "RIN_cat")
for(i in 1:ncol(svobj_TL$sv)) {
sv_vector <- svobj_TL$sv[,i]
cat(sprintf("\n---------------------- SV%d ------------------------\n", i))
cat(sprintf("%-20s | %-10s | %-6s\n", "Variable", "p-val", "rho"))
cat("----------------------------------------------------\n")
for(var_name in vars2) {
x <- colData(vsd_TL )[[var_name]]
tryCatch({
if (is.numeric(x)) {
res <- cor.test(sv_vector, x, method = "spearman")
p_val <- if(res$p.value < 0.001) "<0.001" else sprintf("%.3f", res$p.value)
cat(sprintf("%-20s | %-10s | %5.3f\n",
var_name, p_val, res$estimate))
} else {
res <- kruskal.test(sv_vector, as.factor(x))
p_val <- if(res$p.value < 0.001) "<0.001" else sprintf("%.3f", res$p.value)
cat(sprintf("%-20s | %-10s | %-6s\n",
var_name, p_val, ""))
}
}, error = function(e) NULL)
}
cat ("\n")
}
##
## ---------------------- SV1 ------------------------
## Variable | p-val | rho
## ----------------------------------------------------
## Subtipo | 0.049 |
## Centro_Secuenciacion | 0.516 |
## Material | 0.035 |
## Pureza | 0.288 |
## RIN | 0.047 | -0.309
## Proliferacion | 0.002 | -0.461
# Correción SV en TL
mat_uns_TL <- removeBatchEffect(
mat_vsd_TL,
covariates = svobj_TL$sv,
design = mod_TL
)
# Definición del modelo para SP
mod_SP <- model.matrix(~ Subtipo + Proliferacion + Centro_Secuenciacion + Pureza, data = colData(vsd_SP))
mod0_SP <- model.matrix(~ Centro_Secuenciacion + Pureza, data = colData(vsd_SP))
n_sv_SP <- num.sv(mat_vsd_SP, mod_SP, method = "leek", seed = 123)
svobj_SP <- svaseq(mat_vsd_SP, mod_SP, mod0_SP,n.sv=n_sv_SP)
## Number of significant surrogate variables is: 1
## Iteration (out of 5 ):1 2 3 4 5
# Correlación SVs con los metadatos
for(i in 1:ncol(svobj_SP$sv)) {
sv_vector <- svobj_SP$sv[, i]
cat(sprintf("\n---------------------- SV%d ------------------------\n", i))
cat(sprintf("%-20s | %-10s | %-6s\n", "Variable", "p-val", "rho"))
cat("----------------------------------------------------\n")
for(var_name in vars2) {
x <- colData(vsd_SP)[[var_name]]
tryCatch({
if (is.numeric(x)) {
res <- cor.test(sv_vector, x, method = "spearman")
p_val <- if(res$p.value < 0.001) "<0.001" else sprintf("%.3f", res$p.value)
cat(sprintf("%-20s | %-10s | %5.3f \n",
var_name, p_val, res$estimate))
} else {
res <- kruskal.test(sv_vector, as.factor(x))
p_val <- if(res$p.value < 0.001) "<0.001" else sprintf("%.3f", res$p.value)
cat(sprintf("%-20s | %-10s | %-6s \n",
var_name, p_val, ""))
}
}, error = function(e) NULL)
}
cat ("\n")
}
##
## ---------------------- SV1 ------------------------
## Variable | p-val | rho
## ----------------------------------------------------
## Subtipo | 0.774 |
## Centro_Secuenciacion | 0.003 |
## Material | 0.016 |
## Pureza | <0.001 |
## RIN | <0.001 | 0.383
## Proliferacion | 0.883 | 0.015
# Correción SV1 y efecto de lote en SP
mat_uns_SP <- removeBatchEffect(
mat_vsd_SP,
covariates = cbind(svobj_SP$sv, vsd_SP$Pureza,vsd_SP$Centro_Secuenciacion),
design = model.matrix(~ Subtipo + Proliferacion, data = colData(vsd_SP))
)
################################################################################
# Métodos no supervisados
################################################################################
# PCA plots---------------------------------------------------------------------
TL_after <- lapply(vars, function(v) plot_pca(mat_uns_TL, v, vsd_TL))
pca_prolif <- plot_pca(mat_uns_TL, "Proliferacion", vsd_TL, continua=TRUE)
TL_after[[6]] <- pca_prolif
wrap_plots(TL_after, ncol = 2) + plot_annotation(tag_levels = "A")

SP_after <- lapply(vars, function(v) plot_pca(mat_uns_SP, v, vsd_SP))
pca_prolif <- plot_pca(mat_uns_SP, "Proliferacion", vsd_SP, continua=TRUE)
SP_after[[6]] <- pca_prolif
wrap_plots(SP_after, ncol = 2) + plot_annotation(tag_levels = "A")

# UMAP SP ---------------------------------------------------------------------------
umap_result <- umap(t(mat_uns_SP))
umap_df <- as.data.frame(umap_result$layout) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza = vsd_SP$Pureza,
Centro = vsd_SP$Centro_Secuenciacion)
d <- ggplot(umap_df, aes(x =V1, y = V2, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = "UMAP1", y = "UMAP2") +
theme_classic()
# t-SNE SP ---------------------------------------------------------------------
tsne_result <- Rtsne(t(mat_uns_SP), perplexity = 10)
tsne_df <- as.data.frame(tsne_result$Y) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza= vsd_SP$Pureza)
e <- ggplot(tsne_df, aes(x = V1, y = V2, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = "tSNE1", y = "tSNE2") +
theme_classic()
# MDS SP -------------------------------------------------------------------------
mds <- plotMDS(mat_uns_SP, gene.selection = "pairwise", plot = FALSE)
mds_df <- data.frame(X = mds$x, Y = mds$y) %>%
mutate(Subtipo = vsd_SP$Subtipo,
Pureza= vsd_SP$Pureza)
per <- mds$var.explained * 100
f <- ggplot(mds_df, aes(X, Y, color = Subtipo)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_brewer(palette = "Set1") +
labs(x = paste0("MDS1 (", round(per[1], 1), "%)"),
y = paste0("MDS2 (", round(per[2], 1), "%)")) +
theme_classic()
# Representación conjunta de los plots
(a + b + c + d + e + f ) +
plot_layout(ncol = 3, guides = "collect") +
plot_annotation(tag_levels = "A") &
theme_bw() &
theme(legend.position = "bottom",
axis.title = element_text(face = "bold", size = 14),
axis.text = element_text(size = 12, color="black"),
legend.title = element_text(size=14, face = "bold"),
legend.text = element_text(size=12))

# Hierarchical clustering -------------------------------------------------------
# Matriz que corrige SV1 y efectos de lote, preservando la señal biológica
p<-consensus_clustering (mat_uns_SP,vsd_SP,"SP_mat_uns25",k=3)
## 8613 genes retendios
grid.arrange(p$plot$gtable)

# PCA de los clústeres que preservan las variables biológicas
vsd_SP0 <- vsd_SP
assay(vsd_SP0) <- mat_uns_SP
clusteres0 <- p$res[[3]]$consensusClass
clusteres0 <- clusteres0[ rownames(colData(vsd_SP0))]
colData(vsd_SP0)$Cluster <- as.factor(clusteres0)
plotPCA(vsd_SP0, intgroup = "Cluster",ntop=8613)

t_cont0<-table(vsd_SP0$Cluster,vsd_SP0$Subtipo)
fisher.test(t_cont0)
##
## Fisher's Exact Test for Count Data
##
## data: t_cont0
## p-value = 1.296e-10
## alternative hypothesis: two.sided
vsd_SP$SV1 <- svobj_SP$sv
# Matriz corregida por efectos de lote, preservando las variables biológicas conocidas
mat_adjusted_SP0.1 <- removeBatchEffect(assay(vsd_SP),
covariates = cbind(vsd_SP$Centro_Secuenciacion, vsd_SP$Pureza),
design = model.matrix(~ vsd_SP$Subtipo + vsd_SP$Proliferacion)
)
# Identificación y observación de clusteres
p0<-consensus_clustering (mat_adjusted_SP0.1,vsd_SP,"SP.025",k=3)
## 8613 genes retendios
grid.arrange(p0$plot$gtable)

# Matriz corregida por efectos de lote y variables biológicas conocidos para identificar nuevos clústeres
mat_adjusted_SP1 <- removeBatchEffect(
assay(vsd_SP),
covariates = cbind(
vsd_SP$Proliferacion,vsd_SP$Subtipo,
vsd_SP$Centro_Secuenciacion,vsd_SP$Pureza
)
)
p1<-consensus_clustering (mat_adjusted_SP1,vsd_SP,"SP1.25",k=3)
## 8613 genes retendios
grid.arrange(p1$plot$gtable)

# Matriz corregida por centro de secuenciación,pureza, efectos biológicos conocidos y SVs identificadas para identificar nuevos clusteres
mat_adjusted_SP2 <- removeBatchEffect(
assay(vsd_SP),
covariates = cbind(
vsd_SP$Proliferacion,vsd_SP$Subtipo,
vsd_SP$Centro_Secuenciacion,vsd_SP$Pureza,
vsd_SP$SV1
)
)
p2<-consensus_clustering (mat_adjusted_SP2,vsd_SP,"SP2.25",k=3)
## 8613 genes retendios
grid.arrange(p2$plot$gtable)

# PCA de los clústeres ajustados por las variables biológicas
vsd_SP1 <- vsd_SP
assay(vsd_SP1) <- mat_adjusted_SP1
clusteres1 <- p1$res[[3]]$consensusClass
clusteres1 <- clusteres1[ rownames(colData(vsd_SP1)) ]
colData(vsd_SP1)$Cluster <- as.factor(clusteres1)
plotPCA(vsd_SP1, intgroup = "Cluster",ntop=8613)

t_cont1<-table(vsd_SP1$Cluster,vsd_SP1$Subtipo)
fisher.test(t_cont1)
##
## Fisher's Exact Test for Count Data
##
## data: t_cont1
## p-value = 0.8342
## alternative hypothesis: two.sided
################################################################################
# Análisis de supervivencia
################################################################################
surv_df <- read.delim("survival.patients.txt", header = TRUE, stringsAsFactors = FALSE)
# Clúesteres que preservan las variables biológicas
cluster_data0 <- data.frame(
id = rownames(colData(vsd_SP0)),
Cluster = colData(vsd_SP0)$Cluster,
Subtipo = colData(vsd_SP0)$Subtipo,
Proliferacion = colData(vsd_SP0)$Proliferacion
)
head(cluster_data0)
## id Cluster Subtipo Proliferacion
## M001 M001 1 LCMc -446.1330
## M003 M003 1 LCMng -259.5152
## M004 M004 2 LCMng -546.5540
## M006 M006 3 LCMc -517.2294
## M008 M008 3 LCMc -538.1799
## M009 M009 3 LCMng -540.2379
surv_data0 <- merge(surv_df, cluster_data0, by = "id")
head(surv_data0)
## id os_yrs event Cluster Subtipo Proliferacion
## 1 M001 0.6926762 1 1 LCMc -446.1330
## 2 M003 7.4168378 1 1 LCMng -259.5152
## 3 M004 4.2710472 1 2 LCMng -546.5540
## 4 M008 1.4839151 0 3 LCMc -538.1799
## 5 M009 6.3299110 1 3 LCMng -540.2379
## 6 M019 5.1033539 1 1 LCMc -474.7440
fit0 <- survfit(Surv(surv_data0$os_yrs, surv_data0$event) ~ Cluster, data = surv_data0)
summary(fit0)$table
## records n.max n.start events rmean se(rmean) median 0.95LCL
## Cluster=1 38 38 38 18 7.218764 1.311128 5.103354 3.433265
## Cluster=2 16 16 16 6 7.737965 2.009521 8.043806 4.271047
## Cluster=3 23 23 23 8 9.909080 1.509100 8.156057 6.329911
## 0.95UCL
## Cluster=1 NA
## Cluster=2 NA
## Cluster=3 NA
ggsurvplot(fit0,
pval = TRUE,
risk.table = TRUE,
xlab = "Tiempo (Años)",
surv.median.line = "hv",
ylab = "Probabilidad de Supervivencia",
ggtheme = theme_minimal(),
palette = "Set4")

cox0 <- coxph(Surv(surv_data0$os_yrs, surv_data0$event) ~ Cluster+ Subtipo + Proliferacion, data = surv_data0)
summary(cox0)
## Call:
## coxph(formula = Surv(surv_data0$os_yrs, surv_data0$event) ~ Cluster +
## Subtipo + Proliferacion, data = surv_data0)
##
## n= 77, number of events= 32
##
## coef exp(coef) se(coef) z Pr(>|z|)
## Cluster2 0.576676 1.780111 0.653983 0.882 0.3779
## Cluster3 -0.684824 0.504179 0.482738 -1.419 0.1560
## SubtipoLCMng -0.751088 0.471853 0.504321 -1.489 0.1364
## SubtipoIndeterminado -0.225571 0.798061 1.109374 -0.203 0.8389
## Proliferacion 0.004169 1.004178 0.001899 2.195 0.0281 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## exp(coef) exp(-coef) lower .95 upper .95
## Cluster2 1.7801 0.5618 0.49406 6.414
## Cluster3 0.5042 1.9834 0.19574 1.299
## SubtipoLCMng 0.4719 2.1193 0.17560 1.268
## SubtipoIndeterminado 0.7981 1.2530 0.09073 7.020
## Proliferacion 1.0042 0.9958 1.00045 1.008
##
## Concordance= 0.69 (se = 0.047 )
## Likelihood ratio test= 9.65 on 5 df, p=0.09
## Wald test = 8.94 on 5 df, p=0.1
## Score (logrank) test = 9.47 on 5 df, p=0.09
# Clusteres ajustados por las variables biológicas
cluster_data1 <- data.frame(
id = rownames(colData(vsd_SP1)),
Cluster = colData(vsd_SP1)$Cluster,
Subtipo = colData(vsd_SP1)$Subtipo,
Proliferacion = colData(vsd_SP1)$Proliferacion
)
head(cluster_data1)
## id Cluster Subtipo Proliferacion
## M001 M001 1 LCMc -446.1330
## M003 M003 2 LCMng -259.5152
## M004 M004 3 LCMng -546.5540
## M006 M006 1 LCMc -517.2294
## M008 M008 1 LCMc -538.1799
## M009 M009 3 LCMng -540.2379
surv_data1 <- merge(surv_df, cluster_data1, by = "id")
head(surv_data1)
## id os_yrs event Cluster Subtipo Proliferacion
## 1 M001 0.6926762 1 1 LCMc -446.1330
## 2 M003 7.4168378 1 2 LCMng -259.5152
## 3 M004 4.2710472 1 3 LCMng -546.5540
## 4 M008 1.4839151 0 1 LCMc -538.1799
## 5 M009 6.3299110 1 3 LCMng -540.2379
## 6 M019 5.1033539 1 3 LCMc -474.7440
fit1 <- survfit(Surv(surv_data1$os_yrs, surv_data1$event) ~ Cluster, data = surv_data1)
summary(fit1)$table
## records n.max n.start events rmean se(rmean) median 0.95LCL
## Cluster=1 28 28 28 13 8.186282 1.423389 6.809035 3.433265
## Cluster=2 25 25 25 11 8.029012 1.623715 7.416838 3.474333
## Cluster=3 24 24 24 8 8.347471 1.688781 5.837098 4.271047
## 0.95UCL
## Cluster=1 NA
## Cluster=2 NA
## Cluster=3 NA
ggsurvplot(fit1,
pval = TRUE,
risk.table = TRUE,
xlab = "Tiempo (Años)",
surv.median.line = "hv",
ylab = "Probabilidad de Supervivencia",
ggtheme = theme_minimal(),
palette = "Set1")

cox1 <- coxph(Surv(surv_data1$os_yrs, surv_data1$event) ~ Cluster + Subtipo + Proliferacion, data = surv_data1)
summary(cox1)
## Call:
## coxph(formula = Surv(surv_data1$os_yrs, surv_data1$event) ~ Cluster +
## Subtipo + Proliferacion, data = surv_data1)
##
## n= 77, number of events= 32
##
## coef exp(coef) se(coef) z Pr(>|z|)
## Cluster2 -0.113326 0.892860 0.421942 -0.269 0.7883
## Cluster3 -0.202695 0.816527 0.464248 -0.437 0.6624
## SubtipoLCMng -0.657962 0.517906 0.391013 -1.683 0.0924 .
## SubtipoIndeterminado -0.916847 0.399778 1.061494 -0.864 0.3877
## Proliferacion 0.002541 1.002544 0.001712 1.484 0.1378
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## exp(coef) exp(-coef) lower .95 upper .95
## Cluster2 0.8929 1.1200 0.39050 2.041
## Cluster3 0.8165 1.2247 0.32870 2.028
## SubtipoLCMng 0.5179 1.9309 0.24067 1.115
## SubtipoIndeterminado 0.3998 2.5014 0.04992 3.202
## Proliferacion 1.0025 0.9975 0.99919 1.006
##
## Concordance= 0.678 (se = 0.055 )
## Likelihood ratio test= 5.7 on 5 df, p=0.3
## Wald test = 5.64 on 5 df, p=0.3
## Score (logrank) test = 5.77 on 5 df, p=0.3
################################################################################
# DEA
################################################################################
# Clíesres que preservan las variables biológicas
clusteres0 <- clusteres0[ rownames(colData(dds_SP)) ]
colData(dds_SP)$Cluster <- as.factor(clusteres0)
dds_SP$SV1 <- svobj_SP$sv[, 1]
design(dds_SP) <- ~ Centro_Secuenciacion + Pureza + SV1 + Proliferacion + Subtipo + Cluster
dds_SP0<- DESeq(dds_SP)
resultsNames(dds_SP0)
## [1] "Intercept"
## [2] "Centro_Secuenciacion_Otros_vs_IDIBAPS"
## [3] "Pureza_Purificada_vs_Desconocida"
## [4] "Pureza_No.purificada_vs_Desconocida"
## [5] "SV1"
## [6] "Proliferacion"
## [7] "Subtipo_LCMng_vs_LCMc"
## [8] "Subtipo_Indeterminado_vs_LCMc"
## [9] "Cluster_2_vs_1"
## [10] "Cluster_3_vs_1"
# Clíesres ajustados por las variables biológicas
clusteres1 <- clusteres1[ rownames(colData(dds_SP))]
colData(dds_SP)$Cluster <- as.factor(clusteres1)
design(dds_SP) <- ~ Centro_Secuenciacion + Pureza + SV1 + Proliferacion + Subtipo + Cluster
dds_SP1 <- DESeq(dds_SP)
resultsNames(dds_SP1)
## [1] "Intercept"
## [2] "Centro_Secuenciacion_Otros_vs_IDIBAPS"
## [3] "Pureza_Purificada_vs_Desconocida"
## [4] "Pureza_No.purificada_vs_Desconocida"
## [5] "SV1"
## [6] "Proliferacion"
## [7] "Subtipo_LCMng_vs_LCMc"
## [8] "Subtipo_Indeterminado_vs_LCMc"
## [9] "Cluster_2_vs_1"
## [10] "Cluster_3_vs_1"
# Volcano plot de los clústeres que preservan las variables biológicas
# Clúster 1 VS Clúster 2
res_C2_vs_C1 <- results(dds_SP0, contrast = c("Cluster", "2", "1"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C2_vs_C1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 4, 0.012%
## LFC < -0.50 (down) : 9, 0.026%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP0_2_1 <- lfcShrink(dds_SP0, contrast = c("Cluster", "2", "1"), res = res_C2_vs_C1, type = "ashr")
summary(resLFC_SP0_2_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 4, 0.012%
## LFC < -0.50 (down) : 9, 0.026%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP0.1 <- as.data.frame(resLFC_SP0_2_1) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP0.1, top_n = 20)

# Clúster 2 VS Clúster 3
res_C2_vs_C3 <- results(dds_SP0, contrast = c("Cluster", "2", "3"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C2_vs_C3)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 214, 0.62%
## LFC < -0.50 (down) : 98, 0.28%
## outliers [1] : 0, 0%
## low counts [2] : 668, 1.9%
## (mean count < 2)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP0_2_3 <- lfcShrink(dds_SP0, contrast = c("Cluster", "2", "3"), res = res_C2_vs_C3, type = "ashr")
summary(resLFC_SP0_2_3)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 214, 0.62%
## LFC < -0.50 (down) : 98, 0.28%
## outliers [1] : 0, 0%
## low counts [2] : 668, 1.9%
## (mean count < 2)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP0.2 <- as.data.frame(resLFC_SP0_2_3) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP0.2, top_n = 20)

# Clúster 3 VS Clúster 1
res_C3_vs_1 <- results(dds_SP0, contrast = c("Cluster", "3", "1"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C3_vs_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 503, 1.5%
## LFC < -0.50 (down) : 589, 1.7%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP0_3_1 <- lfcShrink(dds_SP0, contrast = c("Cluster", "3", "1"), res = res_C3_vs_1, type = "ashr")
summary(resLFC_SP0_3_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 503, 1.5%
## LFC < -0.50 (down) : 589, 1.7%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP0.3 <- as.data.frame(resLFC_SP0_3_1) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP0.3, top_n = 20)

# Heatmap de clústeres que preservan las variables biológicas
vsd_SP0 <- vst(dds_SP0, blind = FALSE)
plot_combined_heatmap(list(df_SP0.1,df_SP0.2,df_SP0.3),
vsd = vsd_SP0,
dds = dds_SP0,
top_n = 20
)

# Volcano plot de clústeres ajustados por variables biológicas
# Clúster 2 VS Clúster 1
res_C2_vs_C1 <- results(dds_SP1, contrast = c("Cluster", "2", "1"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C2_vs_C1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 829, 2.4%
## LFC < -0.50 (down) : 459, 1.3%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP1_2_1 <- lfcShrink(dds_SP1, contrast = c("Cluster", "2", "1"), res = res_C2_vs_C1, type = "ashr")
summary(resLFC_SP1_2_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 829, 2.4%
## LFC < -0.50 (down) : 459, 1.3%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP1.1 <- as.data.frame(resLFC_SP1_2_1) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP1.1, top_n = 20)

# Clúster 2 VS Clúster 3
res_C2_vs_C3 <- results(dds_SP1, contrast = c("Cluster", "2", "3"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C2_vs_C3)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 74, 0.21%
## LFC < -0.50 (down) : 67, 0.19%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP1_2_3 <- lfcShrink(dds_SP1, contrast = c("Cluster", "2", "3"), res = res_C2_vs_C3, type = "ashr")
summary(resLFC_SP1_2_3)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 74, 0.21%
## LFC < -0.50 (down) : 67, 0.19%
## outliers [1] : 0, 0%
## low counts [2] : 0, 0%
## (mean count < 0)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP1.2 <- as.data.frame(resLFC_SP1_2_3) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP1.2, top_n = 20)

# Clúster 3 VS Clúster 1
res_C3_vs_1 <- results(dds_SP1, contrast = c("Cluster", "3", "1"), alpha = 0.1, lfcThreshold = 0.5)
summary(res_C3_vs_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 5, 0.015%
## LFC < -0.50 (down) : 32, 0.093%
## outliers [1] : 0, 0%
## low counts [2] : 668, 1.9%
## (mean count < 2)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
resLFC_SP1_3_1 <- lfcShrink(dds_SP1, contrast = c("Cluster", "3", "1"), res = res_C3_vs_1, type = "ashr")
summary(resLFC_SP1_3_1)
##
## out of 34452 with nonzero total read count
## adjusted p-value < 0.1
## LFC > 0.50 (up) : 5, 0.015%
## LFC < -0.50 (down) : 32, 0.093%
## outliers [1] : 0, 0%
## low counts [2] : 668, 1.9%
## (mean count < 2)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
df_SP1.3 <- as.data.frame(resLFC_SP1_3_1) %>%
rownames_to_column("Gene.stable.ID") %>%
filter(!is.na(pvalue) & !is.na(log2FoldChange)) %>%
left_join(gene_names, by = "Gene.stable.ID")
plot_volcano(df_SP1.3, top_n = 20)

# Heatmap de clústeres ajustado por las variables biológicas
vsd_SP1 <- vst(dds_SP1, blind = FALSE)
plot_combined_heatmap(list(df_SP1.1,df_SP1.2,df_SP1.3),
vsd = vsd_SP1,
dds = dds_SP1,
top_n = 20
)

################################################################################
# GSEA
################################################################################
set.seed(123)
# Cargar bases de datos
gene_sets_c2 <- msigdbr(species = "Homo sapiens", category = "C2", subcollection = "CP:REACTOME") %>%
select(gs_name, ensembl_gene)
gene_sets_H <- msigdbr(species = "Homo sapiens", category = "H" ) %>%
select(gs_name, ensembl_gene)
gene_sets <- rbind(gene_sets_H, gene_sets_c2)
# GSEA para los contrastes enre los clústeres que preservan las variables biológicas
gse0.1 <- run_gsea(df_SP0.1, gene_sets)
gse0.2 <- run_gsea(df_SP0.2, gene_sets)
gse0.3 <- run_gsea(df_SP0.3, gene_sets)
plot_gsea_dotplot(gse0.1, plot_title = "SP0 Contraste C2vsC1")

plot_gsea_dotplot(gse0.2, plot_title = "SP0 Contraste C2vsC3")

plot_gsea_dotplot(gse0.3, plot_title = "SP0 Contraste C3vsC1")

# GSEA para los contrastes enre los clústeres ajustados por las variables biológicas
gse1.1 <- run_gsea(df_SP1.1, gene_sets)
gse1.2 <- run_gsea(df_SP1.2, gene_sets)
gse1.3 <- run_gsea(df_SP1.3, gene_sets)
plot_gsea_dotplot(gse1.1, plot_title = "SP1 Contraste C2vsC1")

plot_gsea_dotplot(gse1.2, plot_title = "SP1 Contraste C2vsC3")

plot_gsea_dotplot(gse1.3, plot_title = "SP1 Contraste C3vsC1")

sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Spain.utf8 LC_CTYPE=Spanish_Spain.utf8
## [3] LC_MONETARY=Spanish_Spain.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Spain.utf8
##
## time zone: Europe/Madrid
## tzcode source: internal
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] enrichplot_1.30.5 ggridges_0.5.7
## [3] fgsea_1.36.2 msigdbr_25.1.1
## [5] clusterProfiler_4.18.4 survminer_0.5.2
## [7] ggpubr_0.6.3 survival_3.8-6
## [9] data.table_1.18.2.1 ggrepel_0.9.7
## [11] pheatmap_1.0.13 ConsensusClusterPlus_1.74.0
## [13] limma_3.66.0 Rtsne_0.17
## [15] umap_0.2.10.0 sva_3.58.0
## [17] BiocParallel_1.44.0 genefilter_1.92.0
## [19] mgcv_1.9-4 nlme_3.1-168
## [21] apeglm_1.32.0 DESeq2_1.50.2
## [23] SummarizedExperiment_1.40.0 Biobase_2.70.0
## [25] MatrixGenerics_1.22.0 GenomicRanges_1.62.1
## [27] Seqinfo_1.0.0 IRanges_2.44.0
## [29] S4Vectors_0.48.0 BiocGenerics_0.56.0
## [31] generics_0.1.4 tximport_1.38.2
## [33] gridExtra_2.3 RColorBrewer_1.1-3
## [35] patchwork_1.3.2 matrixStats_1.5.0
## [37] lubridate_1.9.5 forcats_1.0.1
## [39] stringr_1.6.0 dplyr_1.2.0
## [41] purrr_1.2.1 readr_2.2.0
## [43] tidyr_1.3.2 tibble_3.3.1
## [45] ggplot2_4.0.2 tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] splines_4.5.2 ggplotify_0.1.3 R.oo_1.27.1
## [4] polyclip_1.10-7 XML_3.99-0.23 lifecycle_1.0.5
## [7] mixsqp_0.3-54 rstatix_0.7.3 edgeR_4.8.2
## [10] vroom_1.7.0 lattice_0.22-9 MASS_7.3-65
## [13] backports_1.5.1 magrittr_2.0.4 sass_0.4.10
## [16] rmarkdown_2.31 jquerylib_0.1.4 yaml_2.3.12
## [19] otel_0.2.0 ggtangle_0.1.2 askpass_1.2.1
## [22] reticulate_1.45.0 cowplot_1.2.0 DBI_1.3.0
## [25] abind_1.4-8 R.utils_2.13.0 yulab.utils_0.2.4
## [28] tweenr_2.0.3 rappdirs_0.3.4 gdtools_0.5.1
## [31] irlba_2.3.7 tidytree_0.4.7 RSpectra_0.16-2
## [34] annotate_1.88.0 codetools_0.2-20 DelayedArray_0.36.0
## [37] xml2_1.5.2 ggtext_0.1.2 ggforce_0.5.0
## [40] DOSE_4.4.0 tidyselect_1.2.1 aplot_0.2.9
## [43] farver_2.1.2 jsonlite_2.0.0 Formula_1.2-5
## [46] systemfonts_1.3.2 bbmle_1.0.25.1 tools_4.5.2
## [49] ggnewscale_0.5.2 treeio_1.34.0 Rcpp_1.1.1
## [52] glue_1.8.0 SparseArray_1.10.9 xfun_0.57
## [55] qvalue_2.42.0 withr_3.0.2 numDeriv_2016.8-1.1
## [58] fastmap_1.2.0 openssl_2.4.2 truncnorm_1.0-9
## [61] digest_0.6.39 timechange_0.4.0 R6_2.6.1
## [64] gridGraphics_0.5-1 GO.db_3.22.0 RSQLite_2.4.6
## [67] R.methodsS3_1.8.2 fontLiberation_0.1.0 htmlwidgets_1.6.4
## [70] httr_1.4.8 S4Arrays_1.10.1 scatterpie_0.2.6
## [73] pkgconfig_2.0.3 gtable_0.3.6 blob_1.3.0
## [76] S7_0.2.1 XVector_0.50.0 htmltools_0.5.9
## [79] fontBitstreamVera_0.1.1 carData_3.0-6 scales_1.4.0
## [82] png_0.1-8 ashr_2.2-63 ggfun_0.2.0
## [85] knitr_1.51 rstudioapi_0.18.0 tzdb_0.5.0
## [88] reshape2_1.4.5 curl_7.0.0 coda_0.19-4.1
## [91] bdsmatrix_1.3-7 cachem_1.1.0 parallel_4.5.2
## [94] AnnotationDbi_1.72.0 pillar_1.11.1 grid_4.5.2
## [97] vctrs_0.7.1 tidydr_0.0.6 car_3.1-5
## [100] xtable_1.8-8 cluster_2.1.8.2 evaluate_1.0.5
## [103] invgamma_1.2 mvtnorm_1.3-3 cli_3.6.5
## [106] locfit_1.5-9.12 compiler_4.5.2 rlang_1.1.7
## [109] crayon_1.5.3 SQUAREM_2026.1 ggsignif_0.6.4
## [112] labeling_0.4.3 emdbook_1.3.14 plyr_1.8.9
## [115] fs_2.1.0 ggiraph_0.9.6 stringi_1.8.7
## [118] viridisLite_0.4.3 babelgene_22.9 assertthat_0.2.1
## [121] Biostrings_2.78.0 lazyeval_0.2.3 GOSemSim_2.36.0
## [124] fontquiver_0.2.1 Matrix_1.7-4 hms_1.1.4
## [127] bit64_4.6.0-1 KEGGREST_1.50.0 statmod_1.5.1
## [130] gridtext_0.1.6 igraph_2.3.1 broom_1.0.13
## [133] memoise_2.0.1 bslib_0.11.0 ggtree_4.0.5
## [136] fastmatch_1.1-8 bit_4.6.0 gson_0.1.0
## [139] ape_5.8-1