Files

259 lines
8.6 KiB
R
Raw Permalink Normal View History

2026-08-03 18:42:47 +08:00
#!/usr/bin/env Rscript
#============================================================================
# DESeq2-based FPKM/TPM Calculation Module (Simplified Version)
# Version: 1.0.0
# DESeq2 Version: 1.42.0 (Bioconductor 3.17)
#
# This version uses simple GTF parsing without GenomicFeatures dependency
#============================================================================
# Parse command line arguments
args <- commandArgs(trailingOnly = TRUE)
# Default values
count_file <- NULL
gtf_file <- NULL
output_dir <- NULL
prefix <- "rnaseq"
# Parse arguments
for (i in seq(1, length(args), 2)) {
if (i < length(args)) {
arg_name <- args[i]
arg_value <- args[i + 1]
if (arg_name == "--count") count_file <- arg_value
if (arg_name == "--gtf") gtf_file <- arg_value
if (arg_name == "--output") output_dir <- arg_value
if (arg_name == "--prefix") prefix <- arg_value
}
}
# Validate arguments
if (is.null(count_file) || is.null(gtf_file) || is.null(output_dir)) {
cat("Usage: Rscript calculate_fpkm_tpm_simple.R [options]\n")
cat("Options:\n")
cat(" --count Path to count matrix file (TSV format)\n")
cat(" --gtf Path to GTF annotation file\n")
cat(" --output Output directory\n")
cat(" --prefix Output file prefix (default: rnaseq)\n")
quit(status = 1)
}
# Record start time and session info
start_time <- Sys.time()
cat("=" ,rep("=", 60), "\n", sep="")
cat("DESeq2 FPKM/TPM Calculation Module (Simplified)\n")
cat("=" ,rep("=", 60), "\n", sep="")
cat("Start time:", format(start_time), "\n\n")
# Load required libraries
suppressPackageStartupMessages({
library(DESeq2)
})
# Print session info for reproducibility
cat("Session Info:\n")
print(sessionInfo())
cat("\n")
#============================================================================
# Step 1: Read count matrix
#============================================================================
cat("Step 1: Reading count matrix...\n")
countdata <- read.table(
count_file,
header = TRUE,
row.names = 1,
sep = "\t",
check.names = FALSE,
quote = "",
comment.char = ""
)
cat(" Count matrix dimensions:", nrow(countdata), "genes x", ncol(countdata), "samples\n")
cat(" Sample names:", paste(colnames(countdata), collapse = ", "), "\n\n")
#============================================================================
# Step 2: Extract gene lengths from GTF (simple parsing)
#============================================================================
cat("Step 2: Extracting gene lengths from GTF...\n")
# Simple GTF parsing - extract gene lengths from exon features
gtf_data <- read.table(gtf_file, sep = "\t", comment.char = "",
stringsAsFactors = FALSE, quote = "",
col.names = c("seqname", "source", "feature", "start", "end",
"score", "strand", "frame", "attribute"))
# Filter for exon features
exons <- gtf_data[gtf_data$feature == "exon", ]
# Extract gene_id from attributes
gene_ids <- gsub('.*gene_id "([^"]+)".*', '\\1', exons$attribute)
# Calculate exon lengths
exon_lengths <- exons$end - exons$start + 1
# Sum exon lengths by gene
gene_length_list <- tapply(exon_lengths, gene_ids, sum)
# Convert to data frame
gene_length_df <- data.frame(
gene_id = names(gene_length_list),
length = as.numeric(gene_length_list),
stringsAsFactors = FALSE
)
cat(" Total genes in GTF:", nrow(gene_length_df), "\n")
cat(" Gene length range:", min(gene_length_df$length), "-", max(gene_length_df$length), "bp\n\n")
#============================================================================
# Step 3: Match genes between count matrix and GTF
#============================================================================
cat("Step 3: Matching genes...\n")
# Find common genes
common_genes <- intersect(rownames(countdata), gene_length_df$gene_id)
cat(" Genes in count matrix:", nrow(countdata), "\n")
cat(" Genes with length info:", length(common_genes), "\n")
cat(" Match rate:", round(length(common_genes) / nrow(countdata) * 100, 2), "%\n\n")
# Filter to common genes
countdata_filtered <- countdata[common_genes, , drop = FALSE]
gene_length_filtered <- gene_length_df$length[match(common_genes, gene_length_df$gene_id)]
names(gene_length_filtered) <- common_genes
#============================================================================
# Step 4: Create DESeq2 object and calculate normalized counts
#============================================================================
cat("Step 4: Creating DESeq2 object...\n")
# Create sample data (dummy design since we only need normalization)
sample_data <- data.frame(
sample = colnames(countdata_filtered),
condition = rep("A", ncol(countdata_filtered)),
row.names = colnames(countdata_filtered),
stringsAsFactors = FALSE
)
# Create DESeqDataSet
dds <- DESeqDataSetFromMatrix(
countData = round(countdata_filtered),
colData = sample_data,
design = ~ 1 # No design, just for normalization
)
# Calculate size factors (normalization)
dds <- estimateSizeFactors(dds)
cat(" Size factors:", paste(round(sizeFactors(dds), 4), collapse = ", "), "\n\n")
#============================================================================
# Step 5: Calculate FPKM
#============================================================================
cat("Step 5: Calculating FPKM...\n")
# Get normalized counts
normalized_counts <- counts(dds, normalized = TRUE)
# Calculate FPKM
# FPKM = (Normalized Counts / Gene Length in kb) / (Total Normalized Counts / 10^6)
gene_lengths_kb <- gene_length_filtered / 1000
fpkm_matrix <- t(t(normalized_counts) / gene_lengths_kb) / (colSums(normalized_counts) / 1e6)
cat(" FPKM matrix dimensions:", nrow(fpkm_matrix), "x", ncol(fpkm_matrix), "\n")
cat(" FPKM range:", round(min(fpkm_matrix), 6), "-", round(max(fpkm_matrix), 2), "\n\n")
#============================================================================
# Step 6: Calculate TPM
#============================================================================
cat("Step 6: Calculating TPM...\n")
# Calculate TPM
# TPM = (Normalized Counts / Gene Length in kb) * (10^6 / sum(Normalized Counts / Gene Length in kb))
rate <- normalized_counts / gene_lengths_kb
tpm_matrix <- t(t(rate) / colSums(rate)) * 1e6
cat(" TPM matrix dimensions:", nrow(tpm_matrix), "x", ncol(tpm_matrix), "\n")
cat(" TPM range:", round(min(tpm_matrix), 6), "-", round(max(tpm_matrix), 2), "\n\n")
# Verify TPM sums to 1 million
cat(" TPM column sums (should be ~1e6):\n")
print(round(colSums(tpm_matrix), 2))
cat("\n")
#============================================================================
# Step 7: Save output files
#============================================================================
cat("Step 7: Saving output files...\n")
# Create output directory if not exists
if (!dir.exists(output_dir)) {
dir.create(output_dir, recursive = TRUE)
}
# Save FPKM matrix
fpkm_file <- file.path(output_dir, paste0(prefix, ".fpkm_matrix.txt"))
write.table(
data.frame(gene_id = rownames(fpkm_matrix), fpkm_matrix, check.names = FALSE),
fpkm_file,
sep = "\t",
row.names = FALSE,
quote = FALSE
)
cat(" FPKM saved to:", fpkm_file, "\n")
# Save TPM matrix
tpm_file <- file.path(output_dir, paste0(prefix, ".tpm_matrix.txt"))
write.table(
data.frame(gene_id = rownames(tpm_matrix), tpm_matrix, check.names = FALSE),
tpm_file,
sep = "\t",
row.names = FALSE,
quote = FALSE
)
cat(" TPM saved to:", tpm_file, "\n")
# Save gene lengths
gene_length_file <- file.path(output_dir, paste0(prefix, ".gene_lengths.txt"))
write.table(
gene_length_df,
gene_length_file,
sep = "\t",
row.names = FALSE,
quote = FALSE
)
cat(" Gene lengths saved to:", gene_length_file, "\n")
# Save normalization info
norm_info_file <- file.path(output_dir, paste0(prefix, ".normalization_info.txt"))
norm_info <- data.frame(
sample = colnames(countdata_filtered),
size_factor = sizeFactors(dds),
total_counts = colSums(countdata_filtered),
total_normalized_counts = colSums(normalized_counts)
)
write.table(
norm_info,
norm_info_file,
sep = "\t",
row.names = FALSE,
quote = FALSE
)
cat(" Normalization info saved to:", norm_info_file, "\n\n")
#============================================================================
# Step 8: Record completion
#============================================================================
end_time <- Sys.time()
elapsed <- as.numeric(difftime(end_time, start_time, units = "secs"))
cat("=" ,rep("=", 60), "\n", sep="")
cat("Calculation completed successfully!\n")
cat("End time:", format(end_time), "\n")
cat("Elapsed time:", round(elapsed, 2), "seconds\n")
cat("=" ,rep("=", 60), "\n", sep="")
# Return success
quit(status = 0)