An R package for testing differential protein abundance from AP-DIA-MS (Affinity Purification - Data-Independent Acquisition Mass Spectrometry) datasets.
DiffTestR provides convenience functions for complete differential expression analysis workflows, from data import through statistical testing to visualization. The package handles precursor-level quantification data, performs robust normalization and imputation, and conducts statistical tests with protein-level rollup.
- Flexible data import from DIA-NN or custom table formats
- Customizable normalization (quantile, VSN, median, or custom methods)
- Frequency-based filtering to remove low-quality measurements
- Smart missing value imputation using low-abundance distributions
- Statistical testing at precursor level with protein-level summarization
- Comprehensive visualizations including volcano plots, heatmaps, and interactive HTML reports
- Quality control plots for normalization effects, sample correlations, and imputation diagnostics
# Install from GitHub (requires devtools)
devtools::install_github("heuselm/DiffTestR")
# Or install locally
devtools::install_local("path/to/DiffTestR")The package requires:
- R >= 3.50
data.table,ggplot2,corrplot,pheatmap,ggrepel,reshape2limma(for normalization and statistics)crosstalk,htmlwidgets,plotly(for interactive visualizations)
library(DiffTestR)
# Import DIA-NN results (first run creates a study design template)
qdata <- importFromDIANN(
path_to_result = "path/to/DIANN_results.tsv",
quant_value_col = "Precursor.Quantity",
study_design = "path/to/Study_design_filled.tsv"
)Study Design Format: Create a tab-separated file with columns:
filename: Must match raw file names in your datacondition: Experimental condition (e.g., "treated", "control")replicate: Replicate number (integer)
Example:
filename condition replicate
sample01 control 1
sample02 control 2
sample03 treated 1
sample04 treated 2
# Run complete differential expression analysis
results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
study_design = "path/to/Study_design_filled.tsv",
condition_1 = "treated",
condition_2 = "control",
min_n_obs = 4, # Minimum observations per precursor
normalize_data = TRUE, # Apply normalization
normalization_function = limma::normalizeQuantiles,
imp_percentile = 0.001, # Imputation centered at 0.1% quantile
imp_sd = 0.2, # Imputation standard deviation
plot_pdf = TRUE, # Generate diagnostic PDFs
write_tsv_tables = TRUE, # Write result tables
target_protein = "Q9Y6K9" # Optional: highlight specific protein
)# Create overview plots for multiple differential tests
# (After combining results from multiple testDifferentialAbundance runs)
overview <- plotDifferentialAbundanceOverview(
diffExpr_result_dt = combined_results,
significance_threshold_p = 0.05,
significance_threshold_fc = 2, # 2-fold change threshold
browsable_html = TRUE, # Create interactive HTML plots
heatmap = TRUE # Generate fold-change heatmap
)qdata <- importFromDIANN(
path_to_result = "DIANN_results.tsv",
quant_value_col = "Precursor.Quantity", # or "Precursor.Normalised"
write_study_design_template = TRUE,
study_design = "Study_design_filled.tsv"
)The function returns a list containing:
data_long: Long-format data tabledata_wide: Wide-format data tablematrix_raw: Raw intensity matrixann_col: Column annotationsann_row: Row annotations (protein/precursor info)
qdata <- importFromTables(
path_to_tsv = "precursor_matrix_wide.txt",
precursor_annotation = "precursor_annotation.txt",
study_design = "Study_design.txt"
)The testDifferentialAbundance function performs:
- Data loading and filtering to samples in study design
- Log2 transformation of intensities
- Normalization (default: quantile normalization)
- Quality control plots: distributions, correlations, heatmaps
- Filtering by minimum observations (
min_n_obs) - Missing value imputation from low-abundance distribution
- T-tests at precursor level
- Protein-level rollup with Benjamini-Hochberg correction
- Volcano plots at precursor and protein levels
results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
protein_group_annotation = NULL, # Auto-extracted if NULL
study_design = "Study_design.txt",
normalize_data = TRUE,
normalization_function = limma::normalizeQuantiles, # Try also: normalizeVSN, normalizeMedianValues
condition_1 = "treated",
condition_2 = "control",
min_n_obs = 4,
imp_percentile = 0.001,
imp_sd = 0.2,
plot_pdf = TRUE,
write_tsv_tables = TRUE,
target_protein = "O08760"
)Output Files Generated:
DiffTest_result.tsv: Complete results tableNormalization_impact_abundance_density.pdf: Normalization diagnosticsHeatmap_*.pdf: Various heatmaps showing data qualityCorrelation_sample_sample_*.pdf: Sample correlation plotsFiltering_min_obs.pdf: Distribution of observationsImputation_*.pdf: Imputation diagnosticsVolcano_plot_*.pdf: Volcano plots at precursor and protein levels
Results Object Contains:
data_source: Input file pathdata_long: Long-format processed datadata_matrix_log2: Filtered, log2-transformed datadata_matrix_log2_imp: Imputed data matrixstudy_design: Study design tableannotation_col: Column annotationsdiffExpr_result_dt: Full results with statisticscandidates_condition1: Significantly upregulated in condition 1candidates_condition2: Significantly upregulated in condition 2
# Combine results from multiple comparisons
all_results <- rbind(
results1$diffExpr_result_dt,
results2$diffExpr_result_dt,
results3$diffExpr_result_dt
)
# Create overview plots
overview <- plotDifferentialAbundanceOverview(
diffExpr_result_dt = all_results,
significance_threshold_p = 0.05,
significance_threshold_fc = 2,
label_prefix = "MyExperiment",
remove_from_protein_names = "\\(Bos;|_MOUSE|_HUMAN",
browsable_html = TRUE,
protein_highlight_tag = "IGSeq",
heatmap = TRUE
)Output Files:
MyExperiment.pdf: Multi-panel volcano plotsMyExperiment_interactive_v1.html: Basic interactive plotMyExperiment_interactive_v2.html: Interactive with cross-panel highlightingMyExperiment_heatmap_diffProtFcProfiles.pdf: Fold-change heatmap
# Use VSN normalization instead of quantile
results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
study_design = "Study_design.txt",
normalize_data = TRUE,
normalization_function = limma::normalizeVSN
)
# Skip normalization (e.g., for pulldown vs input comparisons)
results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
study_design = "Study_design.txt",
normalize_data = FALSE
)results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
study_design = "Study_design.txt",
min_n_obs = 6, # More stringent filtering
imp_percentile = 0.01, # Center imputation at 1% quantile
imp_sd = 0.3 # Wider imputation distribution
)# View top differentially abundant proteins
head(results$candidates_condition1)
head(results$candidates_condition2)
# Access full results table
diff_table <- results$diffExpr_result_dt
# Get specific proteins
target_results <- diff_table[Protein.Names %like% "MYC"]
# Export filtered candidates
write.table(results$candidates_condition1,
"upregulated_proteins.tsv",
sep = "\t",
quote = FALSE,
row.names = FALSE)- Replicates: Include at least 3 biological replicates per condition for robust statistics
- Study Design: Ensure filenames in study design exactly match those in your data
- Normalization: For pulldown experiments comparing bait vs control, consider setting
normalize_data = FALSE - Filtering: Adjust
min_n_obsbased on your total sample number and expected data completeness - Imputation: Lower
imp_percentilevalues impute more conservatively (lower abundance) - Quality Control: Always review the generated PDFs to assess data quality and processing steps
- Reproducibility: The analysis uses
set.seed(123)for reproducible imputation
library(DiffTestR)
# 1. Import data
qdata <- importFromDIANN(
path_to_result = "data/DIANN_results.tsv",
study_design = "data/Study_design.tsv"
)
# 2. Test differential abundance
results <- testDifferentialAbundance(
input_dt = qdata$data_wide,
study_design = "data/Study_design.tsv",
condition_1 = "KO",
condition_2 = "WT",
min_n_obs = 4,
plot_pdf = TRUE
)
# 3. Examine results
print(paste("Found", nrow(results$candidates_condition1), "proteins upregulated in KO"))
print(paste("Found", nrow(results$candidates_condition2), "proteins upregulated in WT"))
# 4. Create overview (if multiple comparisons)
overview <- plotDifferentialAbundanceOverview(
diffExpr_result_dt = results$diffExpr_result_dt,
label_prefix = "KO_vs_WT"
)If you use DiffTestR in your research, please cite:
Heusel, M. DiffTestR: Testing Differential Expression from AP-DIA-MS datasets.
R package version 0.0.0.9000. https://github.com/heuselm/DiffTestR
Moritz Heusel (moritz.heusel@med.lu.se) ORCID: 0000-0002-8506-530X
MIT License