Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
docs
/doc/
/Meta/
*.Rproj
2 changes: 1 addition & 1 deletion R/houseman.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#'
#' @param methyl_set A minfi MethylSet
#' @param array type of methylation array that was used. possible options are '450k' and 'EPIC'
#' @param compositeCellType Which composite cell type is being deconvoluted. Should be one of "Blood", "CordBloodCombined", "CordBlood", "CordBloodNorway", "CordTissueAndBlood", or "DLPFC". See details for preferred approaches.
#' @param compositeCellType Which composite cell type is being deconvoluted. Should be one of "Blood", "CordBloodCombined", "CordBlood", "CordBloodNorway", "CordTissueAndBlood", or "DLPFC".
#' @param processMethod Joint normalization/background correction for user and reference data. For MethylSet objects only "preprocessQuantile" is available. Set it to any minfi preprocessing function as a character if you want to override it, like "preprocessFunnorm"
#' @param probeSelect How should probes be selected to distinguish cell types? Options include: 1) "IDOL", (default) for using a customized set of probes obtained from IDOL optimization, available for Blood and Umbilical Cord Blood 2) "both", which selects an equal number (50) of probes (with F-stat p-value < 1E-8) with the greatest magnitude of effect from the hyper- and hypo-methylated sides, and 3) "any", which selects the 100 probes (with F-stat p-value < 1E-8) with the greatest magnitude of difference regardless of direction of effect. This according to minfi algorithm. Default input "auto" in minfi will use "any" for cord blood and "both" otherwise. Please see references for more details.
#' @param cellTypes A vector of length K that contains the cell type names. Default: c("CD8T", "CD4T", "NK", "Bcell", "Mono", "Neu"). Please notice that this library use Neutrophils instead of Granulocytes. See details for your library.
Expand Down
45 changes: 43 additions & 2 deletions R/main.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,48 @@ deconvolute <- function(methyl_set, method=deconvolution_methods, scale_results
return(result)
}

#' Run selected set of methods and average results
#' Run multiple deconvolution methods and create aggregated results
#'
#' This function runs multiple cell-type deconvolution methods on the same methylation data and creates both individual method results and an aggregated (averaged) estimate. The aggregation approach can help reduce method-specific biases and provide more robust cell-type proportion estimates.
#'
#' \strong{How it works:}
#' \itemize{
#' \item Runs each specified method independently on the methylation data
#' \item Standardizes cell-type names across methods using \code{rename_cell_types()}
#' \item For MethylResolver specifically, combines Tnaive and Tmem into "T cell CD4+" to match other methods
#' \item Calculates the mean estimate for each cell type across all methods (aggregated results)
#' \item Returns both individual method results and aggregated results in a long-format data frame
#' }
#'
#' \strong{Cell-type standardization:}
#' The function uses \code{rename_cell_types()} to standardize cell-type names across different methods. This mapping includes:
#' \itemize{
#' \item CD8T/CD8/CD8T-cells_EPIC → "T cell CD8+"
#' \item CD4T/CD4T-cells_EPIC → "T cell CD4+"
#' \item B/Bcell/B-cells_EPIC → "B cell"
#' \item NK/NK-cells_EPIC → "NK cell"
#' \item Mono/Mon/Monocytes_EPIC → "Monocyte"
#' \item Neu/Neutro/Neutrophils/Neutrophils_EPIC → "Neutrophil"
#' \item Any unrecognized cell types → "other"
#' }
#'
#' \strong{Meaning of 'other' cell types:}
#' The "other" category includes:
#' \itemize{
#' \item Cell types that are not in the standardized mapping above
#' \item Method-specific cell types that don't have direct equivalents in other methods
#' \item Rare or specialized cell populations that are only detected by certain methods
#' }
#'
#' \strong{Limitations of the aggregation approach:}
#' \itemize{
#' \item \strong{Method heterogeneity:} Different methods use different algorithms, reference datasets, and cell-type definitions, which may not be directly comparable
#' \item \strong{Missing data handling:} If a method fails to estimate a particular cell type, it may be excluded from the aggregation, potentially biasing results
#' \item \strong{Equal weighting:} The current implementation gives equal weight to all methods, regardless of their individual performance or reliability
#' \item \strong{Cell-type coverage:} Methods may detect different sets of cell types, leading to incomplete coverage in aggregated results
#' \item \strong{No confidence intervals:} The aggregation provides point estimates without uncertainty quantification
#' }
#'
#'
#' @param methyl_set A minfi MethylSet
#' @param array type of methylation array that was used. possible options are '450k' and 'EPIC'
Expand All @@ -85,7 +126,7 @@ deconvolute <- function(methyl_set, method=deconvolution_methods, scale_results
#' Defaults to FALSE.
#' @param ... Additional parameters, passed to the algorithm used. See individual method documentations for details.
#'
#' @return dataframe with results of all selected methods as well as the combined estimates
#' @return A data frame with columns: sample, method, celltype, value. Contains results from all individual methods plus an 'aggregated' method that averages the estimates across methods.
#' @export
#' @examples
#'
Expand Down
5 changes: 3 additions & 2 deletions R/methatlas.R
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ run_methatlas <- function(beta_matrix, reference_atlas = system.file("reference_
#' Get MethAtlas Signature Matrix
#'
#' Returns the reference matrix used by MethAtlas from a CSV file.
#' @param reference_atlas Path to a csv file (default: package reference_atlas.csv)
#'
#' @return Signature matrix as tibble with CpGs in rows (column 'CpGs') and cell types in columns
#' @export
get_methatlas_signature_matrix <- function(reference_atlas = system.file("reference_atlas.csv", package = "deconvMe")) {
get_methatlas_signature_matrix <- function() {
reference_atlas = system.file("reference_atlas.csv", package = "deconvMe")
read.csv(reference_atlas, check.names = FALSE)
}

3 changes: 3 additions & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ articles:
- getting_started
- visualization
- signatures
- combined_methods
- title: "Contributing"
contents:
- contribute
Expand All @@ -29,6 +30,8 @@ navbar:
href: articles/visualization.html
- text: "Signature Matrices & Custom CpGs"
href: articles/signatures.html
- text: "Combined Methods Approach"
href: articles/combined_methods.html
- text: "Contribute"
href: articles/contribute.html

18 changes: 17 additions & 1 deletion deconvMe.Rproj
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@

Version: 1.0

RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default

EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8

RnwWeave: Sweave
LaTeX: pdfLaTeX

BuildType: Package
PackageUseDevtools: Yes
PackageInstallArgs: --no-multiarch --with-keep.source
45 changes: 42 additions & 3 deletions man/deconvolute_combined.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 7 additions & 9 deletions tests/testthat/test_others.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# result <- deconvMe::deconvolute(methyl_set = methyl_set, method = 'epidish')

suppressMessages(library(deconvMe))
options(matrixStats.useNames.NA = "deprecated")

# ----------------------
# EpiDISH tests
Expand Down Expand Up @@ -104,23 +105,21 @@ test_that("run_houseman warns and proceeds if some CpGs are missing, errors if a
# ----------------------

test_that("get_methatlas_signature_matrix returns a data frame with expected structure", {
ref_path <- system.file("reference_atlas.csv", package = "deconvMe")
sig <- deconvMe::get_methatlas_signature_matrix(ref_path)
sig <- deconvMe::get_methatlas_signature_matrix()
expect_s3_class(sig, "data.frame")
expect_true(nrow(sig) > 0)
expect_true(ncol(sig) > 0)
expect_true("CpGs" %in% colnames(sig))
})

test_that("run_methatlas supports cpg_subset argument", {
ref_path <- system.file("reference_atlas.csv", package = "deconvMe")
sig <- deconvMe::get_methatlas_signature_matrix(ref_path)
sig <- deconvMe::get_methatlas_signature_matrix()
subset_cpgs <- sig$CpGs[1:10]
beta <- matrix(runif(10 * 3), nrow = 10, ncol = 3)
rownames(beta) <- subset_cpgs
colnames(beta) <- paste0("Sample", 1:3)
expect_error(
deconvMe::run_methatlas(beta, reference_atlas = ref_path, cpg_subset = subset_cpgs),
deconvMe::run_methatlas(beta, cpg_subset = subset_cpgs),
NA
)
})
Expand All @@ -130,19 +129,18 @@ test_that("run_methatlas supports cpg_subset argument", {
# ----------------------

test_that("run_methatlas warns and proceeds if some CpGs are missing, errors if all are missing", {
ref_path <- system.file("reference_atlas.csv", package = "deconvMe")
sig <- deconvMe::get_methatlas_signature_matrix(ref_path)
sig <- deconvMe::get_methatlas_signature_matrix()
subset_cpgs <- sig$CpGs[1:5]
missing_cpgs <- paste0("cg_missing_", 1:5)
beta <- matrix(runif(10 * 3), nrow = 10, ncol = 3)
rownames(beta) <- c(subset_cpgs, missing_cpgs)
colnames(beta) <- paste0("Sample", 1:3)
expect_warning(
deconvMe::run_methatlas(beta, reference_atlas = ref_path, cpg_subset = c(subset_cpgs, missing_cpgs)),
deconvMe::run_methatlas(beta, cpg_subset = c(subset_cpgs, missing_cpgs)),
"not present in the reference atlas"
)
expect_error(
deconvMe::run_methatlas(beta, reference_atlas = ref_path, cpg_subset = missing_cpgs),
deconvMe::run_methatlas(beta, cpg_subset = missing_cpgs),
"None of the specified cpg_subset CpGs are present"
)
})
Expand Down
131 changes: 131 additions & 0 deletions vignettes/combined_methods.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: "Combined Methods Approach in deconvMe"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Combined Methods Approach in deconvMe}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

# Combined Methods Approach in deconvMe

The `deconvolute_combined()` function in deconvMe allows you to run multiple cell-type deconvolution methods simultaneously and create aggregated results. This approach can help reduce method-specific biases and provide more robust estimates of cell-type proportions.

## Basic Usage

```{r, message=FALSE, warning=FALSE}
library(deconvMe)
library(minfiData)
options(matrixStats.useNames.NA = "deprecated")

# Example data
methyl_set <- minfiData::MsetEx

# Run multiple methods
result_combined <- deconvolute_combined(
methyl_set = methyl_set,
methods = c('epidish', 'houseman', 'methylcc'),
array = '450k'
)
```

## Understanding the Output

The combined results include both individual method results and aggregated estimates:

```{r}
# View the structure
head(result_combined)

# Check available methods
unique(result_combined$method)

# Check available cell types
unique(result_combined$celltype)
```

## Cell-Type Standardization

The function automatically standardizes cell-type names across methods:

```{r}
# Example of cell-type mapping
library(dplyr)

# Show how cell types are mapped
cell_type_mapping <- data.frame(
Original = c("CD8T", "CD4T", "B", "NK", "Mono", "Neu", "Unknown"),
Standardized = c("T cell CD8+", "T cell CD4+", "B cell", "NK cell", "Monocyte", "Neutrophil", "other")
)
knitr::kable(cell_type_mapping, caption = "Cell-type standardization mapping")
```

## The "Other" Category

The "other" category includes:
- Cell types not in the standardized mapping
- Method-specific cell types without equivalents
- Rare or specialized cell populations

This category should be interpreted carefully, as it may represent:
- True rare cell types
- Method-specific artifacts
- Incomplete cell-type coverage

## Visualizing Combined Results

```{r, fig.height=6, fig.width=10, message=FALSE, warning=FALSE}
# Create visualization of individual vs aggregated results
library(ggplot2)

# Filter for one sample to show the comparison
sample_data <- result_combined[result_combined$sample == result_combined$sample[1], ]

ggplot(sample_data, aes(x = celltype, y = value, fill = method)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Individual Methods vs Aggregated Results",
subtitle = paste("Sample:", sample_data$sample[1]),
y = "Estimated Fraction",
x = "Cell Type") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
```

## Limitations and Considerations

### 1. Method Heterogeneity
Different methods use different algorithms and reference datasets, which may not be directly comparable. Consider:
- Whether methods are validated for your tissue type
- The quality and relevance of reference datasets
- Algorithm-specific assumptions

### 2. Equal Weighting
The current implementation gives equal weight to all methods. This may not be optimal if:
- Some methods are more reliable for your specific use case
- Methods have different levels of validation
- You have prior knowledge about method performance

### 3. Missing Data
If a method fails to estimate a particular cell type, it may be excluded from aggregation, potentially biasing results.

### 4. Cell-Type Coverage
Methods may detect different sets of cell types, leading to incomplete coverage in aggregated results.

## Quality Control
```{r}
# Check for consistency across methods
consistency_check <- result_combined %>%
group_by(sample, celltype) %>%
summarise(
mean_value = mean(value),
sd_value = sd(value),
cv = sd_value / mean_value, # coefficient of variation
.groups = 'drop'
) %>%
filter(!is.na(mean_value))

# Identify cell types with high variability across methods
high_variability <- consistency_check %>%
filter(cv > 0.5) # arbitrary threshold
```

16 changes: 15 additions & 1 deletion vignettes/getting_started.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,18 @@ The result of the deconvolution is a table with the estimated cell-type fraction
knitr::kable(head(result), caption = "Estimated cell-type fractions for the first few samples")
```

For more details, see the package documentation and other vignettes!
For more details, see the package documentation and other vignettes!

# Overview of Included Methods

Below is a template table summarizing the deconvolution methods included in deconvMe. This table lists each method, the type of algorithm it uses, the intended tissue types, and the type of data used to build the internal deconvolution reference.

| Name | Algorithm Type(s) | Intended Tissue Type(s) | Reference Source Data |
|:--------------|:-------------------------|:----------------------------|:------------------------------------------------------|
| EpiDISH | Robust Partial Correlations (RPC), Constrained Projection (CP), Support Vector Regression (SVR) | Blood, Epithelial, Breast | purified blood, epithelial, and non-epithelial data, extended with DNAse Hypersensitivity Sites from Roadmap and ENCODE |
| Houseman | Constrained projection/Quadratic Programming (CP/QP) | Blood| Purified blood cell methylation profiles (IDOL-optimized) |
| MethylCC | Constrained Linear model based on DMRs | Blood (in deconvMe, can be extended to other tissues) | purified blood |
| MethylResolver | Least Trimmed Squares (LTS) regression | Blood, cancer | Purified blood methylation profiles (IDOL-optimized), extended with additional leukocytes |
| MethAtlas | non-negative least squares (NNLS) | Blood, immune, tissue-wide | Comprehensive atlas of purified tissue and immune cell methylomes |

*This table can be expanded as new methods are added to the package.*
Loading
Loading