dspin 1.3.1__tar.gz → 1.3.2__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.
- {dspin-1.3.1/dspin.egg-info → dspin-1.3.2}/PKG-INFO +1 -1
- {dspin-1.3.1 → dspin-1.3.2}/dspin/compute.py +45 -80
- {dspin-1.3.1 → dspin-1.3.2}/dspin/dspin.py +37 -33
- {dspin-1.3.1 → dspin-1.3.2/dspin.egg-info}/PKG-INFO +1 -1
- {dspin-1.3.1 → dspin-1.3.2}/setup.py +1 -1
- {dspin-1.3.1 → dspin-1.3.2}/LICENSE +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/README.md +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/__init__.cpython-310.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/__init__.cpython-37.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/compute.cpython-310.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/compute.cpython-37.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/dspin_abstract.cpython-310.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/plot.cpython-310.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/__pycache__/plotting.cpython-37.pyc +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin/__init__.py +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin/annotate.py +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin/plot.py +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin.egg-info/SOURCES.txt +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin.egg-info/dependency_links.txt +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin.egg-info/requires.txt +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/dspin.egg-info/top_level.txt +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/requirements.txt +0 -0
- {dspin-1.3.1 → dspin-1.3.2}/setup.cfg +0 -0
|
@@ -290,7 +290,9 @@ def compute_onmf(seed: int,
|
|
|
290
290
|
return nmf_model
|
|
291
291
|
|
|
292
292
|
|
|
293
|
-
def onmf_discretize(onmf_rep_ori: np.array,
|
|
293
|
+
def onmf_discretize(onmf_rep_ori: np.array,
|
|
294
|
+
num_init: int = 10,
|
|
295
|
+
fig_folder: str = None) -> np.array:
|
|
294
296
|
"""
|
|
295
297
|
Discretize the ONMF representation using KMeans clustering and visualize the sorted representations.
|
|
296
298
|
|
|
@@ -313,10 +315,11 @@ def onmf_discretize(onmf_rep_ori: np.array, fig_folder: str = None) -> np.array:
|
|
|
313
315
|
21, num_spin))
|
|
314
316
|
|
|
315
317
|
onmf_rep_tri = np.zeros(onmf_rep_ori.shape)
|
|
316
|
-
|
|
318
|
+
print('Processing gene or program expression levels...')
|
|
319
|
+
for ii in tqdm(range(num_spin)):
|
|
317
320
|
# Perform KMeans clustering for each spin/component
|
|
318
|
-
km_fit = KMeans(n_clusters=3, n_init=
|
|
319
|
-
|
|
321
|
+
km_fit = KMeans(n_clusters=3, n_init=num_init).fit(onmf_rep_ori[:, ii].reshape(-1, 1))
|
|
322
|
+
|
|
320
323
|
onmf_rep_tri[:, ii] = (km_fit.labels_ == np.argsort(km_fit.cluster_centers_.reshape(-1))[0]) * \
|
|
321
324
|
(-1) + (km_fit.labels_ == np.argsort(km_fit.cluster_centers_.reshape(-1))[2]) * 1
|
|
322
325
|
|
|
@@ -324,8 +327,7 @@ def onmf_discretize(onmf_rep_ori: np.array, fig_folder: str = None) -> np.array:
|
|
|
324
327
|
if ii < 21:
|
|
325
328
|
ax = plt.subplot(grid[ii])
|
|
326
329
|
plt.plot(np.sort(onmf_rep_ori[:, ii]))
|
|
327
|
-
plt.plot(np.sort(km_fit.cluster_centers_[
|
|
328
|
-
km_fit.labels_].reshape(-1)))
|
|
330
|
+
plt.plot(np.sort(km_fit.cluster_centers_[km_fit.labels_].reshape(-1)))
|
|
329
331
|
|
|
330
332
|
# Save the visual representation
|
|
331
333
|
if fig_folder is not None:
|
|
@@ -564,50 +566,7 @@ def para_moments(j_mat: np.array, h_vec: np.array) -> Tuple[np.array, np.array]:
|
|
|
564
566
|
return corr_para, mean_para
|
|
565
567
|
|
|
566
568
|
|
|
567
|
-
@numba.njit
|
|
568
|
-
def np_apply_along_axis(func1d: Callable[[np.ndarray], float],
|
|
569
|
-
axis: int,
|
|
570
|
-
arr: np.ndarray) -> np.ndarray:
|
|
571
|
-
"""
|
|
572
|
-
Applies a function along the specified axis.
|
|
573
|
-
|
|
574
|
-
Parameters
|
|
575
|
-
----------
|
|
576
|
-
func1d : Callable[[np.ndarray], float]
|
|
577
|
-
A 1-D function to be applied.
|
|
578
|
-
axis : int
|
|
579
|
-
Axis along which the function should be applied.
|
|
580
|
-
arr : np.ndarray
|
|
581
|
-
Input array.
|
|
582
|
-
|
|
583
|
-
Returns
|
|
584
|
-
-------
|
|
585
|
-
np.ndarray
|
|
586
|
-
The result of applying func1d to arr along the specified axis.
|
|
587
|
-
"""
|
|
588
|
-
assert arr.ndim == 2
|
|
589
|
-
assert axis in [0, 1]
|
|
590
|
-
result = np.empty(arr.shape[1]) if axis == 0 else np.empty(arr.shape[0])
|
|
591
|
-
|
|
592
|
-
for i in range(len(result)):
|
|
593
|
-
result[i] = func1d(arr[:, i]) if axis == 0 else func1d(arr[i, :])
|
|
594
|
-
|
|
595
|
-
return result
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
@numba.njit
|
|
599
|
-
def np_mean(array: np.array, axis: int) -> np.array:
|
|
600
|
-
"""
|
|
601
|
-
Computes the arithmetic mean along the specified axis.
|
|
602
|
-
|
|
603
|
-
Parameters:
|
|
604
|
-
array (np.array): Input array.
|
|
605
|
-
axis (int): Axis along which the mean is computed. 1 is for row-wise, 0 is for column-wise.
|
|
606
|
-
"""
|
|
607
|
-
return np_apply_along_axis(np.mean, axis, array)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
@numba.jit()
|
|
569
|
+
@numba.njit(fastmath=True)
|
|
611
570
|
def pseudol_gradient(cur_j: np.ndarray,
|
|
612
571
|
cur_h: np.ndarray,
|
|
613
572
|
cur_state: np.ndarray,
|
|
@@ -632,37 +591,43 @@ def pseudol_gradient(cur_j: np.ndarray,
|
|
|
632
591
|
The gradient of j.
|
|
633
592
|
The gradient of h.
|
|
634
593
|
"""
|
|
635
|
-
num_spin =
|
|
636
|
-
|
|
637
|
-
cur_j_grad = np.zeros((num_spin, num_spin))
|
|
638
|
-
cur_h_grad = np.zeros((num_spin, 1))
|
|
594
|
+
num_spin, num_sample = cur_state.shape
|
|
595
|
+
sample_factor = 1.0 / num_sample
|
|
639
596
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
effective_h = j_filt.dot(cur_state) + cur_h
|
|
597
|
+
diag_j = np.empty(num_spin, dtype=cur_j.dtype)
|
|
598
|
+
for ii in range(num_spin):
|
|
599
|
+
diag_j[ii] = cur_j[ii, ii]
|
|
644
600
|
|
|
601
|
+
j_no_diag = cur_j.copy()
|
|
645
602
|
for ii in range(num_spin):
|
|
646
|
-
|
|
647
|
-
j_sub = cur_j[ii, ii]
|
|
648
|
-
h_sub = effective_h[ii, :]
|
|
603
|
+
j_no_diag[ii, ii] = 0.0
|
|
649
604
|
|
|
650
|
-
|
|
651
|
-
term2 = np.exp(j_sub - h_sub)
|
|
605
|
+
eff_h = j_no_diag @ cur_state + cur_h.reshape(num_spin, 1)
|
|
652
606
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
h_eff_grad = cur_state[ii, :] - (term1 - term2) / (term1 + term2 + 1)
|
|
607
|
+
exp_plus = np.empty_like(eff_h)
|
|
608
|
+
exp_minus = np.empty_like(eff_h)
|
|
609
|
+
denom = np.empty_like(eff_h)
|
|
657
610
|
|
|
658
|
-
|
|
611
|
+
for ii in range(num_spin):
|
|
612
|
+
exp_plus[ii, :] = np.exp(diag_j[ii] + eff_h[ii, :])
|
|
613
|
+
exp_minus[ii, :] = np.exp(diag_j[ii] - eff_h[ii, :])
|
|
614
|
+
denom[ii, :] = exp_plus[ii, :] + exp_minus[ii, :] + 1.0
|
|
659
615
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
cur_h_grad[ii] = np.mean(h_eff_grad)
|
|
616
|
+
# Diagonal (self‑coupling) gradients
|
|
617
|
+
term_diag = cur_state * cur_state - (exp_plus + exp_minus) / denom
|
|
663
618
|
|
|
664
|
-
|
|
665
|
-
|
|
619
|
+
# Off‑diagonal and field gradients
|
|
620
|
+
h_eff_grad = cur_state - (exp_plus - exp_minus) / denom
|
|
621
|
+
cur_j_grad = (h_eff_grad @ cur_state.T) * sample_factor
|
|
622
|
+
for ii in range(num_spin):
|
|
623
|
+
cur_j_grad[ii, ii] = np.sum(term_diag[ii, :]) * sample_factor
|
|
624
|
+
|
|
625
|
+
cur_h_grad = np.zeros((num_spin, 1), dtype=cur_j.dtype)
|
|
626
|
+
for ii in range(num_spin):
|
|
627
|
+
cur_h_grad[ii, 0] = np.sum(h_eff_grad[ii, :]) * sample_factor
|
|
628
|
+
|
|
629
|
+
if not directed:
|
|
630
|
+
cur_j_grad = 0.5 * (cur_j_grad + cur_j_grad.T)
|
|
666
631
|
|
|
667
632
|
return - cur_j_grad, - cur_h_grad
|
|
668
633
|
|
|
@@ -1029,13 +994,13 @@ def learn_network_adam(raw_data: Any,
|
|
|
1029
994
|
|
|
1030
995
|
# Save training log and print progress
|
|
1031
996
|
if counter in list_step:
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
997
|
+
if train_dat['save_log']:
|
|
998
|
+
sio.savemat(save_path + 'train_log.mat',
|
|
999
|
+
{'list_step': list_step,
|
|
1000
|
+
'rec_hvec_all': rec_hvec_all,
|
|
1001
|
+
'rec_jmat_all': rec_jmat_all,
|
|
1002
|
+
'rec_jgrad_sum_norm': rec_jgrad_sum_norm})
|
|
1003
|
+
|
|
1039
1004
|
pbar.update(rec_gap)
|
|
1040
1005
|
pbar.set_postfix({"Network Gradient": f"{rec_jgrad_sum_norm[counter - 1]:.6f}"})
|
|
1041
1006
|
|
|
@@ -130,7 +130,9 @@ class AbstractDSPIN(ABC):
|
|
|
130
130
|
def sample_list(self, value):
|
|
131
131
|
self._samp_list = value
|
|
132
132
|
|
|
133
|
-
def discretize(self,
|
|
133
|
+
def discretize(self,
|
|
134
|
+
clip_percentile: float = 100,
|
|
135
|
+
num_init: int = 10) -> None:
|
|
134
136
|
"""
|
|
135
137
|
Discretizes the oNMF representation into three states (-1, 0, 1) using K-means clustering.
|
|
136
138
|
|
|
@@ -138,6 +140,8 @@ class AbstractDSPIN(ABC):
|
|
|
138
140
|
----------
|
|
139
141
|
clip_percentile : float, optional
|
|
140
142
|
Percentile at which to clip values before discretization (default is 100).
|
|
143
|
+
num_init : int, optional
|
|
144
|
+
Number of initializations for K-means clustering (default is 10).
|
|
141
145
|
"""
|
|
142
146
|
onmf_rep_ori = self.program_representation_raw.copy()
|
|
143
147
|
fig_folder = self.fig_folder
|
|
@@ -147,7 +151,7 @@ class AbstractDSPIN(ABC):
|
|
|
147
151
|
onmf_rep_ori[:, ii] = onmf_rep_ori[:, ii] / np.percentile(onmf_rep_ori[:, ii], clip_percentile)
|
|
148
152
|
onmf_rep_ori = onmf_rep_ori.clip(0, 1)
|
|
149
153
|
|
|
150
|
-
onmf_rep_tri = onmf_discretize(onmf_rep_ori, fig_folder)
|
|
154
|
+
onmf_rep_tri = onmf_discretize(onmf_rep_ori, num_init, fig_folder)
|
|
151
155
|
self._onmf_rep_tri = onmf_rep_tri
|
|
152
156
|
|
|
153
157
|
def raw_data_corr(self, sample_id_key: str) -> None:
|
|
@@ -209,7 +213,8 @@ class AbstractDSPIN(ABC):
|
|
|
209
213
|
'cur_h': np.zeros((num_spin, num_sample)),
|
|
210
214
|
'save_path': self.save_path,
|
|
211
215
|
'rec_gap': 10,
|
|
212
|
-
'seed': 0
|
|
216
|
+
'seed': 0,
|
|
217
|
+
'save_log': False}
|
|
213
218
|
params.update({'lambda_l1_j': 0.01,
|
|
214
219
|
'lambda_l1_h': 0,
|
|
215
220
|
'lambda_l2_j': 0,
|
|
@@ -351,7 +356,7 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
351
356
|
adata: ad.AnnData,
|
|
352
357
|
save_path: str,
|
|
353
358
|
num_spin: int,
|
|
354
|
-
|
|
359
|
+
discretize_params: dict = {}):
|
|
355
360
|
"""
|
|
356
361
|
Initialize the GeneDSPIN object.
|
|
357
362
|
|
|
@@ -363,6 +368,8 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
363
368
|
Directory path where results will be saved.
|
|
364
369
|
num_spin : int
|
|
365
370
|
Number of spins used in inference.
|
|
371
|
+
use_default_discretize : bool, optional
|
|
372
|
+
Whether to use default discretization. Default is True. Otherwise the discretized data should be explicited setted in program_representation.
|
|
366
373
|
clip_percentile : float, optional
|
|
367
374
|
Percentile threshold for clipping values before discretization. Default is 95.
|
|
368
375
|
"""
|
|
@@ -375,7 +382,14 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
375
382
|
else:
|
|
376
383
|
self._onmf_rep_ori = adata.X
|
|
377
384
|
|
|
378
|
-
|
|
385
|
+
dparams = {'use_default_discretize': True,
|
|
386
|
+
'clip_percentile': 95,
|
|
387
|
+
'num_init': 10}
|
|
388
|
+
dparams.update(discretize_params)
|
|
389
|
+
|
|
390
|
+
if dparams['use_default_discretize']:
|
|
391
|
+
self.discretize(clip_percentile=dparams['clip_percentile'],
|
|
392
|
+
num_init=dparams['num_init'])
|
|
379
393
|
|
|
380
394
|
def program_regulator_discovery(self,
|
|
381
395
|
program_representation_raw: np.ndarray,
|
|
@@ -440,10 +454,7 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
440
454
|
def __init__(self,
|
|
441
455
|
adata: ad.AnnData,
|
|
442
456
|
save_path: str,
|
|
443
|
-
num_spin: int
|
|
444
|
-
num_onmf_components: int = None,
|
|
445
|
-
prior_programs: List[List[str]] = None,
|
|
446
|
-
num_repeat: int = 10):
|
|
457
|
+
num_spin: int):
|
|
447
458
|
"""
|
|
448
459
|
Initialize the ProgramDSPIN object.
|
|
449
460
|
|
|
@@ -455,12 +466,6 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
455
466
|
Directory path for saving results.
|
|
456
467
|
num_spin : int
|
|
457
468
|
Number of spins used in DSPIN.
|
|
458
|
-
num_onmf_components : int, optional
|
|
459
|
-
Number of oNMF components, default is None.
|
|
460
|
-
prior_programs : List[List[str]], optional
|
|
461
|
-
List of predefined gene programs.
|
|
462
|
-
num_repeat : int, optional
|
|
463
|
-
Number of repetitions for oNMF computation. Default is 10.
|
|
464
469
|
"""
|
|
465
470
|
super().__init__(adata, save_path, num_spin)
|
|
466
471
|
|
|
@@ -470,8 +475,6 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
470
475
|
self._gene_matrix_large = None
|
|
471
476
|
self._use_data_list = None
|
|
472
477
|
self.gene_program_csv = None
|
|
473
|
-
self.prior_programs = None
|
|
474
|
-
self.preprogram_num = len(prior_programs) if prior_programs else 0
|
|
475
478
|
|
|
476
479
|
@property
|
|
477
480
|
def onmf_decomposition(self):
|
|
@@ -546,32 +549,28 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
546
549
|
cluster_key: str = 'leiden',
|
|
547
550
|
mode: str = 'compute_summary',
|
|
548
551
|
prior_programs: List[List[str]] = None,
|
|
549
|
-
|
|
552
|
+
clip_percentile: float = 100,
|
|
553
|
+
params: dict = {},
|
|
554
|
+
discretize_params: dict = {}) -> None:
|
|
550
555
|
"""
|
|
551
556
|
Discover gene programs by performing oNMF decomposition on the annotated data.
|
|
552
557
|
|
|
553
558
|
Parameters
|
|
554
559
|
----------
|
|
555
|
-
num_onmf_components : int, optional
|
|
556
|
-
Number of oNMF components. Default is None.
|
|
557
|
-
num_subsample : int, optional
|
|
558
|
-
Number of samples to use after subsampling. Default is 10000.
|
|
559
|
-
num_subsample_large : int, optional
|
|
560
|
-
Number of samples for large subsampling. Default is None.
|
|
561
560
|
num_repeat : int, optional
|
|
562
561
|
Number of times to repeat oNMF. Default is 10.
|
|
563
562
|
seed : int, optional
|
|
564
563
|
Random seed for reproducibility. Default is 0.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
max_sample_rate : float, optional
|
|
570
|
-
Maximum sampling rate. Default is 2.
|
|
564
|
+
cluster_key : str, optional
|
|
565
|
+
Key in `adata.obs` for clustering labels. Default is 'leiden'.
|
|
566
|
+
mode: str, optional
|
|
567
|
+
Mode of operation. Options: 'compute_summary', 'compute_only', 'summary_only'. Default is 'compute_summary'.
|
|
571
568
|
prior_programs : List[List[str]], optional
|
|
572
569
|
List of predefined gene programs. Default is None.
|
|
573
|
-
|
|
574
|
-
|
|
570
|
+
clip_percentile : float, optional
|
|
571
|
+
Percentile for clipping values before discretization. Default is 100.
|
|
572
|
+
params : dict, optional
|
|
573
|
+
Additional parameters for oNMF. Default is an empty dictionary.
|
|
575
574
|
"""
|
|
576
575
|
|
|
577
576
|
if mode not in ['compute_summary', 'compute_only', 'summary_only']:
|
|
@@ -662,7 +661,12 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
662
661
|
self._onmf_rep_ori = onmf_summary.transform(
|
|
663
662
|
gene_matrix / self.matrix_std)
|
|
664
663
|
|
|
665
|
-
|
|
664
|
+
dparams = {'clip_percentile': 100,
|
|
665
|
+
'num_init': 10}
|
|
666
|
+
dparams.update(discretize_params)
|
|
667
|
+
|
|
668
|
+
self.discretize(clip_percentile=dparams['clip_percentile'],
|
|
669
|
+
num_init=dparams['num_init'])
|
|
666
670
|
|
|
667
671
|
|
|
668
672
|
class DSPIN(object):
|
|
@@ -21,7 +21,7 @@ install_requires = [
|
|
|
21
21
|
# ]
|
|
22
22
|
|
|
23
23
|
# Managing your version (adjust accordingly)
|
|
24
|
-
version = "1.3.
|
|
24
|
+
version = "1.3.2" # 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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|