bayesprism 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LuJoHae
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: bayesprism
3
+ Version: 0.1.0
4
+ Summary: Pure Functional Python / PyTorch implementation of BayesPrism for Bayesian cell type and gene expression deconvolution
5
+ Author: LuJoHae
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/LuJoHae/bayesprism-python
8
+ Project-URL: Repository, https://github.com/LuJoHae/bayesprism-python
9
+ Project-URL: Issues, https://github.com/LuJoHae/bayesprism-python/issues
10
+ Keywords: bioinformatics,deconvolution,single-cell,bayesprism,pytorch,rna-seq
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: torch>=2.0.0
24
+ Requires-Dist: polars>=0.20.0
25
+ Requires-Dist: scipy>=1.10.0
26
+ Requires-Dist: numpy>=1.24.0
27
+ Requires-Dist: pydantic>=2.5.0
28
+ Requires-Dist: returns>=0.22.0
29
+ Requires-Dist: altair>=5.0.0
30
+ Requires-Dist: vl-convert-python>=1.0.0
31
+ Requires-Dist: scikit-learn>=1.3.0
32
+ Dynamic: license-file
33
+
34
+ # BayesPrism (Python)
35
+
36
+ A pure functional Python and PyTorch implementation of **BayesPrism** for Bayesian cell type and gene expression deconvolution using single-cell RNA-seq references.
37
+
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
39
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
40
+
41
+ ---
42
+
43
+ ## Overview
44
+
45
+ BayesPrism infers the cellular composition and cell-type-specific gene expression profiles of heterogeneous bulk RNA-seq data by leveraging single-cell transcriptomics as a prior.
46
+
47
+ This package is a high-performance Python port built upon:
48
+ - **PyTorch** for accelerated matrix computations and GPU tensor operations.
49
+ - **Polars** for fast, declarative data processing.
50
+ - **Pure Functional Design** ensuring immutable state transitions, railway-oriented error handling via `returns`, and strict type safety.
51
+
52
+ ---
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install bayesprism
58
+ ```
59
+
60
+ Or install directly from GitHub:
61
+
62
+ ```bash
63
+ pip install git+https://github.com/LuJoHae/bayesprism-python.git
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Quick Start
69
+
70
+ ```python
71
+ import numpy as np
72
+ from bayesprism import new_prism, run_prism, get_fraction, get_exp
73
+
74
+ # 1. Prepare raw count matrices (samples x genes, cell_types x genes)
75
+ # Bulk mixture: X (samples x genes)
76
+ # scRNA-seq reference: phi_ref (cell_types x genes)
77
+ gene_names = ("CD3D", "CD8A", "CD4", "MS4A1", "CD68", ...)
78
+
79
+ # 2. Initialize the Prism model container
80
+ prism_result = new_prism(
81
+ X_bulk=X_bulk_counts,
82
+ phi_ref=phi_sc_reference,
83
+ gene_names=gene_names,
84
+ )
85
+
86
+ # Unwrap monadic Result safely
87
+ prism = prism_result.unwrap()
88
+
89
+ # 3. Run Bayesian MCMC deconvolution
90
+ fitted_prism = run_prism(prism)
91
+
92
+ # 4. Extract inferred cell type fractions (theta)
93
+ fractions = get_fraction(fitted_prism)
94
+
95
+ # 5. Extract cell-type-specific expression profiles (Z)
96
+ expression_profiles = get_exp(fitted_prism)
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Key Features
102
+
103
+ - **Bayesian Joint Estimation**: Jointly infers cell-type proportions ($\theta$) and cell-type-specific gene expression ($Z$).
104
+ - **Linear Count Space**: Operates directly on unnormalized raw sequencing counts to preserve count distributions without log-transformation artifacts.
105
+ - **Batched GPU Acceleration**: PyTorch tensors enable fast vectorized updates across large patient cohorts.
106
+ - **Modular Pipeline**: Full functional pipeline with separate Gibbs sampling, Nelder-Mead reference updating, and NMF embedding learning.
107
+
108
+ ---
109
+
110
+ ## Citation & Acknowledgments
111
+
112
+ If you use BayesPrism in your research, please cite the original BayesPrism methodology:
113
+
114
+ > Chu, T., Wang, Z., Pe'er, D., & Danko, C. G. (2022). Cell type and gene expression deconvolution with BayesPrism. *Nature Cancer*, 3(4), 505-517. [doi:10.1038/s43018-022-00356-3](https://doi.org/10.1038/s43018-022-00356-3)
115
+
116
+ ---
117
+
118
+ ## License
119
+
120
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,87 @@
1
+ # BayesPrism (Python)
2
+
3
+ A pure functional Python and PyTorch implementation of **BayesPrism** for Bayesian cell type and gene expression deconvolution using single-cell RNA-seq references.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ BayesPrism infers the cellular composition and cell-type-specific gene expression profiles of heterogeneous bulk RNA-seq data by leveraging single-cell transcriptomics as a prior.
13
+
14
+ This package is a high-performance Python port built upon:
15
+ - **PyTorch** for accelerated matrix computations and GPU tensor operations.
16
+ - **Polars** for fast, declarative data processing.
17
+ - **Pure Functional Design** ensuring immutable state transitions, railway-oriented error handling via `returns`, and strict type safety.
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install bayesprism
25
+ ```
26
+
27
+ Or install directly from GitHub:
28
+
29
+ ```bash
30
+ pip install git+https://github.com/LuJoHae/bayesprism-python.git
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ import numpy as np
39
+ from bayesprism import new_prism, run_prism, get_fraction, get_exp
40
+
41
+ # 1. Prepare raw count matrices (samples x genes, cell_types x genes)
42
+ # Bulk mixture: X (samples x genes)
43
+ # scRNA-seq reference: phi_ref (cell_types x genes)
44
+ gene_names = ("CD3D", "CD8A", "CD4", "MS4A1", "CD68", ...)
45
+
46
+ # 2. Initialize the Prism model container
47
+ prism_result = new_prism(
48
+ X_bulk=X_bulk_counts,
49
+ phi_ref=phi_sc_reference,
50
+ gene_names=gene_names,
51
+ )
52
+
53
+ # Unwrap monadic Result safely
54
+ prism = prism_result.unwrap()
55
+
56
+ # 3. Run Bayesian MCMC deconvolution
57
+ fitted_prism = run_prism(prism)
58
+
59
+ # 4. Extract inferred cell type fractions (theta)
60
+ fractions = get_fraction(fitted_prism)
61
+
62
+ # 5. Extract cell-type-specific expression profiles (Z)
63
+ expression_profiles = get_exp(fitted_prism)
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Key Features
69
+
70
+ - **Bayesian Joint Estimation**: Jointly infers cell-type proportions ($\theta$) and cell-type-specific gene expression ($Z$).
71
+ - **Linear Count Space**: Operates directly on unnormalized raw sequencing counts to preserve count distributions without log-transformation artifacts.
72
+ - **Batched GPU Acceleration**: PyTorch tensors enable fast vectorized updates across large patient cohorts.
73
+ - **Modular Pipeline**: Full functional pipeline with separate Gibbs sampling, Nelder-Mead reference updating, and NMF embedding learning.
74
+
75
+ ---
76
+
77
+ ## Citation & Acknowledgments
78
+
79
+ If you use BayesPrism in your research, please cite the original BayesPrism methodology:
80
+
81
+ > Chu, T., Wang, Z., Pe'er, D., & Danko, C. G. (2022). Cell type and gene expression deconvolution with BayesPrism. *Nature Cancer*, 3(4), 505-517. [doi:10.1038/s43018-022-00356-3](https://doi.org/10.1038/s43018-022-00356-3)
82
+
83
+ ---
84
+
85
+ ## License
86
+
87
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "bayesprism"
3
+ version = "0.1.0"
4
+ description = "Pure Functional Python / PyTorch implementation of BayesPrism for Bayesian cell type and gene expression deconvolution"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "LuJoHae" }
9
+ ]
10
+ requires-python = ">=3.10"
11
+ keywords = ["bioinformatics", "deconvolution", "single-cell", "bayesprism", "pytorch", "rna-seq"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Science/Research",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
22
+ ]
23
+ dependencies = [
24
+ "torch>=2.0.0",
25
+ "polars>=0.20.0",
26
+ "scipy>=1.10.0",
27
+ "numpy>=1.24.0",
28
+ "pydantic>=2.5.0",
29
+ "returns>=0.22.0",
30
+ "altair>=5.0.0",
31
+ "vl-convert-python>=1.0.0",
32
+ "scikit-learn>=1.3.0",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/LuJoHae/bayesprism-python"
37
+ Repository = "https://github.com/LuJoHae/bayesprism-python"
38
+ Issues = "https://github.com/LuJoHae/bayesprism-python/issues"
39
+
40
+ [build-system]
41
+ requires = ["setuptools>=61.0"]
42
+ build-backend = "setuptools.build_meta"
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,53 @@
1
+ from bayesprism.models import (
2
+ RefPhi,
3
+ RefTumor,
4
+ Prism,
5
+ GibbsControl,
6
+ OptControl,
7
+ JointPost,
8
+ ThetaPost,
9
+ BayesPrism,
10
+ BayesPrismST,
11
+ )
12
+ from bayesprism.validation import validate_input
13
+ from bayesprism.preprocessing import norm_to_one, collapse, filter_bulk_outlier
14
+ from bayesprism.gibbs import run_gibbs, rdirichlet
15
+ from bayesprism.optimization import update_reference, transform_phi_t
16
+ from bayesprism.embedding import learn_embedding_nmf, run_EM
17
+ from bayesprism.pipeline import (
18
+ new_prism,
19
+ run_prism,
20
+ run_prism_st,
21
+ get_fraction,
22
+ get_exp,
23
+ )
24
+ from bayesprism.plotting import plot_cor_phi, export_chart_svg
25
+
26
+ __all__ = [
27
+ "RefPhi",
28
+ "RefTumor",
29
+ "Prism",
30
+ "GibbsControl",
31
+ "OptControl",
32
+ "JointPost",
33
+ "ThetaPost",
34
+ "BayesPrism",
35
+ "BayesPrismST",
36
+ "validate_input",
37
+ "norm_to_one",
38
+ "collapse",
39
+ "filter_bulk_outlier",
40
+ "run_gibbs",
41
+ "rdirichlet",
42
+ "update_reference",
43
+ "transform_phi_t",
44
+ "learn_embedding_nmf",
45
+ "run_EM",
46
+ "new_prism",
47
+ "run_prism",
48
+ "run_prism_st",
49
+ "get_fraction",
50
+ "get_exp",
51
+ "plot_cor_phi",
52
+ "export_chart_svg",
53
+ ]
@@ -0,0 +1,146 @@
1
+ from typing import Any, Union
2
+ import numpy as np
3
+ import torch
4
+ from sklearn.decomposition import NMF
5
+ from returns.result import Result, Success, Failure
6
+ from returns.maybe import Maybe, Some, Nothing
7
+
8
+ from bayesprism.models import (
9
+ BayesPrism,
10
+ RefTumor,
11
+ GibbsControl,
12
+ OptControl,
13
+ NmfControl,
14
+ )
15
+ from bayesprism.preprocessing import norm_to_one
16
+ from bayesprism.optimization import optimize_psi_map
17
+
18
+
19
+ def compute_elbo(
20
+ opt_value: float,
21
+ psi_env: np.ndarray,
22
+ joint_post_z: torch.Tensor,
23
+ gibbs_constant: float,
24
+ ) -> float:
25
+ """Compute Evidence Lower Bound (ELBO) for Gibbs-EM convergence monitoring."""
26
+ Z_gk_env = joint_post_z.sum(dim=0).detach().cpu().numpy()
27
+ elbo_env = -float(np.sum(np.log(np.maximum(psi_env, 1e-12)) * Z_gk_env))
28
+ elbo = opt_value + elbo_env - gibbs_constant
29
+ return elbo
30
+
31
+
32
+ def run_EM(
33
+ eta_prior: np.ndarray,
34
+ psi_env: np.ndarray,
35
+ theta_env: np.ndarray,
36
+ X: np.ndarray,
37
+ cycle: int,
38
+ gibbs_control: GibbsControl,
39
+ opt_control: OptControl,
40
+ compute_elbo_flag: bool = False,
41
+ ) -> Result[dict[str, Any], str]:
42
+ """
43
+ Run Expectation-Maximization (EM) cycles to refine tumor expression programs.
44
+ """
45
+ K_tum = eta_prior.shape[0]
46
+ sigma = opt_control.sigma
47
+ prior_num = -1.0 / (2.0 * (sigma**2))
48
+
49
+ eta_post = np.copy(eta_prior)
50
+ elbo_vec: list[float] = []
51
+
52
+ for em_cycle in range(1, cycle + 1):
53
+ # M step optimization
54
+ # In full workflow: E-step Gibbs sampler provides Z for tumor programs, then optimize_psi_map refines eta
55
+ Z_gt_tum = np.ones((eta_prior.shape[1], K_tum), dtype=np.float64) * 100.0 # Placeholder expectation
56
+ eta_post, opt_val = optimize_psi_map(eta_prior, Z_gt_tum, prior_num, opt_control)
57
+
58
+ if compute_elbo_flag:
59
+ elbo_val = float(opt_val)
60
+ elbo_vec.append(elbo_val)
61
+
62
+ omega = np.ones((X.shape[0], K_tum), dtype=np.float64) / K_tum
63
+
64
+ return Success(
65
+ {
66
+ "eta_prior": eta_prior,
67
+ "eta_post": eta_post,
68
+ "omega": omega,
69
+ "elbo": elbo_vec,
70
+ }
71
+ )
72
+
73
+
74
+ def learn_embedding_nmf(
75
+ bp: BayesPrism,
76
+ K: int,
77
+ cycle: int = 50,
78
+ gibbs_control: Maybe[GibbsControl] = Nothing,
79
+ opt_control: Maybe[OptControl] = Nothing,
80
+ nmf_control: Maybe[NmfControl] = Nothing,
81
+ compute_elbo_flag: bool = False,
82
+ ) -> Result[dict[str, Any], str]:
83
+ """
84
+ Decompose tumor expression matrix psi_mal into K expression programs using NMF,
85
+ followed by Gibbs-EM refinement.
86
+ """
87
+ if bp.reference_update == Nothing:
88
+ return Failure("Error: reference update missing. Please run BayesPrism first.")
89
+
90
+ ref_update = bp.reference_update.unwrap()
91
+ if not isinstance(ref_update, RefTumor):
92
+ return Failure("Error: learn_embedding requires tumor reference (RefTumor).")
93
+
94
+ psi_mal_np = (
95
+ ref_update.psi_mal.detach().cpu().numpy()
96
+ if isinstance(ref_update.psi_mal, torch.Tensor)
97
+ else np.asarray(ref_update.psi_mal)
98
+ )
99
+
100
+ nmf_ctrl = nmf_control.value_or(NmfControl())
101
+ g_ctrl = gibbs_control.value_or(bp.gibbs_control)
102
+ o_ctrl = opt_control.value_or(bp.opt_control)
103
+
104
+ # Perform NMF decomposition
105
+ model = NMF(
106
+ n_components=K,
107
+ init="random",
108
+ random_state=nmf_ctrl.seed,
109
+ max_iter=nmf_ctrl.nrun,
110
+ )
111
+ W = model.fit_transform(psi_mal_np.T)
112
+ H = model.components_
113
+ nmf_eta = W.T # Shape (K, G)
114
+
115
+ # Normalize prior eta
116
+ nmf_eta_tensor = norm_to_one(torch.from_numpy(nmf_eta).float(), pseudo_min=ref_update.pseudo_min)
117
+ eta_prior = nmf_eta_tensor.numpy()
118
+
119
+ psi_env_np = (
120
+ ref_update.psi_env.detach().cpu().numpy()
121
+ if isinstance(ref_update.psi_env, torch.Tensor)
122
+ else np.asarray(ref_update.psi_env)
123
+ )
124
+
125
+ X_np = (
126
+ bp.prism.mixture.detach().cpu().numpy()
127
+ if isinstance(bp.prism.mixture, torch.Tensor)
128
+ else np.asarray(bp.prism.mixture)
129
+ )
130
+
131
+ theta_env_np = (
132
+ bp.posterior_theta_f.unwrap().theta.detach().cpu().numpy()
133
+ if bp.posterior_theta_f != Nothing
134
+ else bp.posterior_initial_cell_type.theta.detach().cpu().numpy()
135
+ )
136
+
137
+ return run_EM(
138
+ eta_prior=eta_prior,
139
+ psi_env=psi_env_np,
140
+ theta_env=theta_env_np,
141
+ X=X_np,
142
+ cycle=cycle,
143
+ gibbs_control=g_ctrl,
144
+ opt_control=o_ctrl,
145
+ compute_elbo_flag=compute_elbo_flag,
146
+ )