---
title: "GSEAlens：从 limma-voom 和 DESeq2 流程准备输入数据"
author: "Shenhui Xu"
date: "`r Sys.Date()`"
output:
  BiocStyle::html_document:
    toc_float: true
    number_sections: true
vignette: >
  %\VignetteIndexEntry{GSEAlens：准备输入数据}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
```

# 安装

GSEAlens 可从 Bioconductor 安装。使用以下命令安装稳定版本：

```{r install-package, eval=FALSE}
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("GSEAlens")
```

开发版本可从 GitHub 安装：

```{r install-github, eval=FALSE}
if (!requireNamespace("pak", quietly = TRUE))
    install.packages("pak")
pak::pkg_install("DDL095/GSEAlens")
```

# 简介

本 vignette 描述如何准备 `r Biocpkg("GSEAlens")` 所需的输入对象。**GSEAlens 本身
不执行差异表达分析（DEG）**；它接收来自 `r Biocpkg("limma")` 的已拟合 `MArrayLM`
对象或来自 `r Biocpkg("DESeq2")` 的 `DESeqDataSet` 对象。以下步骤基于这些包的标准
工作流程，此处提供是为了完整性。
完整的 DEG 工作流程文档请参阅：

- `r Biocpkg("limma")` 和 `r Biocpkg("edgeR")` 的 vignette（limma-voom 流程）

- `r Biocpkg("DESeq2")` 的 vignette

- `r Biocpkg("airway")` 包提供示例数据集

# 示例数据

我们使用 `r Biocpkg("airway")` 数据集（4 vs 4 样本，dex 处理）。

```{r setup-environment, results='hide'}
library(airway)
data(airway)
expression_data <- airway
```

# limma-voom 流程

GSEAlens 要求**无截距设计**（`~0+group`），使列名直接对应组别名称，从而精确构建
对比。

```{r limma-voom-data-preparation}
library(edgeR)
library(limma)
group_level <- expression_data$dex
design <- model.matrix(~0+group_level)
colnames(design) <- levels(group_level)
compare_end <- combn(levels(group_level), 2, simplify = FALSE)
contrast_strings <- sapply(compare_end, function(x) paste(x[2], x[1], sep = " - "))
contrast_matrix <- makeContrasts(contrasts = contrast_strings, levels = design)
genes_df <- data.frame(
  gene_id = SummarizedExperiment::rowData(expression_data)$gene_id,
  symbol = SummarizedExperiment::rowData(expression_data)$symbol,
  gene_biotype = SummarizedExperiment::rowData(expression_data)$gene_biotype
)
genes_df$Length <- SummarizedExperiment::rowData(expression_data)$gene_seq_end -
                   SummarizedExperiment::rowData(expression_data)$gene_seq_start + 1
gsea_limma_voom_data <- edgeR::DGEList(
  counts = SummarizedExperiment::assay(expression_data, "counts"),
  genes = genes_df,
  norm.factors = NULL,
  group = group_level,
  remove.zeros = TRUE
)
# 过滤低表达基因，保留蛋白质编码 RNA，去重复 symbol
total_counts <- edgeR::cpm(gsea_limma_voom_data) |> rowSums()
dup_symbols <- gsea_limma_voom_data$genes$symbol[duplicated(gsea_limma_voom_data$genes$symbol)]
keep <- rep(TRUE, nrow(gsea_limma_voom_data))
for (gene in dup_symbols) {
  idx <- which(gsea_limma_voom_data$genes$symbol == gene)
  best_idx <- idx[which.max(total_counts[idx])]
  remove_idx <- idx[idx != best_idx]
  keep[remove_idx] <- FALSE
}
gsea_limma_voom_data <- gsea_limma_voom_data[keep, ]
rownames(gsea_limma_voom_data) <- gsea_limma_voom_data$genes$symbol
keep_biotype <- gsea_limma_voom_data$genes$gene_biotype == "protein_coding"
gsea_limma_voom_data <- gsea_limma_voom_data[keep_biotype, ]
gsea_limma_voom_data <- edgeR::normLibSizes(gsea_limma_voom_data, method = "TMM")
isexpr <- rowSums(edgeR::cpm(gsea_limma_voom_data) > 1) >= 3
gsea_limma_voom_data <- gsea_limma_voom_data[isexpr, ]
```

进行 limma-voom 拟合，获取 `fit` 对象。

```{r limma-voom-processing}
VoomOutPut <- voom(gsea_limma_voom_data, design)
fit <- lmFit(object = VoomOutPut, design = design) |>
  contrasts.fit(contrasts = contrast_matrix) |>
  eBayes()
```

# DESeq2 流程（SummarizedExperiment 输入）

```{r deseq2-se-workflow}
library("DESeq2")
dds_se <- DESeqDataSet(expression_data, design = ~ cell + dex)
# 仅保留蛋白编码基因
gene_biotypes <- SummarizedExperiment::rowData(dds_se)$gene_biotype
keep_protein_coding <- gene_biotypes == "protein_coding"
dds_se <- dds_se[keep_protein_coding, ]
# 去除低表达基因（要求 >=10 reads 且至少出现在 >=3 个样本中）
smallestGroupSize <- 3
keep <- rowSums(DESeq2::counts(dds_se) >= 10) >= smallestGroupSize
dds_se <- dds_se[keep, ]
# 基因符号去重，保留表达量最高的那一行
rownames(dds_se) <- SummarizedExperiment::rowData(dds_se)$gene_name
total_counts <- rowSums(SummarizedExperiment::assay(dds_se))
dup_genes <- rownames(dds_se)[duplicated(rownames(dds_se))]
keep <- rep(TRUE, nrow(dds_se))
for (gene in dup_genes) {
  idx <- which(rownames(dds_se) == gene)
  best_idx <- idx[which.max(total_counts[idx])]
  remove_idx <- idx[idx != best_idx]
  keep[remove_idx] <- FALSE
}
dds_se <- dds_se[keep, ]
# 运行 DESeq2 拟合
dds_se <- DESeq(dds_se)
```

# DESeq2 流程（Count Matrix 输入）

```{r deseq2-matrix-workflow}
DDS_rawdata <- expression_data
# 仅保留蛋白编码基因
gene_biotypes <- SummarizedExperiment::rowData(DDS_rawdata)$gene_biotype
keep_protein_coding <- gene_biotypes == "protein_coding"
DDS_rawdata <- DDS_rawdata[keep_protein_coding, ]
# 去除低表达基因
smallestGroupSize <- 3
keep_epd <- rowSums(SummarizedExperiment::assay(DDS_rawdata, "counts") >= 10) >= smallestGroupSize
DDS_rawdata <- DDS_rawdata[keep_epd, ]
# 基因符号去重
rownames(DDS_rawdata) <- SummarizedExperiment::rowData(DDS_rawdata)$gene_name
total_counts <- rowSums(SummarizedExperiment::assay(DDS_rawdata))
keep_name <- rep(TRUE, nrow(DDS_rawdata))
dup_genes <- rownames(DDS_rawdata)[duplicated(rownames(DDS_rawdata))]
for (gene in dup_genes) {
  idx <- which(rownames(DDS_rawdata) == gene)
  best_idx <- idx[which.max(total_counts[idx])]
  remove_idx <- idx[idx != best_idx]
  keep_name[remove_idx] <- FALSE
}
DDS_rawdata <- DDS_rawdata[keep_name, ]
# 抽取 counts 矩阵和样本元数据
cts <- SummarizedExperiment::assay(DDS_rawdata, "counts")
coldata <- as.data.frame(SummarizedExperiment::colData(DDS_rawdata))
coldata <- coldata[, c("cell", "dex")]
coldata$cell <- factor(coldata$cell)
coldata$dex <- factor(coldata$dex)
# 从矩阵构建 DESeqDataSet 并运行 DESeq2
dds <- DESeqDataSetFromMatrix(countData = cts, colData = coldata, design = ~ dex)
dds <- DESeq(dds)
```

# 下一步

`fit`、`dds_se` 和 `dds` 准备就绪后，返回主 vignette：

```r

vignette("GSEAlens")

```

```{r sessionInfo}
sessionInfo()
```
