Skip to content
Open
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
23 changes: 14 additions & 9 deletions inmoose/diffexp/meta.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Copyright (C) 2024 M. Colange

# This program is free software: you can redistribute it and/or modify
Expand All @@ -13,8 +13,9 @@

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------

from typing import List, Optional
import numpy as np
import pandas as pd
from scipy.stats import combine_pvalues, false_discovery_control
Expand All @@ -23,7 +24,11 @@
from .DEResults import DEResults


def meta_de(de_results, alpha=0.05, min_common_genes=None):
def meta_de(
de_results: List[DEResults],
alpha: float = 0.05,
min_common_genes: Optional[int] = None
) -> pd.DataFrame:
"""
Combine logFC and *p*-values of differential expression analyses

Expand All @@ -42,13 +47,13 @@ def meta_de(de_results, alpha=0.05, min_common_genes=None):
the use-case, it can be results obtained with different tools on the
same dataset, results obtained with the same tool on different
datasets, or any combination thereof
alpha : float between 0 and 1
significance level for the confidence intervals. Defaults to 0.05.
min_common_genes : int or None
alpha : float, optional
significance level for the confidence intervals, by default 0.05.
min_common_genes : int, optional
minimal number of genes all the elements of :code:`de_results` need to
have in common. Below this threshold, an error will be raised. If
:code:`None`, then all elements of :code:`de_results` must have the
same set of genes.
same set of genes, by default None.

Returns
-------
Expand All @@ -58,7 +63,7 @@ def meta_de(de_results, alpha=0.05, min_common_genes=None):
- :code:`"combined logFC"`: the combined log-fold-change
- :code:`"combined logFC (CI_L)"`: the lower bound of the confidence
interval for the combined log-fold-change
- :code:`"combined logFC (CI_R)"`: the lower bound of the confidence
- :code:`"combined logFC (CI_R)"`: the upper bound of the confidence
interval for the combined log-fold-change
- :code:`"adjusted combined pval"`: the combined *p*-value, adjusted
for multiple testing
Expand Down Expand Up @@ -112,4 +117,4 @@ def meta_de(de_results, alpha=0.05, min_common_genes=None):
)
res["adjusted combined pval"] = meta_adj_pvals

return res
return res
41 changes: 21 additions & 20 deletions inmoose/pycombat/pycombat_seq.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Copyright (C) 2019-2020 Yuqing Zhang
# Copyright (C) 2022-2023 Maximilien Colange

Expand All @@ -14,10 +14,11 @@

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------

# This file is based on the file 'R/ComBat_seq.R' of the Bioconductor sva package (version 3.44.0).

from typing import Union, Optional, Any
import numpy as np
import pandas as pd

Expand All @@ -28,15 +29,15 @@


def pycombat_seq(
counts,
batch,
covar_mod=None,
shrink=False,
shrink_disp=False,
gene_subset_n=None,
ref_batch=None,
na_cov_action="raise",
):
counts: Union[np.ndarray, pd.DataFrame],
batch: Union[list, np.ndarray, str],
covar_mod: Optional[Union[list, np.ndarray, pd.DataFrame]] = None,
shrink: bool = False,
shrink_disp: bool = False,
gene_subset_n: Optional[int] = None,
ref_batch: Optional[Any] = None,
na_cov_action: str = "raise",
) -> Union[np.ndarray, pd.DataFrame]:
"""pycombat_seq is an improved model from ComBat using negative binomial regression, which specifically targets RNA-Seq count data.

Arguments
Expand All @@ -47,14 +48,14 @@ def pycombat_seq(
Batch indices. Must have as many elements as the number of columns in the expression matrix.
covar_mod : list or matrix, optional
model matrix (dataframe, list or numpy array) for one or multiple covariates to include in linear model (signal
from these variables are kept in data after adjustment). Covariates have to be categorial,
they can not be continious values (default: `None`).
from these variables are kept in data after adjustment). Covariates have to be categorical,
they can not be continuous values (default: `None`).
shrink : bool, optional
whether to apply shrinkage on parameter estimation
whether to apply shrinkage on parameter estimation (default: `False`)
shrink_disp : bool, optional
whether to apply shrinkage on dispersion
whether to apply shrinkage on dispersion (default: `False`)
gene_subset_n : int, optional
number of genes to use in emprirical Bayes estimation, only useful when shrink = True
number of genes to use in empirical Bayes estimation, only useful when shrink = True (default: `None`)
ref_batch : any, optional
batch id of the batch to use as reference (default: `None`)
na_cov_action : str
Expand All @@ -70,7 +71,7 @@ def pycombat_seq(
-------
matrix
the input expression matrix adjusted for batch effects.
same type as the input `data`
same type as the input `counts`
"""

####### Preparation #######
Expand Down Expand Up @@ -113,7 +114,7 @@ def pycombat_seq(
LOGGER.info("Estimating dispersions")

# Estimate common dispersion within each batch as an initial value
def disp_common_helper(i):
def disp_common_helper(i: str) -> float:
if (
batch_sizes[i] <= design.shape[1] - batchmod.shape[1] + 1
or np.linalg.matrix_rank(mod[batches_ind[i]]) < mod.shape[1]
Expand All @@ -133,7 +134,7 @@ def disp_common_helper(i):
disp_common = {b: disp_common_helper(b) for b in batch.categories}

# Estimate gene-wise dispersion within each batch
def genewise_disp_helper(i):
def genewise_disp_helper(i: str) -> list:
if (
batch_sizes[i] <= design.shape[1] - batchmod.shape[1] + 1
or np.linalg.matrix_rank(mod[batches_ind[i]]) < mod.shape[1]
Expand Down Expand Up @@ -232,4 +233,4 @@ def genewise_disp_helper(i):
elif vci.input_type == "dataframe":
return pd.DataFrame(adjust_counts_whole, columns=list_samples, index=list_genes)
else:
return adjust_counts_whole
return adjust_counts_whole
60 changes: 49 additions & 11 deletions inmoose/utils/factor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Copyright (C) 2022-2023 M. Colange

# This program is free software: you can redistribute it and/or modify
Expand All @@ -13,9 +13,9 @@

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------

# ------------------------------------------------------------------------------

from typing import Union, Any, List
from pandas import Categorical


Expand All @@ -27,7 +27,7 @@ class Factor(Categorical):
methods to mimic R API.
"""

def __init__(self, arr):
def __init__(self, arr: Union[List[Any], tuple, 'Factor']) -> None:
"""
Constructs a Factor instance from an array

Expand All @@ -39,30 +39,68 @@ def __init__(self, arr):

super().__init__(arr)

def droplevels(self):
def droplevels(self) -> 'Factor':
"""
drop unused levels
Drop unused levels

Returns
-------
Factor
a new Factor object with unused levels removed
"""

return Factor(self.__array__())

def nlevels(self):
def nlevels(self) -> int:
"""
the number of levels
Get the number of levels

Returns
-------
int
the number of levels
"""

return len(self.categories)


def asfactor(g):
def asfactor(g: Union[List[Any], tuple, 'Factor']) -> 'Factor':
"""
Convert an array-like object to a Factor

Parameters
----------
g : array_like
The object to convert to a Factor

Returns
-------
Factor
A Factor object based on the input
"""
if type(g) is Factor:
return g
else:
return Factor(g)


def gl(n, k):
def gl(n: int, k: int) -> 'Factor':
"""
Generate factor levels

Parameters
----------
n : int
The number of levels
k : int
The number of replications for each level

Returns
-------
Factor
A Factor object with generated levels
"""
arr = []
for i in range(1, n + 1):
arr.extend([i for j in range(k)])
return Factor(arr)
return Factor(arr)