dspin 1.3.2__tar.gz → 1.3.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dspin
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: Regulatory network models from single-cell perturbation profiling
5
5
  Home-page: https://github.com/JialongJiang/DSPIN
6
6
  Author: Jialong Jiang
dspin-1.3.3/README.md ADDED
@@ -0,0 +1,175 @@
1
+ # DSPIN
2
+
3
+ Tutorial, code and examples of the D-SPIN framework for the preprint "D-SPIN constructs gene regulatory network models from multiplexed scRNA-seq data revealing organizing principles of cellular perturbation response" ([bioRxiv](https://www.biorxiv.org/content/10.1101/2023.04.19.537364))
4
+
5
+ <div align="center">
6
+ <img src="figure/readme/Overview_20250523.png" width="800">
7
+ </div>
8
+
9
+ ## Installation
10
+
11
+ D-SPIN is implemented in Python and MATLAB. The Python code is sufficient for the analysis of most datasets with less than 100k cells and a few hundreds of conditions. The MATLAB code is more efficient on large datasets as MATLAB can be easily deployed on clusters for parallelization with the build-in "parfor" function.
12
+
13
+ The python code can be installed with
14
+
15
+ `pip install dspin`
16
+
17
+ The MATLAB code can be downloaded from the folder "DSPIN_matlab", and directly executable in MATLAB after specifying the path to the data.
18
+
19
+ ## Demos
20
+
21
+ Two demos of D-SPIN are available on Google Colab.
22
+
23
+ The first demo reconstructs the regulatory network of simulated hematopoietic stem cell (HSC) differentiation network with perturbations using the BEELINE framework (Pratapa, Aditya, et al. Nature methods, 2020).
24
+
25
+ [Demo1](https://colab.research.google.com/drive/1Nja8AAcP0L7Ag6DGbyn21SxGafsYX4fA?usp=sharing)
26
+
27
+ The second demo reconstructs regulatory network and response vector in a public single-cell dataset of immune dictionary (Cui, Ang, et al. Nature, 2024), where mouse were treated with different cytokines and lymph nodes were than collected and profiled.
28
+
29
+ [Demo2](https://colab.research.google.com/drive/1E8v4awQ-m4D02emG0jp6kmRDmkZ8hjjN?usp=sharing)
30
+
31
+ ## Dependencies
32
+
33
+ DSPIN package was tested with the following python packages versions:
34
+
35
+ - python (3.9.18)
36
+ - anndata (0.10.3)
37
+ - matplotlib (3.8.2)
38
+ - scanpy (1.9.6)
39
+ - tqdm (4.65.0)
40
+ - leidenalg (0.10.1)
41
+ - igraph (0.10.8)
42
+
43
+ Note: other versions may work as well.
44
+
45
+ ## Input data
46
+
47
+ D‑SPIN can work with many perturbation types **as long as they share the same core regulatory network**:
48
+
49
+ * **Genetic screens** – Perturb‑seq, CRISPR, RNAi
50
+ * **Chemical or signaling cues** – drug treatments, growth‑factor changes
51
+ * **Physiological differences** – healthy vs disease, different patients, time‑course samples
52
+ * **Spatial niches** – local micro‑environments from spatial transcriptomics
53
+
54
+ Because D‑SPIN models the *entire* distribution of transcriptional states, it expects **single‑cell RNA‑seq** data. Bulk data is possible but less informative.
55
+
56
+ #### AnnData requirements
57
+
58
+ * `adata.X` — log‑normalized (`log1p`) count matrix **after** QC: remove low‑quality cells, filter high mitochondrial content, keep highly variable genes.
59
+ * `adata.obs['sample_id']` — condition label; cells with the same value form one perturbation group (≥ 25 cells per group is ideal).
60
+ * `adata.obs['batch']` — batch identifier for cells processed together; D‑SPIN corrects batch effects by comparing conditions within each batch.
61
+ * `adata.obs['if_control']` — `True` for controls, `False` otherwise; perturbation effects are measured relative to these controls (or to the overall average if no controls exist).
62
+
63
+ ## Building network models with D‑SPIN
64
+
65
+ #### Gene‑level network models
66
+
67
+ ```python
68
+ from dspin.dspin import DSPIN
69
+
70
+ model = DSPIN(adata, save_path, num_spin=adata.shape[1])
71
+ model.network_inference(
72
+ sample_id_key='sample_id',
73
+ method='pseudo_likelihood',
74
+ directed=True,
75
+ run_with_matlab=False,
76
+ params={'stepsz': 0.05, 'lam_l1_j': 0.01}
77
+ )
78
+ model.response_relative_to_control(
79
+ sample_id_key='sample_id',
80
+ if_control_key='if_control',
81
+ batch_key='batch'
82
+ )
83
+ ```
84
+
85
+ * **Results saved to `model`:**
86
+ * `model.network` – regulatory weights **J**
87
+ * `model.responses` – gene‑level responses **h**
88
+ * `model.relative_responses` – responses relative to control (per batch)
89
+
90
+ * **Key options**
91
+ * `num_spin=adata.shape[1]` → build a *gene‑level* network
92
+ * `directed=True` → only supported with `pseudo_likelihood`
93
+ * `run_with_matlab=True` → skip Python inference and write variables to `save_path` for the MATLAB version (speeds up very large data sets)
94
+ * `params` → hyper‑parameters such as step size, training epochs, and L1/L2 regularisation
95
+
96
+ ---
97
+
98
+ #### Program‑level network models
99
+
100
+ ```python
101
+ from dspin.dspin import DSPIN
102
+
103
+ model = DSPIN(adata, save_path, num_spin=15) # 15 gene programs
104
+ model.gene_program_discovery(
105
+ num_repeat=10,
106
+ seed=0,
107
+ cluster_key='cell_type' # balance cell types
108
+ )
109
+ model.network_inference(
110
+ sample_id_key='sample_id',
111
+ method='mcmc_maximum_likelihood'
112
+ )
113
+ model.response_relative_to_control()
114
+ ```
115
+
116
+ * **Extra outputs**
117
+ * Gene‑program compositions in `save_path/onmf`
118
+ * Consensus program gene lists in `save_path`
119
+
120
+ * **Tips**
121
+ * Choose `num_spin` ≈ 5 × (number of cell types); keep ≤ 40
122
+ * `cluster_key` lets D‑SPIN down‑sample over‑represented cell types before oNMF
123
+
124
+ ---
125
+
126
+ #### Finding gene regulators of programs
127
+
128
+ ```python
129
+ # model_program : program‑level DSPIN object
130
+ # model_gene : gene‑level DSPIN object
131
+ model_gene.program_regulator_discovery(
132
+ model_program.program_representation,
133
+ sample_id_key='sample_id',
134
+ params={'stepsz': 0.02, 'lam_l1_interaction': 0.01}
135
+ )
136
+ ```
137
+
138
+ * **Prerequisite** – `adata.obs` metadata must match between the two models so each cell’s gene and program states align.
139
+
140
+ * **Outputs in `model_gene`:**
141
+ * `model_gene.program_interactions` – regression coefficients **J** (gene ↔ program)
142
+ * `model_gene.program_activities` – global activity **h** for each program
143
+
144
+
145
+ ## Application to the T cell population of the immune dictionary dataset
146
+
147
+
148
+ <div align="center">
149
+ <img src="figure/readme/Figure2_immune_dictionary.png" width="600">
150
+ </div>
151
+
152
+ <div align="left">
153
+ <em>Figure&nbsp; Overview and program discovery for the immune dictionary dataset. (A) t-distributed stochastic neighbour (t-SNE) embedding for a subsetted cell population from the immune dictionary dataset \cite{cui2024dictionary}. The subset dataset includes CD4, CD8, and regulatory T cells treated by 12 different cytokines, as well as corresponding control samples treated by phosphate-buffered saline (PBS).
154
+ (B) Heatmaps of gene expression and discretized gene program levels for control and IFN-$\alpha$1-treated samples. The gene programs are weighted averages of single-gene expressions that characterize and denoise the major expression pattern of the gene matrix.</em>
155
+ </div>
156
+
157
+ <div align="center">
158
+ <img src="figure/readme/Figure3_program_network.png" width="600">
159
+ </div>
160
+
161
+ <div align="left">
162
+ <em>Figure&nbsp; Program/gene-level regulatory network inferred by D-SPIN.
163
+ (A) Diagram of D-SPIN-inferred network model on gene programs. The network is partitioned into 4 modules, each associated with a T cell type in the population.
164
+ (B) Heatmap of the program response of each cytokine. Clustering of the response partitioned the cytokines into 3 major categories.
165
+ (C) Diagram of the core subnetwork of the D-SPIN-inferred gene network model. The node sizes scale with the number of identified interactions. The network is partitioned into 4 modules, each primarily composed of genes that have elevated expression in one specific T cell type.
166
+ (D) Interaction diagram for the subnetwork of IFN-$\alpha$1/IFN-$\beta$ and IFN-$\gamma$ acting on the program P18-IFN-$\gamma$ response. D-SPIN model shows that Type I and Type II interferons have different effectors to activate the program.</em>
167
+ </div>
168
+
169
+ ## References
170
+
171
+ 1. Jiang, Jialong, et al. "D-SPIN constructs gene regulatory network models from multiplexed scRNA-seq data revealing organizing principles of cellular perturbation response." bioRxiv (2023).
172
+
173
+ 2. Pratapa, Aditya, et al. "Benchmarking algorithms for gene regulatory network inference from single-cell transcriptomic data." Nature methods 17.2 (2020): 147-154.
174
+
175
+ 3. Cui, Ang, et al. "Dictionary of immune responses to cytokines at single-cell resolution." Nature 625.7994 (2024): 377-384.
@@ -20,6 +20,7 @@ from tqdm import tqdm
20
20
  from typing import Tuple, List, Callable, Any, Dict
21
21
  import warnings
22
22
  from scipy.sparse import issparse
23
+ from joblib import Parallel, delayed, parallel_backend
23
24
 
24
25
 
25
26
  def category_balance_number(
@@ -526,7 +527,7 @@ def sample_states(samp_full: np.array, onmf_rep_tri: np.array) -> Tuple[np.array
526
527
  return state_list, samp_list
527
528
 
528
529
 
529
- def para_moments(j_mat: np.array, h_vec: np.array) -> Tuple[np.array, np.array]:
530
+ def para_moments_numpy(j_mat: np.array, h_vec: np.array) -> Tuple[np.array, np.array]:
530
531
  """
531
532
  Calculate the correlation and mean parameters given a j matrix and an h vector.
532
533
 
@@ -565,6 +566,74 @@ def para_moments(j_mat: np.array, h_vec: np.array) -> Tuple[np.array, np.array]:
565
566
 
566
567
  return corr_para, mean_para
567
568
 
569
+ @numba.njit(fastmath=True, cache=True)
570
+ def para_moments(j_mat: np.ndarray,
571
+ h_vec: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
572
+ """
573
+ Calculate the correlation and mean parameters given an interaction
574
+ matrix and an external field vector, using a Numba‑accelerated kernel.
575
+
576
+ Parameters
577
+ ----------
578
+ j_mat : np.ndarray
579
+ Interaction matrix.
580
+ h_vec : np.ndarray
581
+ External field vector.
582
+
583
+ Returns
584
+ -------
585
+ Tuple[np.ndarray, np.ndarray]
586
+ The correlation parameter array of shape (n, n).
587
+ The mean parameter array of shape (n,).
588
+ """
589
+ num_spin: int = j_mat.shape[0]
590
+
591
+ # Copy so we can safely modify the diagonal in‑place
592
+ j_dbl = j_mat.copy()
593
+ for ii in range(num_spin):
594
+ j_dbl[ii, ii] += j_mat[ii, ii]
595
+
596
+ num_sample: int = 3 ** num_spin
597
+
598
+ partition: float = 0.0
599
+ mean_para = np.zeros(num_spin, dtype=np.float64)
600
+ corr_para = np.zeros((num_spin, num_spin), dtype=np.float64)
601
+
602
+ spins = np.empty(num_spin, dtype=np.int8)
603
+
604
+ for idx in range(num_sample):
605
+ # Decode `idx` → base‑3 spin configuration in {‑1, 0, 1}
606
+ tmp = idx
607
+ for s in range(num_spin - 1, -1, -1):
608
+ spins[s] = (tmp % 3) - 1
609
+ tmp //= 3
610
+
611
+ # Energy: E = −h·s − ½ sᵀJs
612
+ energy: float = 0.0
613
+ for ii in range(num_spin):
614
+ energy -= h_vec[ii] * spins[ii]
615
+
616
+ js_term: float = 0.0
617
+ for ii in range(num_spin):
618
+ dot: float = 0.0
619
+ for jj in range(num_spin):
620
+ dot += j_dbl[ii, jj] * spins[jj]
621
+ js_term += spins[ii] * dot
622
+ energy -= 0.5 * js_term
623
+
624
+ weight: float = np.exp(-energy)
625
+
626
+ partition += weight
627
+ for ii in range(num_spin):
628
+ mean_para[ii] += spins[ii] * weight
629
+ for jj in range(num_spin):
630
+ corr_para[ii, jj] += spins[ii] * spins[jj] * weight
631
+
632
+ mean_para /= partition
633
+ corr_para /= partition
634
+
635
+ return corr_para, mean_para
636
+
568
637
 
569
638
  @numba.njit(fastmath=True)
570
639
  def pseudol_gradient(cur_j: np.ndarray,
@@ -602,7 +671,7 @@ def pseudol_gradient(cur_j: np.ndarray,
602
671
  for ii in range(num_spin):
603
672
  j_no_diag[ii, ii] = 0.0
604
673
 
605
- eff_h = j_no_diag @ cur_state + cur_h.reshape(num_spin, 1)
674
+ eff_h = j_no_diag @ cur_state + cur_h
606
675
 
607
676
  exp_plus = np.empty_like(eff_h)
608
677
  exp_minus = np.empty_like(eff_h)
@@ -727,10 +796,10 @@ def samp_moments(j_mat: np.ndarray,
727
796
  return corr_para, mean_para
728
797
 
729
798
 
730
- def compute_gradient(cur_j: np.ndarray,
731
- cur_h: np.ndarray,
732
- raw_data: List[Tuple[np.ndarray, np.ndarray]],
733
- method: str, train_dat: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
799
+ def compute_gradient(cur_j: np.ndarray,
800
+ cur_h: np.ndarray,
801
+ raw_data: List[Tuple[np.ndarray, np.ndarray]],
802
+ method: str, train_dat: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
734
803
  """
735
804
  Compute the gradient based on the specified method.
736
805
 
@@ -763,7 +832,7 @@ def compute_gradient(cur_j: np.ndarray,
763
832
  # Computing gradients using the specified method.
764
833
  if method == 'pseudo_likelihood':
765
834
  j_grad, h_grad = pseudol_gradient(
766
- cur_j, cur_h[:, kk].reshape(- 1, 1), raw_data[kk], directed=train_dat['directed'])
835
+ cur_j, cur_h[:, kk: kk + 1], raw_data[kk], directed=train_dat['directed'])
767
836
  h_grad = h_grad.flatten()
768
837
  else:
769
838
  # Distinguishing between other methods and computing gradients
@@ -772,7 +841,7 @@ def compute_gradient(cur_j: np.ndarray,
772
841
  corr_para, mean_para = para_moments(cur_j, cur_h[:, kk])
773
842
  elif method == 'mcmc_maximum_likelihood':
774
843
  corr_para, mean_para = samp_moments(
775
- cur_j, cur_h, train_dat['mcmc_samplingsz'], train_dat['mcmc_samplingmix'], train_dat['mcmc_samplegap'])
844
+ cur_j, cur_h[:, kk], train_dat['mcmc_samplingsz'], train_dat['mcmc_samplingmix'], train_dat['mcmc_samplegap'])
776
845
  j_grad = corr_para - raw_data[kk][0]
777
846
  h_grad = mean_para - raw_data[kk][1].flatten()
778
847
 
@@ -782,6 +851,77 @@ def compute_gradient(cur_j: np.ndarray,
782
851
  return rec_jgrad, rec_hgrad
783
852
 
784
853
 
854
+ def _gradient_one_round(kk: int,
855
+ cur_j: np.ndarray,
856
+ cur_h: np.ndarray,
857
+ raw_data_k,
858
+ method: str,
859
+ train_dat: Dict[str, Any]):
860
+ """Helper that compute gradient for a single round."""
861
+ if method == 'pseudo_likelihood':
862
+ j_grad, h_grad = pseudol_gradient(cur_j, cur_h[:, kk:kk + 1], raw_data_k, directed=train_dat['directed'])
863
+ h_grad = h_grad.flatten()
864
+ else:
865
+ if method == 'maximum_likelihood':
866
+ corr_para, mean_para = para_moments(cur_j, cur_h[:, kk])
867
+ elif method == 'mcmc_maximum_likelihood':
868
+ corr_para, mean_para = samp_moments(
869
+ cur_j, cur_h[:, kk], train_dat['mcmc_samplingsz'], train_dat['mcmc_samplingmix'], train_dat['mcmc_samplegap'])
870
+ j_grad = corr_para - raw_data_k[0]
871
+ h_grad = mean_para - raw_data_k[1].flatten()
872
+
873
+ return kk, j_grad, h_grad
874
+
875
+
876
+ def compute_gradient_para(cur_j: np.ndarray,
877
+ cur_h: np.ndarray,
878
+ raw_data: List[Tuple[np.ndarray, np.ndarray]],
879
+ method: str, train_dat: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
880
+ """
881
+ Compute the gradient based on the specified method with parallel processing.
882
+
883
+ Parameters
884
+ ----------
885
+ cur_j : np.ndarray
886
+ Current j matrix.
887
+ cur_h : np.ndarray
888
+ Current h matrix.
889
+ raw_data : List[Tuple[np.ndarray, np.ndarray]]
890
+ Raw data used to calculate the gradient (each element is a tuple of correlation and mean).
891
+ method : str
892
+ The method to calculate the gradient; possible values are 'pseudo_likelihood',
893
+ 'maximum_likelihood', and 'mcmc_maximum_likelihood'.
894
+ train_dat : Dict[str, Any]
895
+ Training data and hyperparameters.
896
+
897
+ Returns
898
+ -------
899
+ Tuple[np.array, np.array]
900
+ The gradient of j.
901
+ The gradient of h.
902
+ """
903
+ num_spin, num_round = cur_h.shape
904
+ rec_jgrad = np.zeros((num_spin, num_spin, num_round))
905
+ rec_hgrad = np.zeros((num_spin, num_round))
906
+
907
+ n_jobs = train_dat.get('max_workers', min(num_round, os.cpu_count()))
908
+ chunk = train_dat.get('chunk_size', 1) # rounds per task
909
+ backend = 'loky' # process‑based
910
+
911
+ # print(f'Processing with {n_jobs} workers...')
912
+ with parallel_backend(backend, n_jobs=n_jobs):
913
+ results = Parallel(batch_size=chunk)(
914
+ delayed(_gradient_one_round)(kk, cur_j, cur_h, raw_data[kk], method, train_dat)
915
+ for kk in range(num_round)
916
+ )
917
+
918
+ for kk, j_grad, h_grad in results:
919
+ rec_jgrad[:, :, kk] = j_grad
920
+ rec_hgrad[:, kk] = h_grad
921
+
922
+ return rec_jgrad, rec_hgrad
923
+
924
+
785
925
  def apply_regularization(rec_jgrad: np.ndarray,
786
926
  rec_hgrad: np.ndarray,
787
927
  cur_j: np.ndarray,
@@ -964,6 +1104,8 @@ def learn_network_adam(raw_data: Any,
964
1104
 
965
1105
  backtrack_counter = 0
966
1106
  counter = 1
1107
+
1108
+ tqdm._instances.clear()
967
1109
  pbar = tqdm(total=num_epoch)
968
1110
 
969
1111
  while counter <= num_epoch:
@@ -34,6 +34,7 @@ import matplotlib.patheffects as patheffects
34
34
  import warnings
35
35
  import itertools
36
36
  from typing import List, Dict, Optional, Tuple, Union
37
+ from scipy.stats import skew
37
38
 
38
39
 
39
40
  class AbstractDSPIN(ABC):
@@ -337,8 +338,8 @@ class AbstractDSPIN(ABC):
337
338
  raise ValueError(f"Column '{batch_key}' not found in adata.obs.")
338
339
 
339
340
  sample_list = self._samp_list
340
- sample_to_control = self.adata.obs.groupby(sample_id_key)[if_control_key].first().to_dict()
341
- sample_to_batch = self.adata.obs.groupby(sample_id_key)[batch_key].first().to_dict()
341
+ sample_to_control = self.adata.obs.groupby(sample_id_key, observed=False)[if_control_key].first().to_dict()
342
+ sample_to_batch = self.adata.obs.groupby(sample_id_key, observed=False)[batch_key].first().to_dict()
342
343
 
343
344
  if_control = np.array([sample_to_control[sample] for sample in sample_list])
344
345
  batch_index = np.array([sample_to_batch[sample] for sample in sample_list])
@@ -382,8 +383,20 @@ class GeneDSPIN(AbstractDSPIN):
382
383
  else:
383
384
  self._onmf_rep_ori = adata.X
384
385
 
386
+ gene_skew = np.zeros(num_spin)
387
+
388
+ for ii in range(num_spin):
389
+ cur_gene = self._onmf_rep_ori[:, ii]
390
+ gene_skew[ii] = skew(cur_gene, bias=False)
391
+
392
+ if gene_skew.mean() > 2:
393
+ default_clip = 95
394
+ print("Input gene expression seem to be heavy-tailed. Using 95 percentile for clipping before discretization.")
395
+ else:
396
+ default_clip = 100
397
+
385
398
  dparams = {'use_default_discretize': True,
386
- 'clip_percentile': 95,
399
+ 'clip_percentile': default_clip,
387
400
  'num_init': 10}
388
401
  dparams.update(discretize_params)
389
402
 
@@ -549,7 +562,6 @@ class ProgramDSPIN(AbstractDSPIN):
549
562
  cluster_key: str = 'leiden',
550
563
  mode: str = 'compute_summary',
551
564
  prior_programs: List[List[str]] = None,
552
- clip_percentile: float = 100,
553
565
  params: dict = {},
554
566
  discretize_params: dict = {}) -> None:
555
567
  """
@@ -567,10 +579,10 @@ class ProgramDSPIN(AbstractDSPIN):
567
579
  Mode of operation. Options: 'compute_summary', 'compute_only', 'summary_only'. Default is 'compute_summary'.
568
580
  prior_programs : List[List[str]], optional
569
581
  List of predefined gene programs. Default is None.
570
- clip_percentile : float, optional
571
- Percentile for clipping values before discretization. Default is 100.
572
582
  params : dict, optional
573
583
  Additional parameters for oNMF. Default is an empty dictionary.
584
+ discretize_params : dict, optional
585
+ Parameters for discretization of the oNMF representation. Default is an empty dictionary.
574
586
  """
575
587
 
576
588
  if mode not in ['compute_summary', 'compute_only', 'summary_only']:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dspin
3
- Version: 1.3.2
3
+ Version: 1.3.3
4
4
  Summary: Regulatory network models from single-cell perturbation profiling
5
5
  Home-page: https://github.com/JialongJiang/DSPIN
6
6
  Author: Jialong Jiang
@@ -21,7 +21,7 @@ install_requires = [
21
21
  # ]
22
22
 
23
23
  # Managing your version (adjust accordingly)
24
- version = "1.3.2" # You can manage versioning in a more sophisticated way if needed
24
+ version = "1.3.3" # You can manage versioning in a more sophisticated way if needed
25
25
 
26
26
  # Long description (usually a README file)
27
27
  readme = open("README_PyPI.md").read() # Adjust the file name if necessary
dspin-1.3.2/README.md DELETED
@@ -1,134 +0,0 @@
1
- # DSPIN
2
-
3
- Tutorial, code and examples of the D-SPIN framework for the preprint "D-SPIN constructs gene regulatory network models from multiplexed scRNA-seq data revealing organizing principles of cellular perturbation response" ([bioRxiv](https://www.biorxiv.org/content/10.1101/2023.04.19.537364))
4
-
5
- ![alternativetext](figure/readme/Figure1_20230309_Inna.png)
6
-
7
- ## Installation
8
-
9
- D-SPIN is implemented in Python and Matlab. The Python code is sufficient for analysis of small datasets with around 20k cells and tens of conditions. The Matlab code is only used for network inference on large datasets as Matlab can be easily deployed on clusters for parallelization with the build-in "parfor" function.
10
-
11
- The python code can be installed with
12
-
13
- `pip install dspin`
14
-
15
- The Matlab code can be downloaded from the folder "DSPIN_matlab", and directly executable in Matlab after specifying the path to the data.
16
-
17
- ## Demos
18
-
19
- Two demos of D-SPIN are available on Google Colab.
20
-
21
- The first demo reconstructs the regulatory network of simulated hematopoietic stem cell (HSC) differentiation network with perturbations using the BEELINE framework (Pratapa, Aditya, et al. Nature methods, 2020).
22
-
23
- [Demo1](https://colab.research.google.com/drive/1mjJ09_E9kjEYsGCzf10-aZfuOqHUEV5u?usp=sharing)
24
-
25
- The second demo reconstructs regulatory network and response vector in a single-cell dataset collected in the ThomsonLab.In the dataset, human peripheral blood mononuclear cells (PBMCs) were treated with various signaling molecules with different dosages.
26
-
27
- [Demo2](https://colab.research.google.com/drive/1U2WbmKvai3Y_-vlLV0SY1dv1LHdFjg2w?usp=sharing)
28
-
29
- ## Dependencies
30
-
31
- DSPIN package was tested with the following python packages versions:
32
-
33
- - python (3.9.18)
34
- - anndata (0.10.3)
35
- - matplotlib (3.8.2)
36
- - scanpy (1.9.6)
37
- - tqdm (4.65.0)
38
- - leidenalg (0.10.1)
39
- - igraph (0.10.8)
40
-
41
- Note: other versions may work as well.
42
-
43
- ## D-SPIN Overview
44
-
45
- D-SPIN contains three main steps:
46
- * Gene program discovery
47
- * Network inference
48
- * Network analysis
49
-
50
- The input data should be AnnData object after typical single-cell preprocessing steps including cell counts normalization, log transformation, highly-varible gene selection, and clustering (optional). The input data should contain the following information. The attribute name is subject to the user while the following are defaults of the code:
51
- * Normalized, log-transformed and filtered gene expression matrix in adata.X
52
- * Sample information for each cell in adata.obs['sample_id']
53
- * Batch information for each cell in adata.obs['batch']
54
- * Clustering information for each cell in adata.obs['leiden']
55
- * If some samples are control conditions, the relative response vectors will be computed for each batch with respect to the average of control conditions. The control conditions can be indicated in adata.obs['if_control'] with 1 for control and 0 for non-control. Note that all cells in the same sample and batch should have the same value in adata.obs['if_control'].
56
-
57
- The D-SPIN is initiallized by model = dspin.DSPIN(adata) with the following major arguments:
58
-
59
- * adata: AnnData object with the above information
60
- * save_path: path to save the results
61
- * num_spin (default 15): number of gene programs in the model
62
- * filter_threshold (default 0.02): threshold for minimal proportion of cells that each gene program is expressed in
63
-
64
- ### Gene program discovery
65
-
66
- By default, D-SPIN use orgthogonal non-negative matrix factorization (oNMF) to discover gene programs that coexpress in the data. The user can also specify pre-defined gene programs in full or partial. There are three senarios depending on the num_spin and the input parameter prior_programs.
67
- * No pre-defined gene programs: empty prior_programs (default)
68
- * Full pre-defined gene programs: the number of gene programs in prior_programs is equal to num_spin
69
- * Partial pre-defined gene programs: the number of gene programs in prior_programs is less than num_spin
70
-
71
- The gene program discovery contains two stages, the first stage subset and normalize the gene matrix, and the second stage run oNMF multiple times on the subset matrix to obtain consensus gene programs. Subsetting the gene matrix is primarily for computational efficiency, and preferrentially sampling cells with smaller population also helps to avoid overfitting on over-represented cell states in the dataset. As the algorithm for oNMF is stochastic, the algorithm is run multiples times and results from multiple run are combined to obtain a set of consensus gene program. Also, the number of oNMF components can be specified by the user to discover gene programs with finer resolution. For example, run the algorithm with 20 components in each individual run but combine the 20 programs into 15 consensus programs.
72
-
73
- Overall, in the gene program discovery function model.gene_program_discovery() , D-SPIN takes the following major arguments:
74
-
75
- * num_onmf_components (int, optional): The number of oNMF components. Default is infer based on num_spin and pre-assigned programs.
76
- * num_subsample (int, optional): Number of cells to use in each individual oNMF components computation. Default is 10000.
77
- * num_subsample_large (int, optional): Number of cells to use in computing consensus oNMF components. Default is 5 times of num_subsample.
78
- * num_repeat (int, optional): Number of times to repeat the oNMF computation. Default is 10.
79
- * balance_obs (str, optional): Cell type or clustering label in adata.obs for balanced sampling. Default is None.
80
- * balance_method (str, optional): Method used for balancing. Options are 'squareroot', 'equal', 'proportional'. Default is 'squareroot'.
81
- * max_sample_rate (float, optional): Maximum oversampling rate for underrepresented cell type or clustering categories. Default is 2.
82
- * prior_programs (List[List[str]], optional): List of pre-assigned gene programs. Default is None.
83
- * summary_method (str, optional): Method used for computing sensus oNMF components. Options are 'kmeans' and 'leiden'. Default is 'kmeans'.
84
-
85
- ### Network inference
86
-
87
- In the network inference step, D-SPIN automatically choose between three inference methods depending on the number of gene programs:
88
-
89
- * Exact maximum-likelihood inference (MLE)
90
- * Markov-Chain Monte-Carlo (MCMC) maximum-likelihood inference
91
- * Pseudo-likelihood inference (PL).
92
-
93
- MLE is exactly solution of the problem but only applies to small network. MCMC is slightly more scalable to networks with 20-40 nodes. PL takes more simplified approximation to the problem which scales to hundreds of nodes, but requires larger cell number for the approximation to be effective. in the gene program discovery function model.network_inference(), D-SPIN takes the following major arguments
94
-
95
- * sample_col_name (str, optional): Column name in adata.obs for sample information. Default is 'sample_id'
96
- * control_col_name (str, optional): Column name in adata.obs for whether the cell is in a control sample. If not provided, all cells are treated as non-control and relative response vector are the same as response vector.
97
- * batch_col_name (str, optional): Column name in adata.obs for batch information. Relative response are computed by the control response vector in the same batch. If not provided, all cells are treated as one batch.
98
- * method (str, optional): Method used for network inference. Options are 'auto', 'maximum_likelihood', 'mcmc_maximum_likelihood', 'pseudo_likelihood'.
99
- * example_list (List[str], optional): List of samples to use for network inference. Default is using all samples available in adata.obs[sample_col_name].
100
- * record_step (int, optional): Number of steps to record training progress. Default is 10.
101
- * params (Dict[str, Any], optional): Hypterparameters for the gradient descent of network inference. Default is None.
102
-
103
-
104
- ### Network analysis
105
-
106
- After network inference, D-SPIN provides three outputs:
107
-
108
- * Inferred network adjacenty matrix in model.network
109
- * Inferred pertrubation response in model.response
110
- * Relative perturbation response to control in model.relative_response
111
-
112
- Typical downstream analysis includes network visualization, module identification, and graph clustering of perturbation response with the network.
113
-
114
- ## Application to signaling response data of human PBMCs
115
-
116
- D-SPIN takes single-cell sequencing data of multiple perturbation conditions. In the second demo, PBMCs are treated with different signaling molecules such as CD3 antibody, LPS, IL1B, and TGFB1
117
-
118
- ![alternativetext](figure/thomsonlab_signaling/example_conditions.png)
119
-
120
- D-SPIN identifies a set of gene programs that coexpress in the data, and represent each cell as a combination of gene program expression states.
121
-
122
- ![alternativetext](figure/thomsonlab_signaling/gene_program_example.png)
123
-
124
- D-SPIN uses cross-correlation and mean of each perturbation condition to inferred a unified regulatory network and the response vector of each perturbation condition. The inference can be parallelized across perturbation conditions. The inference code is in Matlab using "parfor", while for demo purpose Python code (without parallelization) is provided.
125
-
126
- The inferred regulatory network and perturbations can be jointly analyzed to reveal how perturbations act in the context of the regulatory network.
127
-
128
- ![alternativetext](figure/thomsonlab_signaling/joint_network_perturbation.png)
129
-
130
- ## References
131
-
132
- 1. Jiang, Jialong, et al. "D-SPIN constructs gene regulatory network models from multiplexed scRNA-seq data revealing organizing principles of cellular perturbation response." bioRxiv (2023).
133
-
134
- 2. Pratapa, Aditya, et al. "Benchmarking algorithms for gene regulatory network inference from single-cell transcriptomic data." Nature methods 17.2 (2020): 147-154.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes