dspin 1.3.0__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.0/dspin.egg-info → dspin-1.3.2}/PKG-INFO +1 -1
- {dspin-1.3.0 → dspin-1.3.2}/dspin/compute.py +97 -88
- {dspin-1.3.0 → dspin-1.3.2}/dspin/dspin.py +42 -37
- {dspin-1.3.0 → dspin-1.3.2/dspin.egg-info}/PKG-INFO +1 -1
- {dspin-1.3.0 → dspin-1.3.2}/setup.py +1 -1
- {dspin-1.3.0 → dspin-1.3.2}/LICENSE +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/README.md +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/__init__.cpython-310.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/__init__.cpython-37.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/compute.cpython-310.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/compute.cpython-37.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/dspin_abstract.cpython-310.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/plot.cpython-310.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/__pycache__/plotting.cpython-37.pyc +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin/__init__.py +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin/annotate.py +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin/plot.py +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin.egg-info/SOURCES.txt +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin.egg-info/dependency_links.txt +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin.egg-info/requires.txt +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/dspin.egg-info/top_level.txt +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/requirements.txt +0 -0
- {dspin-1.3.0 → dspin-1.3.2}/setup.cfg +0 -0
|
@@ -76,7 +76,8 @@ import networkx as nx
|
|
|
76
76
|
|
|
77
77
|
def summary_components(all_components: np.array,
|
|
78
78
|
num_spin: int,
|
|
79
|
-
summary_method: str = 'kmeans'
|
|
79
|
+
summary_method: str = 'kmeans',
|
|
80
|
+
figure_folder: str=None) -> List[np.array]:
|
|
80
81
|
"""
|
|
81
82
|
Summarize components using a clustering algorithm.
|
|
82
83
|
|
|
@@ -88,6 +89,8 @@ def summary_components(all_components: np.array,
|
|
|
88
89
|
The number of clusters.
|
|
89
90
|
summary_method : str, optional
|
|
90
91
|
The method used for summarizing the components. Options are 'kmeans' or 'leiden'. Default is 'kmeans'.
|
|
92
|
+
figure_folder : str, optional
|
|
93
|
+
Folder in which to save the figure.
|
|
91
94
|
|
|
92
95
|
Returns
|
|
93
96
|
-------
|
|
@@ -99,7 +102,12 @@ def summary_components(all_components: np.array,
|
|
|
99
102
|
if summary_method == 'kmeans':
|
|
100
103
|
# Fit KMeans clustering to the components and to their transposed version.
|
|
101
104
|
kmeans = KMeans(n_clusters=num_spin, random_state=0, n_init=50).fit(all_components)
|
|
102
|
-
|
|
105
|
+
|
|
106
|
+
# take the genes with larger weights for display only
|
|
107
|
+
gene_weight = np.sum(all_components ** 2, axis=0) ** 0.5
|
|
108
|
+
gene_sele_filt = gene_weight > np.mean(gene_weight)
|
|
109
|
+
all_components_sub = all_components[:, gene_sele_filt]
|
|
110
|
+
kmeans_gene = KMeans(n_clusters=2 * num_spin, random_state=0, n_init=10).fit(all_components_sub.T)
|
|
103
111
|
|
|
104
112
|
# Initialize an array to store the average component for each cluster.
|
|
105
113
|
components_kmeans = np.zeros((num_spin, num_gene))
|
|
@@ -115,6 +123,24 @@ def summary_components(all_components: np.array,
|
|
|
115
123
|
for ii in range(num_spin):
|
|
116
124
|
# Determine gene cluster assignments based on the maximum component values.
|
|
117
125
|
gene_groups_ind.append(np.argmax(components_kmeans, axis=0) == ii)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
sc.set_figure_params(figsize=(8, 4))
|
|
130
|
+
|
|
131
|
+
plt.subplot(1, 2, 1)
|
|
132
|
+
gene_order = np.argsort(kmeans_gene.labels_)
|
|
133
|
+
comp_order = np.argsort(kmeans.labels_)
|
|
134
|
+
plt.imshow(all_components_sub[comp_order, :][:, gene_order], aspect='auto', cmap='Blues', vmax=np.max(all_components) / 10, interpolation='none')
|
|
135
|
+
plt.title('All components')
|
|
136
|
+
|
|
137
|
+
plt.subplot(1, 2, 2)
|
|
138
|
+
plt.imshow(components_kmeans[:, gene_sele_filt][:, gene_order], aspect='auto', cmap='Blues', vmax=np.max(components_kmeans) / 10, interpolation='none')
|
|
139
|
+
plt.title('Kmeans components')
|
|
140
|
+
|
|
141
|
+
if figure_folder is not None:
|
|
142
|
+
plt.savefig(figure_folder + 'onmf_decomposition_summary.png', dpi=300, bbox_inches='tight')
|
|
143
|
+
plt.close()
|
|
118
144
|
|
|
119
145
|
elif summary_method == 'leiden':
|
|
120
146
|
|
|
@@ -144,8 +170,25 @@ def summary_components(all_components: np.array,
|
|
|
144
170
|
diff = optimiser.optimise_partition(partition, n_iterations=- 1, is_membership_fixed=list(membership_fixed))
|
|
145
171
|
|
|
146
172
|
gene_groups_ind = []
|
|
173
|
+
cluster_label_full = np.nan * np.ones(consensus.shape[0])
|
|
174
|
+
|
|
147
175
|
for ii, part in enumerate(partition):
|
|
148
176
|
gene_groups_ind.append(part)
|
|
177
|
+
cluster_label_full[part] = ii
|
|
178
|
+
|
|
179
|
+
gene_order = np.argsort(cluster_label_full)
|
|
180
|
+
gene_list_size = np.array([np.sum(cluster_label_full == ii) for ii in range(num_spin)])
|
|
181
|
+
|
|
182
|
+
plt.figure(figsize=[8, 8])
|
|
183
|
+
plt.imshow(consensus[gene_order][:, gene_order], cmap='Blues', vmin=0, vmax=np.percentile(consensus.flatten(), 98))
|
|
184
|
+
for ii in range(1, num_spin):
|
|
185
|
+
plt.axvline(np.sum(gene_list_size[:ii]) - 0.5, color='k')
|
|
186
|
+
plt.axhline(np.sum(gene_list_size[:ii]) - 0.5, color='k')
|
|
187
|
+
plt.grid()
|
|
188
|
+
|
|
189
|
+
if figure_folder is not None:
|
|
190
|
+
plt.savefig(figure_folder + 'onmf_decomposition_summary.png', dpi=300, bbox_inches='tight')
|
|
191
|
+
plt.close()
|
|
149
192
|
|
|
150
193
|
return gene_groups_ind
|
|
151
194
|
|
|
@@ -194,7 +237,9 @@ def onmf(X: np.array, rank: int, max_iter: int = 500) -> Tuple[np.array, np.arra
|
|
|
194
237
|
# progress bar
|
|
195
238
|
if itr % 10 == 0:
|
|
196
239
|
error = np.linalg.norm(X - np.dot(A, S), 'fro')
|
|
197
|
-
|
|
240
|
+
self_product = S.dot(S.T)
|
|
241
|
+
orthogonal_error = np.linalg.norm(self_product - np.diag(np.diag(self_product)), 'fro') / np.linalg.norm(S, 'fro')
|
|
242
|
+
pbar.set_postfix({"Reconstruction error": f"{error:.2f}", "Orthogonal error": f"{orthogonal_error:.4f}"})
|
|
198
243
|
|
|
199
244
|
pbar.close()
|
|
200
245
|
|
|
@@ -245,7 +290,9 @@ def compute_onmf(seed: int,
|
|
|
245
290
|
return nmf_model
|
|
246
291
|
|
|
247
292
|
|
|
248
|
-
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:
|
|
249
296
|
"""
|
|
250
297
|
Discretize the ONMF representation using KMeans clustering and visualize the sorted representations.
|
|
251
298
|
|
|
@@ -268,10 +315,11 @@ def onmf_discretize(onmf_rep_ori: np.array, fig_folder: str = None) -> np.array:
|
|
|
268
315
|
21, num_spin))
|
|
269
316
|
|
|
270
317
|
onmf_rep_tri = np.zeros(onmf_rep_ori.shape)
|
|
271
|
-
|
|
318
|
+
print('Processing gene or program expression levels...')
|
|
319
|
+
for ii in tqdm(range(num_spin)):
|
|
272
320
|
# Perform KMeans clustering for each spin/component
|
|
273
|
-
km_fit = KMeans(n_clusters=3, n_init=
|
|
274
|
-
|
|
321
|
+
km_fit = KMeans(n_clusters=3, n_init=num_init).fit(onmf_rep_ori[:, ii].reshape(-1, 1))
|
|
322
|
+
|
|
275
323
|
onmf_rep_tri[:, ii] = (km_fit.labels_ == np.argsort(km_fit.cluster_centers_.reshape(-1))[0]) * \
|
|
276
324
|
(-1) + (km_fit.labels_ == np.argsort(km_fit.cluster_centers_.reshape(-1))[2]) * 1
|
|
277
325
|
|
|
@@ -279,8 +327,7 @@ def onmf_discretize(onmf_rep_ori: np.array, fig_folder: str = None) -> np.array:
|
|
|
279
327
|
if ii < 21:
|
|
280
328
|
ax = plt.subplot(grid[ii])
|
|
281
329
|
plt.plot(np.sort(onmf_rep_ori[:, ii]))
|
|
282
|
-
plt.plot(np.sort(km_fit.cluster_centers_[
|
|
283
|
-
km_fit.labels_].reshape(-1)))
|
|
330
|
+
plt.plot(np.sort(km_fit.cluster_centers_[km_fit.labels_].reshape(-1)))
|
|
284
331
|
|
|
285
332
|
# Save the visual representation
|
|
286
333
|
if fig_folder is not None:
|
|
@@ -519,50 +566,7 @@ def para_moments(j_mat: np.array, h_vec: np.array) -> Tuple[np.array, np.array]:
|
|
|
519
566
|
return corr_para, mean_para
|
|
520
567
|
|
|
521
568
|
|
|
522
|
-
@numba.njit
|
|
523
|
-
def np_apply_along_axis(func1d: Callable[[np.ndarray], float],
|
|
524
|
-
axis: int,
|
|
525
|
-
arr: np.ndarray) -> np.ndarray:
|
|
526
|
-
"""
|
|
527
|
-
Applies a function along the specified axis.
|
|
528
|
-
|
|
529
|
-
Parameters
|
|
530
|
-
----------
|
|
531
|
-
func1d : Callable[[np.ndarray], float]
|
|
532
|
-
A 1-D function to be applied.
|
|
533
|
-
axis : int
|
|
534
|
-
Axis along which the function should be applied.
|
|
535
|
-
arr : np.ndarray
|
|
536
|
-
Input array.
|
|
537
|
-
|
|
538
|
-
Returns
|
|
539
|
-
-------
|
|
540
|
-
np.ndarray
|
|
541
|
-
The result of applying func1d to arr along the specified axis.
|
|
542
|
-
"""
|
|
543
|
-
assert arr.ndim == 2
|
|
544
|
-
assert axis in [0, 1]
|
|
545
|
-
result = np.empty(arr.shape[1]) if axis == 0 else np.empty(arr.shape[0])
|
|
546
|
-
|
|
547
|
-
for i in range(len(result)):
|
|
548
|
-
result[i] = func1d(arr[:, i]) if axis == 0 else func1d(arr[i, :])
|
|
549
|
-
|
|
550
|
-
return result
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
@numba.njit
|
|
554
|
-
def np_mean(array: np.array, axis: int) -> np.array:
|
|
555
|
-
"""
|
|
556
|
-
Computes the arithmetic mean along the specified axis.
|
|
557
|
-
|
|
558
|
-
Parameters:
|
|
559
|
-
array (np.array): Input array.
|
|
560
|
-
axis (int): Axis along which the mean is computed. 1 is for row-wise, 0 is for column-wise.
|
|
561
|
-
"""
|
|
562
|
-
return np_apply_along_axis(np.mean, axis, array)
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
@numba.jit()
|
|
569
|
+
@numba.njit(fastmath=True)
|
|
566
570
|
def pseudol_gradient(cur_j: np.ndarray,
|
|
567
571
|
cur_h: np.ndarray,
|
|
568
572
|
cur_state: np.ndarray,
|
|
@@ -587,37 +591,43 @@ def pseudol_gradient(cur_j: np.ndarray,
|
|
|
587
591
|
The gradient of j.
|
|
588
592
|
The gradient of h.
|
|
589
593
|
"""
|
|
590
|
-
num_spin =
|
|
594
|
+
num_spin, num_sample = cur_state.shape
|
|
595
|
+
sample_factor = 1.0 / num_sample
|
|
591
596
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
# Filtering diagonal elements of cur_j matrix.
|
|
596
|
-
j_filt = cur_j.copy()
|
|
597
|
-
np.fill_diagonal(j_filt, 0)
|
|
598
|
-
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]
|
|
599
600
|
|
|
601
|
+
j_no_diag = cur_j.copy()
|
|
600
602
|
for ii in range(num_spin):
|
|
601
|
-
|
|
602
|
-
j_sub = cur_j[ii, ii]
|
|
603
|
-
h_sub = effective_h[ii, :]
|
|
603
|
+
j_no_diag[ii, ii] = 0.0
|
|
604
604
|
|
|
605
|
-
|
|
606
|
-
term2 = np.exp(j_sub - h_sub)
|
|
605
|
+
eff_h = j_no_diag @ cur_state + cur_h.reshape(num_spin, 1)
|
|
607
606
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
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)
|
|
612
610
|
|
|
613
|
-
|
|
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
|
|
615
|
+
|
|
616
|
+
# Diagonal (self‑coupling) gradients
|
|
617
|
+
term_diag = cur_state * cur_state - (exp_plus + exp_minus) / denom
|
|
618
|
+
|
|
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
|
|
614
624
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
cur_h_grad[ii] = np.
|
|
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
|
|
618
628
|
|
|
619
|
-
|
|
620
|
-
|
|
629
|
+
if not directed:
|
|
630
|
+
cur_j_grad = 0.5 * (cur_j_grad + cur_j_grad.T)
|
|
621
631
|
|
|
622
632
|
return - cur_j_grad, - cur_h_grad
|
|
623
633
|
|
|
@@ -927,6 +937,8 @@ def learn_network_adam(raw_data: Any,
|
|
|
927
937
|
The trained h matrix.
|
|
928
938
|
The training log.
|
|
929
939
|
"""
|
|
940
|
+
np.random.seed(train_dat['seed'])
|
|
941
|
+
|
|
930
942
|
# Retrieve training parameters and data
|
|
931
943
|
num_spin, num_round = train_dat['cur_h'].shape
|
|
932
944
|
num_epoch, stepsz, rec_gap = (
|
|
@@ -936,10 +948,7 @@ def learn_network_adam(raw_data: Any,
|
|
|
936
948
|
list_step = np.arange(num_epoch, 0, - rec_gap)[::-1]
|
|
937
949
|
cur_j, cur_h = (train_dat.get(key, None) for key in ["cur_j", "cur_h"])
|
|
938
950
|
save_path = train_dat.get('save_path', None)
|
|
939
|
-
backtrack_gap, backtrack_tol = (
|
|
940
|
-
train_dat.get(
|
|
941
|
-
key, None) for key in [
|
|
942
|
-
"backtrack_gap", "backtrack_tol"])
|
|
951
|
+
backtrack_gap, backtrack_tol = (train_dat.get(key, None) for key in ["backtrack_gap", "backtrack_tol"])
|
|
943
952
|
|
|
944
953
|
# Initialize variables to store parameters, gradients, and other values
|
|
945
954
|
# during training
|
|
@@ -985,15 +994,15 @@ def learn_network_adam(raw_data: Any,
|
|
|
985
994
|
|
|
986
995
|
# Save training log and print progress
|
|
987
996
|
if counter in list_step:
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
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
|
+
|
|
995
1004
|
pbar.update(rec_gap)
|
|
996
|
-
pbar.set_postfix({"Network Gradient": f"{rec_jgrad_sum_norm[counter - 1]:.
|
|
1005
|
+
pbar.set_postfix({"Network Gradient": f"{rec_jgrad_sum_norm[counter - 1]:.6f}"})
|
|
997
1006
|
|
|
998
1007
|
# Handle backtracking
|
|
999
1008
|
|
|
@@ -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:
|
|
@@ -204,17 +208,19 @@ class AbstractDSPIN(ABC):
|
|
|
204
208
|
self._raw_data = raw_data
|
|
205
209
|
|
|
206
210
|
num_sample = len(raw_data)
|
|
207
|
-
params = {'num_epoch':
|
|
211
|
+
params = {'num_epoch': 400,
|
|
208
212
|
'cur_j': np.zeros((num_spin, num_spin)),
|
|
209
213
|
'cur_h': np.zeros((num_spin, num_sample)),
|
|
210
214
|
'save_path': self.save_path,
|
|
211
|
-
'rec_gap': 10
|
|
215
|
+
'rec_gap': 10,
|
|
216
|
+
'seed': 0,
|
|
217
|
+
'save_log': False}
|
|
212
218
|
params.update({'lambda_l1_j': 0.01,
|
|
213
219
|
'lambda_l1_h': 0,
|
|
214
220
|
'lambda_l2_j': 0,
|
|
215
221
|
'lambda_l2_h': 0.005})
|
|
216
|
-
params.update({'backtrack_gap':
|
|
217
|
-
'backtrack_tol':
|
|
222
|
+
params.update({'backtrack_gap': 40,
|
|
223
|
+
'backtrack_tol': 5})
|
|
218
224
|
|
|
219
225
|
if method == 'maximum_likelihood':
|
|
220
226
|
params['stepsz'] = 0.2
|
|
@@ -350,7 +356,7 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
350
356
|
adata: ad.AnnData,
|
|
351
357
|
save_path: str,
|
|
352
358
|
num_spin: int,
|
|
353
|
-
|
|
359
|
+
discretize_params: dict = {}):
|
|
354
360
|
"""
|
|
355
361
|
Initialize the GeneDSPIN object.
|
|
356
362
|
|
|
@@ -362,6 +368,8 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
362
368
|
Directory path where results will be saved.
|
|
363
369
|
num_spin : int
|
|
364
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.
|
|
365
373
|
clip_percentile : float, optional
|
|
366
374
|
Percentile threshold for clipping values before discretization. Default is 95.
|
|
367
375
|
"""
|
|
@@ -374,7 +382,14 @@ class GeneDSPIN(AbstractDSPIN):
|
|
|
374
382
|
else:
|
|
375
383
|
self._onmf_rep_ori = adata.X
|
|
376
384
|
|
|
377
|
-
|
|
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'])
|
|
378
393
|
|
|
379
394
|
def program_regulator_discovery(self,
|
|
380
395
|
program_representation_raw: np.ndarray,
|
|
@@ -439,10 +454,7 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
439
454
|
def __init__(self,
|
|
440
455
|
adata: ad.AnnData,
|
|
441
456
|
save_path: str,
|
|
442
|
-
num_spin: int
|
|
443
|
-
num_onmf_components: int = None,
|
|
444
|
-
prior_programs: List[List[str]] = None,
|
|
445
|
-
num_repeat: int = 10):
|
|
457
|
+
num_spin: int):
|
|
446
458
|
"""
|
|
447
459
|
Initialize the ProgramDSPIN object.
|
|
448
460
|
|
|
@@ -454,12 +466,6 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
454
466
|
Directory path for saving results.
|
|
455
467
|
num_spin : int
|
|
456
468
|
Number of spins used in DSPIN.
|
|
457
|
-
num_onmf_components : int, optional
|
|
458
|
-
Number of oNMF components, default is None.
|
|
459
|
-
prior_programs : List[List[str]], optional
|
|
460
|
-
List of predefined gene programs.
|
|
461
|
-
num_repeat : int, optional
|
|
462
|
-
Number of repetitions for oNMF computation. Default is 10.
|
|
463
469
|
"""
|
|
464
470
|
super().__init__(adata, save_path, num_spin)
|
|
465
471
|
|
|
@@ -469,8 +475,6 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
469
475
|
self._gene_matrix_large = None
|
|
470
476
|
self._use_data_list = None
|
|
471
477
|
self.gene_program_csv = None
|
|
472
|
-
self.prior_programs = None
|
|
473
|
-
self.preprogram_num = len(prior_programs) if prior_programs else 0
|
|
474
478
|
|
|
475
479
|
@property
|
|
476
480
|
def onmf_decomposition(self):
|
|
@@ -518,7 +522,7 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
518
522
|
|
|
519
523
|
all_components = rec_components.reshape(-1, np.sum(prior_programs_mask))
|
|
520
524
|
|
|
521
|
-
gene_group_ind = summary_components(all_components, num_onmf_components, summary_method=summary_method)
|
|
525
|
+
gene_group_ind = summary_components(all_components, num_onmf_components, summary_method=summary_method, figure_folder=self.fig_folder)
|
|
522
526
|
|
|
523
527
|
# Mask the prior programs and add them to the gene group indices
|
|
524
528
|
sub_mask_ind = np.where(prior_programs_mask)[0]
|
|
@@ -545,32 +549,28 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
545
549
|
cluster_key: str = 'leiden',
|
|
546
550
|
mode: str = 'compute_summary',
|
|
547
551
|
prior_programs: List[List[str]] = None,
|
|
548
|
-
|
|
552
|
+
clip_percentile: float = 100,
|
|
553
|
+
params: dict = {},
|
|
554
|
+
discretize_params: dict = {}) -> None:
|
|
549
555
|
"""
|
|
550
556
|
Discover gene programs by performing oNMF decomposition on the annotated data.
|
|
551
557
|
|
|
552
558
|
Parameters
|
|
553
559
|
----------
|
|
554
|
-
num_onmf_components : int, optional
|
|
555
|
-
Number of oNMF components. Default is None.
|
|
556
|
-
num_subsample : int, optional
|
|
557
|
-
Number of samples to use after subsampling. Default is 10000.
|
|
558
|
-
num_subsample_large : int, optional
|
|
559
|
-
Number of samples for large subsampling. Default is None.
|
|
560
560
|
num_repeat : int, optional
|
|
561
561
|
Number of times to repeat oNMF. Default is 10.
|
|
562
562
|
seed : int, optional
|
|
563
563
|
Random seed for reproducibility. Default is 0.
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
max_sample_rate : float, optional
|
|
569
|
-
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'.
|
|
570
568
|
prior_programs : List[List[str]], optional
|
|
571
569
|
List of predefined gene programs. Default is None.
|
|
572
|
-
|
|
573
|
-
|
|
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.
|
|
574
574
|
"""
|
|
575
575
|
|
|
576
576
|
if mode not in ['compute_summary', 'compute_only', 'summary_only']:
|
|
@@ -661,7 +661,12 @@ class ProgramDSPIN(AbstractDSPIN):
|
|
|
661
661
|
self._onmf_rep_ori = onmf_summary.transform(
|
|
662
662
|
gene_matrix / self.matrix_std)
|
|
663
663
|
|
|
664
|
-
|
|
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'])
|
|
665
670
|
|
|
666
671
|
|
|
667
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
|