diff --git a/setup.py b/setup.py index fb9d8e1..2946b1f 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ "typing_extensions", "rich", "dill", + "pytest", ], python_requires=">=3.5, <3.12", classifiers=[ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..5658143 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,40 @@ +import pytest +import numpy as np +import pandas as pd +import networkx as nx +import scanpy as sc + + +@pytest.fixture(scope="session") +def fake_grn(): + G = nx.barbell_graph(5, 1) + G = nx.relabel_nodes(G, {i: str(i) for i in G}) + return G + + +@pytest.fixture(scope="session") +def fake_snp_score(): + snp_score = {"6": 1, "7": 1, "8": 1, "3": 1, "5": 0} + return snp_score + + +@pytest.fixture(scope="session") +def fake_adata(fake_grn): + df = pd.DataFrame( + 0, + index=[str(i) for i in np.arange(90)], + columns=[str(i) for i in np.arange(len(fake_grn))], + ) + df.iloc[:30, [0, 2, 3]] = 100 + df.iloc[30:60, [2, 5, 9]] = 100 + df.iloc[60:90, [8, 9, 10]] = 100 + + ad = sc.AnnData(df) + ad.obs["cell type"] = ["A"] * 30 + ["B"] * 30 + ["C"] * 30 + + sc.pp.normalize_total(ad) + sc.pp.log1p(ad) + ad.raw = ad + sc.pp.scale(ad) + + return ad diff --git a/tests/test_toy_example.py b/tests/test_toy_example.py new file mode 100644 index 0000000..7ac6ae8 --- /dev/null +++ b/tests/test_toy_example.py @@ -0,0 +1,67 @@ +import snp2cell + +snp2cell.util.set_num_cpu(1) + + +def test_toy_example(fake_grn, fake_adata, fake_snp_score): + s2c = snp2cell.SNP2CELL() + s2c.add_grn_from_networkx(fake_grn) + + assert s2c is not None, "snp2cell object was not created" + assert s2c.grn is not None, "networkx object was not added to snp2cell object" + + # add SNP score, propagate, calculate statistics + s2c.add_score(score_dct=fake_snp_score, score_key="snp_score") + + assert ( + "snp_score" in s2c.scores + ), "original snp_score was not added to snp2cell object" + assert ( + "snp_score" in s2c.scores_prop + ), "propagated snp_score was not added to snp2cell object" + assert ( + "snp_score" in s2c.scores_rand + ), "permuted snp_scores were not added to snp2cell object" + assert ( + "snp_score__pval" in s2c.scores_prop + ), "statistics were not calculated for snp_score" + + # add DE scores, propagate, calculate statistics + s2c.link_adata(fake_adata) + s2c.adata_add_de_scores(groupby="cell type", check=False) + + assert s2c.adata is not None, "anndata object was not added to snp2cell object" + for grp in fake_adata.obs["cell type"].unique().tolist(): + assert ( + grp in s2c.de_groups["cell type"] + ), f"cell type {grp} was not added to snp2cell object" + assert ( + f"DE_{grp}__score" in s2c.scores + ), f"original DE score for {grp} was not added to snp2cell object" + assert ( + f"DE_{grp}__score" in s2c.scores_prop + ), f"propagated DE score for {grp} was not added to snp2cell object" + + assert ( + f"DE_cell type__score" in s2c.scores_rand + ), f"permuted DE scores for 'cell type' were not added to snp2cell object" + + for grp in fake_adata.obs["cell type"].unique().tolist(): + assert ( + f"DE_{grp}__score__pval" in s2c.scores_prop + ), f"statistics were not calculated for {grp}" + + # combine each DE score with the SNP score + s2c.adata_combine_de_scores( + group_key="cell type", score_key="snp_score", suffix="__zscore" + ) + + assert ( + "min(DE_C__score__zscore,snp_score__zscore)" in s2c.scores_prop + ), "DE score and SNP score were not combined" + + sum_a = s2c.scores_prop["min(DE_A__score__zscore,snp_score__zscore)"].sum() + sum_b = s2c.scores_prop["min(DE_B__score__zscore,snp_score__zscore)"].sum() + sum_c = s2c.scores_prop["min(DE_C__score__zscore,snp_score__zscore)"].sum() + + assert sum_c > sum_b > sum_a, "computed values are not correct"