---
title: "MSstatsResponse: A package for detecting drug-protein interactions in dose-response mass spectrometry-based proteomics experiments"
author: "Sarah Szvetecz (szvetecz.s@northeastern.edu), Devon Kohler (kohler.d@northeastern.edu), Tony Wu (wu.anthon@northeastern.edu), Swaraj Chandrakant Patil (patil.swaraj@northeastern.edu), Olga Vitek (o.vitek@northeastern.edu)"
date: "`r Sys.Date()`"
output:
  BiocStyle::html_document:
    toc: true
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{MSstatsResponse User Guide}
  %\VignetteEncoding{UTF-8}
  %\VignetteEngine{knitr::rmarkdown}
editor_options:
  markdown:
    wrap: sentence
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5
)
```

## Introduction

MSstatsResponse provides statistical tools for detecting drug-protein interactions and estimating IC50 values from dose-response mass spectrometry-based proteomics experiments.
The package implements isotonic regression models to detect monotonic dose-response relationships and provides robust statistical inference through F-tests and bootstrap confidence intervals.

MSstatsResponse is designed to work with summarized data from MSstats and MSstatsTMT workflows, but can also process other dose-response proteomics datasets that include protein-level quantifications across drug treatments.

### Statistical framework

The package uses isotonic regression, a non-parametric method that fits monotonic functions without assuming a specific functional form.
This approach is particularly suitable for dose-response data where monotonicity (increasing or decreasing trend) is expected.

### Analysis workflow

MSstatsResponse implements the following statistical analysis pipeline:

1. **Data preparation**: `MSstatsPrepareDoseResponseFit()` - Convert input data to standardized format
2. **Interaction detection**: `doseResponseFit()` - Detect drug-protein interactions using isotonic regression and F-tests
3. **IC50 estimation**: `predictIC50()` - Estimate IC50 values with bootstrap confidence intervals
4. **Experimental design planning**: `futureExperimentSimulation()` and `run_tpr_simulation()` - Simulate dose-response experiments to determine optimal study designs

## Installation and setup


### Install MSstatsResponse

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

BiocManager::install("MSstatsResponse")

```

### Load required libraries

```{r load_libraries, message=FALSE}
library(MSstatsResponse)
library(dplyr)
library(ggplot2)
library(data.table)

# Optional: for upstream data processing
# library(MSstats)
# library(MSstatsTMT)
```

## Data preprocessing with MSstats

When analyzing a mass spectrometry-based proteomics dataset, data should be preprocessed using the standard MSstats workflow for cleaning, normalization, and protein summarization prior to analysis with MSstatsResponse.

When using MSstats, data can be converted with one of the available converter functions (e.g., `SpectronauttoMSstatsFormat()`) and subsequently processed with `dataProcess()`.

For MSstatsTMT, converter functions (e.g., `MaxQtoMSstatsTMTFormat()`) may be used, followed by `proteinSummarization()` for data processing.

Please refer to the MSstats and MSstatsTMT Bioconductor vignettes for a comprehensive list of supported converters and detailed preprocessing recommendations.

### Example MSstats workflow

```{r msstats_preprocessing, eval=FALSE}
# Read raw data (example with Spectronaut output)
raw_data <- read_tsv("path/to/spectronaut_report.tsv")

# Convert to MSstats format
msstats_data <- SpectronauttoMSstatsFormat(raw_data)

# Process data: normalization and protein summarization
processed_data <- dataProcess(
  msstats_data,
  normalization = "equalizeMedians",
  summaryMethod = "TMP",
  MBimpute = TRUE,
  maxQuantileforCensored = 0.999
)

# Extract protein-level data for dose-response analysis
protein_level_data <- processed_data$ProteinLevelData
```

### Loading example data

For this vignette, we use pre-processed data prepared following the steps described above.
For more information on preprocessing mass spectrometry-based proteomics experiments, see the vignettes for MSstats and/or MSstatsTMT.

```{r load_example_data}
data("DIA_MSstats_Normalized", package = "MSstatsResponse")
dia_normalized <- DIA_MSstats_Normalized

str(dia_normalized$ProteinLevelData[1:5, ])
```

## Data preparation for dose-response analysis

### Converting GROUP labels to numeric doses

The `convertGroupToNumericDose()` function parses MSstats-style GROUP labels to extract drug names and numeric dose values.

**Format expectations:**

- Control samples: `"DMSO"` (assigned dose = 0)
- Treatment samples: `"DrugName_ValueUnit"` (e.g., `"Drug1_003uM"`, `"Drug2_300nM"`)

```{r convert_doses}
dose_info <- convertGroupToNumericDose(dia_normalized$ProteinLevelData$GROUP)

dia_normalized$ProteinLevelData <- dia_normalized$ProteinLevelData %>%
  mutate(
    dose_nM = dose_info$dose_nM,
    drug = dose_info$drug
  )

dia_normalized$ProteinLevelData %>%
  select(Protein, GROUP, drug, dose_nM) %>%
  head(10)
```

### Formatting data with MSstatsPrepareDoseResponseFit()

This function standardizes the data structure for downstream analysis.
The `dose_column` corresponds to the x-axis values (e.g., dose, concentration, time, or temperature), the `protein_column` should contain unique identifiers (e.g., protein, gene, or peptide sequence), and the `log_abundance_column` represents the response values on the y-axis (e.g., log intensity, expression, or growth).

```{r prepare_data}
dia_prepared <- MSstatsPrepareDoseResponseFit(
  data = dia_normalized$ProteinLevelData,
  dose_column = "dose_nM",
  drug_column = "drug",
  protein_column = "Protein",
  log_abundance_column = "LogIntensities",
  transform_nM_to_M = TRUE
)

str(dia_prepared)

dia_prepared %>%
  filter(protein %in% c("PROTEIN_A", "PROTEIN_B")) %>%
  arrange(protein, drug, dose) %>%
  head(20)
```

## Drug-protein interaction detection

### Isotonic regression

Isotonic regression fits a monotonic function to the data without assuming a parametric form.
For inhibitor data, we typically expect a non-increasing response: drug binding reduces protein abundance monotonically with dose.

### Running doseResponseFit()

The `doseResponseFit()` function:

1. Fits isotonic regression for each protein-drug pair
2. Performs an F-test comparing the fitted model to a flat (null) model
3. Adjusts p-values for multiple testing using the Benjamini-Hochberg procedure

```{r fit_dose_response, message=FALSE, warning=FALSE}
response_results <- doseResponseFit(
  data = dia_prepared,
  increasing = FALSE,
  transform_dose = TRUE,
  ratio_response = FALSE
)

response_results %>%
  select(Protein, drug, F_statistic, log2FC, adj.pvalue) %>%
  arrange(adj.pvalue) %>%
  head(10)
```

### Interpreting results

- **log2FC**: Log2 fold change between the highest and lowest doses
- **F_statistic**: Ratio of between-dose to within-dose variance
- **P_value**: Raw p-value from F-test
- **adj.pvalue**: FDR-adjusted p-value (Benjamini-Hochberg)

Proteins with `adj.pvalue < 0.05` show significant dose-dependent responses.

```{r summarize_results}
n_total <- nrow(response_results)
n_significant <- sum(response_results$adj.pvalue < 0.05)

cat("Total protein-drug pairs tested:", n_total, "\n")
cat("Significant interactions (FDR < 0.05):", n_significant, "\n")
cat("Percentage significant:", round(100 * n_significant / n_total, 1), "%\n")

top_hits <- response_results %>%
  filter(adj.pvalue < 0.05) %>%
  arrange(adj.pvalue) %>%
  head(5)

print(top_hits)
```

## IC50 estimation

### Understanding IC50 calculation

IC50 represents the dose at which the response is reduced to 50% of the control (DMSO) level.
MSstatsResponse estimates IC50 by:

1. Fitting isotonic regression on the ratio scale (response relative to DMSO)
2. Finding the dose where the fitted response equals 0.5
3. Computing confidence intervals via bootstrap resampling

### Running predictIC50()

```{r predict_ic50, message=FALSE, warning=FALSE}
ic50_predictions <- predictIC50(
  data = dia_prepared,
  ratio_response = TRUE,
  transform_dose = TRUE,
  increasing = FALSE,
  bootstrap = TRUE,
  n_samples = 1000,
  alpha = 0.10
)

ic50_predictions %>%
  arrange(IC50) %>%
  head(10)
```

### Parallel processing for large datasets

For datasets with many proteins, use parallel processing via BiocParallel:

```{r parallel_ic50, eval=FALSE}
library(BiocParallel)
if (.Platform$OS.type == "windows") {
  register(SnowParam(workers = 4, type = "SOCK"))
} else {
  register(MulticoreParam(workers = 4))
}

ic50_parallel <- predictIC50(
  data = dia_prepared,
  ratio_response = TRUE,
  transform_dose = TRUE,
  bootstrap = TRUE,
  n_samples = 1000,
  BPPARAM = bpparam()
)
```


## Visualization

### Individual protein dose-response curves

The `visualizeResponseProtein()` function creates dose-response plots:

```{r visualize_single, fig.height=5, fig.width=7}
visualizeResponseProtein(
  data = dia_prepared,
  protein_name = "PROTEIN_A",
  drug_name = "Drug1",
  ratio_response = TRUE,
  show_ic50 = TRUE,
  add_ci = TRUE,
  transform_dose = TRUE,
  n_samples = 1000
)
```

```{r visualize_another, fig.height=5, fig.width=7}
visualizeResponseProtein(
  data = dia_prepared,
  protein_name = "PROTEIN_B",
  drug_name = "Drug1",
  ratio_response = TRUE,
  show_ic50 = TRUE,
  add_ci = TRUE
)
```

### Comparing log2 vs ratio scale

```{r compare_scales, fig.height=4, fig.width=12}
p1 <- visualizeResponseProtein(
  data = dia_prepared,
  protein_name = "PROTEIN_A",
  drug_name = "Drug1",
  ratio_response = FALSE,
  show_ic50 = TRUE,
  add_ci = FALSE
)

p2 <- visualizeResponseProtein(
  data = dia_prepared,
  protein_name = "PROTEIN_A",
  drug_name = "Drug1",
  ratio_response = TRUE,
  show_ic50 = TRUE,
  add_ci = FALSE
)

gridExtra::grid.arrange(p1, p2, ncol = 2)
```

### Predicting at other response levels

The `target_response` argument allows prediction at any response level (default 0.50).
This example predicts IC25 with confidence intervals:

```{r visualize_ic25, fig.height=5, fig.width=7}
visualizeResponseProtein(
  data = dia_prepared,
  protein_name = "PROTEIN_B",
  drug_name = "Drug1",
  ratio_response = TRUE,
  show_ic50 = TRUE,
  add_ci = TRUE,
  transform_dose = TRUE,
  target_response = 0.25
)
```

## Experimental design planning

### Simulating future experiments

`futureExperimentSimulation()` evaluates detection performance under a user-defined experimental design by simulating a full experiment and measuring true-positive and false-positive rates.
Proteins from your own data can be used as templates for more realistic simulations.

```{r future_experiment, message=FALSE, warning=FALSE}
# pull unique doses 
dose_levels = as.numeric(unique(dia_prepared$dose))

# run simulation
custom_simulation <- futureExperimentSimulation(
  N_proteins = 300,
  N_rep = 2,
  N_Control_Rep = 2,
  Concentrations = dose_levels,
  data = dia_prepared,
  strong_proteins = "PROTEIN_B",
  weak_proteins = "PROTEIN_A",
  no_interaction_proteins = "PROTEIN_C",
  drug_name = "Drug1",
  IC50_Prediction = FALSE
)

print(custom_simulation$Hit_Rates_Plot)
print(custom_simulation$Template_Used)
```

### Sweeping experimental design parameters

`run_tpr_simulation()` sweeps over combinations of replicate counts and number of concentration levels, measuring the true positive rate (TPR) for each design configuration.
Control (0) and the highest dose are always included; intermediate doses are selected by greedily choosing the concentration farthest from the log-median of the already-selected set.

Use `plot_tpr_power_curve()` to visualize the resulting power curves.

```{r tpr_power_curve, eval=TRUE, message=FALSE, warning=FALSE}
power_results <- run_tpr_simulation(
  rep_range = c(2,4),
  concentrations = dose_levels,
  dose_range = c(2, 9),
  data = dia_prepared,
  protein = "PROTEIN_A",
  n_proteins = 300
)

plot_tpr_power_curve(power_results)

```



## Session information

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

## References

1. Szvetecz, S., et al. (2026). MSstatsResponse: Semi-parametric statistical model enhances drug-protein interaction detection and IC50 estimation in chemoproteomics experiments. *Manuscript in preparation.*

2. Choi, M., et al. (2014). MSstats: an R package for statistical analysis of quantitative mass spectrometry-based proteomic experiments. *Bioinformatics*, 30(17), 2524-2526.
