autoencodix 0.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of autoencodix might be problematic. Click here for more details.

Files changed (91) hide show
  1. autoencodix/__init__.py +27 -0
  2. autoencodix/base/.ipynb_checkpoints/_base_pipeline-checkpoint.py +1274 -0
  3. autoencodix/base/.ipynb_checkpoints/_base_trainer-checkpoint.py +247 -0
  4. autoencodix/base/__init__.py +21 -0
  5. autoencodix/base/_base_autoencoder.py +138 -0
  6. autoencodix/base/_base_dataset.py +103 -0
  7. autoencodix/base/_base_evaluator.py +34 -0
  8. autoencodix/base/_base_loss.py +311 -0
  9. autoencodix/base/_base_pipeline.py +1355 -0
  10. autoencodix/base/_base_predictor.py +6 -0
  11. autoencodix/base/_base_preprocessor.py +1033 -0
  12. autoencodix/base/_base_trainer.py +259 -0
  13. autoencodix/base/_base_visualizer.py +578 -0
  14. autoencodix/base/sampleconfig.yaml +132 -0
  15. autoencodix/configs/__init__.py +21 -0
  16. autoencodix/configs/default_config.py +652 -0
  17. autoencodix/configs/disentanglix_config.py +15 -0
  18. autoencodix/configs/maskix_config.py +39 -0
  19. autoencodix/configs/ontix_config.py +35 -0
  20. autoencodix/configs/stackix_config.py +33 -0
  21. autoencodix/configs/vanillix_config.py +18 -0
  22. autoencodix/configs/varix_config.py +15 -0
  23. autoencodix/configs/xmodalix_config.py +43 -0
  24. autoencodix/disentanglix.py +72 -0
  25. autoencodix/evaluate/.ipynb_checkpoints/evaluate-checkpoint.py +532 -0
  26. autoencodix/evaluate/__init__.py +4 -0
  27. autoencodix/evaluate/_general_evaluator.py +730 -0
  28. autoencodix/evaluate/_xmodalix_evaluator.py +376 -0
  29. autoencodix/imagix.py +76 -0
  30. autoencodix/losses/disentanglix_loss.py +215 -0
  31. autoencodix/losses/maskix_loss.py +94 -0
  32. autoencodix/losses/vanillix_loss.py +28 -0
  33. autoencodix/losses/varix_loss.py +102 -0
  34. autoencodix/losses/xmodal_loss.py +304 -0
  35. autoencodix/maskix.py +108 -0
  36. autoencodix/modeling/.ipynb_checkpoints/__init__-checkpoint.py +16 -0
  37. autoencodix/modeling/.ipynb_checkpoints/_captum_forward-checkpoint.py +16 -0
  38. autoencodix/modeling/__init__.py +20 -0
  39. autoencodix/modeling/_captum_forward.py +20 -0
  40. autoencodix/modeling/_classifier.py +45 -0
  41. autoencodix/modeling/_imagevae_architecture.py +296 -0
  42. autoencodix/modeling/_imgfast_architecture.py +288 -0
  43. autoencodix/modeling/_layer_factory.py +71 -0
  44. autoencodix/modeling/_maskix_architecture.py +190 -0
  45. autoencodix/modeling/_ontix_architecture.py +305 -0
  46. autoencodix/modeling/_vanillix_architecture.py +136 -0
  47. autoencodix/modeling/_varix_architecture.py +192 -0
  48. autoencodix/notebooks/__init__.py +0 -0
  49. autoencodix/ontix.py +136 -0
  50. autoencodix/py.typed +0 -0
  51. autoencodix/stackix.py +122 -0
  52. autoencodix/trainers/__init__.py +6 -0
  53. autoencodix/trainers/_general_trainer.py +525 -0
  54. autoencodix/trainers/_maskix_trainer.py +116 -0
  55. autoencodix/trainers/_ontix_trainer.py +77 -0
  56. autoencodix/trainers/_stackix_orchestrator.py +522 -0
  57. autoencodix/trainers/_stackix_trainer.py +209 -0
  58. autoencodix/trainers/_xmodal_trainer.py +1006 -0
  59. autoencodix/utils/.ipynb_checkpoints/_explainer-checkpoint.py +118 -0
  60. autoencodix/utils/.ipynb_checkpoints/_model_output-checkpoint.py +30 -0
  61. autoencodix/utils/.ipynb_checkpoints/_utils-checkpoint.py +626 -0
  62. autoencodix/utils/.ipynb_checkpoints/adata_converter-checkpoint.py +99 -0
  63. autoencodix/utils/.ipynb_checkpoints/feature_importance-checkpoint.py +823 -0
  64. autoencodix/utils/__init__.py +22 -0
  65. autoencodix/utils/_annealer.py +193 -0
  66. autoencodix/utils/_bulkreader.py +232 -0
  67. autoencodix/utils/_explainer.py +246 -0
  68. autoencodix/utils/_imgreader.py +404 -0
  69. autoencodix/utils/_internals.py +23 -0
  70. autoencodix/utils/_llm_explainer.py +318 -0
  71. autoencodix/utils/_losses.py +20 -0
  72. autoencodix/utils/_model_output.py +30 -0
  73. autoencodix/utils/_result.py +393 -0
  74. autoencodix/utils/_screader.py +76 -0
  75. autoencodix/utils/_traindynamics.py +187 -0
  76. autoencodix/utils/_utils.py +690 -0
  77. autoencodix/utils/adata_converter.py +111 -0
  78. autoencodix/utils/example_data.py +398 -0
  79. autoencodix/utils/feature_importance.py +829 -0
  80. autoencodix/utils/prompts.py +21 -0
  81. autoencodix/vanillix.py +141 -0
  82. autoencodix/varix.py +80 -0
  83. autoencodix/visualize/__init__.py +5 -0
  84. autoencodix/visualize/_general_visualizer.py +913 -0
  85. autoencodix/visualize/_imagix_visualizer.py +111 -0
  86. autoencodix/visualize/_xmodal_visualizer.py +841 -0
  87. autoencodix/visualize/visualize.py +1214 -0
  88. autoencodix/xmodalix.py +318 -0
  89. autoencodix-0.2.0.dist-info/METADATA +143 -0
  90. autoencodix-0.2.0.dist-info/RECORD +91 -0
  91. autoencodix-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,111 @@
1
+ from typing import Literal, Dict, Optional
2
+ from autoencodix.data._multimodal_dataset import MultiModalDataset
3
+ from autoencodix.data._numeric_dataset import NumericDataset
4
+ from autoencodix.data._datasetcontainer import DatasetContainer
5
+ import anndata as ad
6
+ import pandas as pd
7
+ import torch
8
+ from scipy.sparse import issparse
9
+
10
+
11
+ class AnnDataConverter:
12
+ """Utility class for converting datasets into AnnData or multimodal AnnData dictionaries."""
13
+
14
+ @staticmethod
15
+ def _numeric_ds_to_adata(ds: NumericDataset) -> Dict[str, ad.AnnData]:
16
+ """Convert a NumericDataset to an AnnData object.
17
+
18
+ Args:
19
+ ds: The numeric dataset to convert.
20
+
21
+ Returns:
22
+ An AnnData object containing the dataset's data, features, and metadata.
23
+ """
24
+
25
+ metadata = ds.metadata.copy()
26
+ if isinstance(metadata, dict):
27
+ if len(metadata) != 1:
28
+ raise NotImplementedError(
29
+ "Unpaired metadata conversion not implemented yet. Expected single modality."
30
+ )
31
+ first_key = next(iter(metadata.keys()))
32
+ metadata = metadata[first_key]
33
+ if not isinstance(metadata, pd.DataFrame):
34
+ raise ValueError(f"metadata needs to be pd.DataFrame, got {type(metadata)}")
35
+ metadata.index = metadata.index.astype(str)
36
+
37
+ var = pd.DataFrame(index=pd.Index(ds.feature_ids, dtype=str))
38
+ # check if ds.data issparse
39
+ if issparse(ds.data):
40
+ x = torch.tensor(ds.data.toarray())
41
+ else:
42
+ x = ds.data
43
+ return {
44
+ "global": ad.AnnData(
45
+ X=x.clone().detach().cpu().numpy(),
46
+ var=var,
47
+ obs=metadata,
48
+ )
49
+ }
50
+
51
+ @staticmethod
52
+ def _parse_multimodal(mds: MultiModalDataset) -> Dict[str, ad.AnnData]:
53
+ """Convert a MultiModalDataset into a dictionary of AnnData objects.
54
+
55
+ Args:
56
+ mds: The multimodal dataset to convert.
57
+
58
+ Returns:
59
+ A dictionary mapping modality names to AnnData objects.
60
+
61
+ Raises:
62
+ NotImplementedError: If any modality is not a NumericDataset.
63
+ """
64
+ result_dict: Dict[str, ad.AnnData] = {}
65
+ for mod_name, dataset in mds.datasets.items():
66
+ if not isinstance(dataset, NumericDataset):
67
+ raise NotImplementedError(
68
+ f"Feature Importance is only implemented for NumericDataset, got type: {type(dataset)}"
69
+ )
70
+ result_dict[mod_name] = AnnDataConverter._numeric_ds_to_adata(dataset) # type: ignore
71
+ return result_dict
72
+
73
+ @staticmethod
74
+ def dataset_to_adata(
75
+ datasetcontainer: DatasetContainer,
76
+ split: Literal["train", "valid", "test"] = "train",
77
+ ) -> Optional[Dict[str, ad.AnnData]]:
78
+ """Convert a DatasetContainer split to an AnnData or multimodal AnnData dictionary.
79
+
80
+ Args:
81
+ datasetcontainer: Container holding train/valid/test datasets.
82
+ split: The dataset split to convert. Defaults to "train".
83
+
84
+ Returns:
85
+ A single AnnData object (for NumericDataset) or a dictionary of AnnData objects (for MultiModalDataset).
86
+
87
+ Raises:
88
+ ValueError: If the specified split does not exist in the DatasetContainer.
89
+ NotImplementedError: If the dataset type is not supported.
90
+ """
91
+ if not hasattr(datasetcontainer, split):
92
+ raise ValueError(
93
+ f"Split: {split} not present in DatasetContainer: {datasetcontainer}"
94
+ )
95
+
96
+ ds = datasetcontainer[split]
97
+
98
+ if isinstance(ds, MultiModalDataset):
99
+ return AnnDataConverter._parse_multimodal(ds)
100
+ elif isinstance(ds, NumericDataset):
101
+ return AnnDataConverter._numeric_ds_to_adata(ds)
102
+ elif ds is None:
103
+ import warnings
104
+
105
+ warnings.warn(f"No dataset found for split: {split}, returning None")
106
+ return None
107
+
108
+ else:
109
+ raise NotImplementedError(
110
+ f"Conversion not implemented for type: {type(ds)}"
111
+ )
@@ -0,0 +1,398 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ from typing import Tuple
4
+ import torch
5
+ from sklearn.datasets import make_blobs # type: ignore
6
+ from sklearn.model_selection import train_test_split # type: ignore
7
+ from autoencodix.data._datasetcontainer import DatasetContainer
8
+ from autoencodix.data._numeric_dataset import NumericDataset
9
+ from autoencodix.configs.default_config import DefaultConfig
10
+ from autoencodix.data.datapackage import DataPackage
11
+ from autoencodix.configs.default_config import DataCase
12
+ import mudata
13
+ import anndata
14
+
15
+ config = DefaultConfig()
16
+
17
+
18
+ def generate_example_data(
19
+ n_samples=1000, n_features=30, n_clusters=5, random_seed=config.global_seed
20
+ ):
21
+ """
22
+ Generate synthetic data for autoencoder testing and examples.
23
+
24
+ Args:
25
+ n_samples: Number of samples to generate, default=1000
26
+ n_features: Number of features for each sample, default=30
27
+ n_clusters: Number of clusters to generate, default=5
28
+ random_seed:: Random seed for reproducibility
29
+
30
+ Returns:
31
+ DatasetContainer: Container with train, validation and test datasets
32
+ """
33
+ np.random.seed(random_seed)
34
+ torch.manual_seed(random_seed)
35
+
36
+ X, cluster_labels = make_blobs(
37
+ n_samples=n_samples,
38
+ n_features=n_features,
39
+ centers=n_clusters,
40
+ cluster_std=1.8,
41
+ random_state=random_seed,
42
+ )
43
+
44
+ # Convert integer cluster labels to string labels like "Cluster_1"
45
+ str_cluster_labels = np.array([f"Cluster_{i + 1}" for i in cluster_labels])
46
+ # Create additional metadata features that correlate with the clusters
47
+ # Convert integer cluster labels to string labels like "Cluster_1"
48
+ str_cluster_labels = np.array([f"Cluster_{i + 1}" for i in cluster_labels])
49
+
50
+ metadata_df = pd.DataFrame(
51
+ {
52
+ "cluster": str_cluster_labels,
53
+ "age": np.random.normal(30, 10, n_samples)
54
+ + cluster_labels * 5, # Age correlates with cluster
55
+ "size": np.random.uniform(0, 10, n_samples)
56
+ + cluster_labels * 2, # Size correlates with cluster
57
+ "density": np.random.exponential(1, n_samples)
58
+ * (cluster_labels + 1)
59
+ / 3, # Density correlates with cluster
60
+ "category": np.random.choice(
61
+ ["A", "B", "C", "D", "E"], n_samples, p=[0.2, 0.2, 0.2, 0.2, 0.2]
62
+ ), # Random category
63
+ }
64
+ )
65
+
66
+ # Add some noise features to metadata that don't correlate with clusters
67
+ metadata_df["random_feature"] = np.random.normal(0, 1, n_samples)
68
+
69
+ ids = [f"sample_{i}" for i in range(n_samples)]
70
+ metadata_df["sample_id"] = ids
71
+ metadata_df.index = ids # Set sample_id as index
72
+ data_tensor = torch.tensor(X, dtype=torch.float32)
73
+
74
+ # Get split ratios from DefaultConfig
75
+ config = DefaultConfig()
76
+ valid_ratio = config.valid_ratio
77
+ test_ratio = config.test_ratio
78
+
79
+ # Calculate test_size for first split (everything except train)
80
+ first_test_size = valid_ratio + test_ratio
81
+
82
+ # Calculate the proportion of valid in the remaining data
83
+ # If valid_ratio + test_ratio = 0.3 and valid_ratio = 0.1, then valid should be 1/3 of the remaining data
84
+ valid_proportion = valid_ratio / first_test_size if first_test_size > 0 else 0
85
+
86
+ train_idx, temp_idx = train_test_split(
87
+ np.arange(n_samples),
88
+ test_size=first_test_size,
89
+ random_state=random_seed,
90
+ stratify=cluster_labels, # Ensure balanced classes in each split
91
+ )
92
+ val_idx, test_idx = train_test_split(
93
+ temp_idx,
94
+ test_size=(1 - valid_proportion), # Remaining proportion goes to test
95
+ random_state=random_seed,
96
+ stratify=cluster_labels[temp_idx], # Ensure balanced classes in each split
97
+ )
98
+
99
+ # Create split indicators as numpy arrays
100
+ train_split = np.zeros(n_samples, dtype=bool)
101
+ train_split[train_idx] = True
102
+
103
+ val_split = np.zeros(n_samples, dtype=bool)
104
+ val_split[val_idx] = True
105
+
106
+ test_split = np.zeros(n_samples, dtype=bool)
107
+ test_split[test_idx] = True
108
+
109
+ train_dataset = NumericDataset(
110
+ data=data_tensor[train_idx],
111
+ config=DefaultConfig(),
112
+ sample_ids=[ids[i] for i in train_idx],
113
+ metadata=metadata_df.iloc[train_idx],
114
+ split_indices=train_split,
115
+ feature_ids=[f"feature_{i}" for i in range(n_features)],
116
+ )
117
+
118
+ val_dataset = NumericDataset(
119
+ data=data_tensor[val_idx],
120
+ config=DefaultConfig(),
121
+ sample_ids=[ids[i] for i in val_idx],
122
+ metadata=metadata_df.iloc[val_idx],
123
+ split_indices=val_split,
124
+ feature_ids=[f"feature_{i}" for i in range(n_features)],
125
+ )
126
+
127
+ test_dataset = NumericDataset(
128
+ data=data_tensor[test_idx],
129
+ config=DefaultConfig(),
130
+ sample_ids=[ids[i] for i in test_idx],
131
+ metadata=metadata_df.iloc[test_idx],
132
+ split_indices=test_split,
133
+ feature_ids=[f"feature_{i}" for i in range(n_features)],
134
+ )
135
+
136
+ return DatasetContainer(train=train_dataset, valid=val_dataset, test=test_dataset)
137
+
138
+
139
+ def generate_multi_bulk_example(
140
+ n_samples=500,
141
+ n_features_modality1=100,
142
+ n_features_modality2=80,
143
+ random_seed=config.global_seed,
144
+ ) -> Tuple[DataPackage, pd.DataFrame, pd.DataFrame, pd.DataFrame]:
145
+ """Generate example data for MULTI_BULK case.
146
+
147
+ Args:
148
+ n_samples: Number of samples to generate
149
+ n_features_modality1: Number of features for first modality
150
+ n_features_modality2: Number of features for second modality
151
+ random_seed: Random seed for reproducibility
152
+
153
+ Returns:
154
+ DataPackage with multi_bulk data and raw_data
155
+ """
156
+ np.random.seed(random_seed)
157
+
158
+ latent_dim = 10
159
+ latent_representation = np.random.normal(0, 1, (n_samples, latent_dim))
160
+
161
+ weights_mod1 = np.random.normal(0, 1, (latent_dim, n_features_modality1))
162
+ weights_mod2 = np.random.normal(0, 1, (latent_dim, n_features_modality2))
163
+
164
+ # Generate observed data with noise
165
+ data_mod1 = np.dot(latent_representation, weights_mod1) + np.random.normal(
166
+ 0, 0.5, (n_samples, n_features_modality1)
167
+ )
168
+ data_mod2 = np.dot(latent_representation, weights_mod2) + np.random.normal(
169
+ 0, 0.5, (n_samples, n_features_modality2)
170
+ )
171
+
172
+ mod1_features = [f"gene_{i}" for i in range(n_features_modality1)]
173
+ mod2_features = [f"protein_{i}" for i in range(n_features_modality2)]
174
+
175
+ sample_ids = [f"sample_{i}" for i in range(n_samples)]
176
+
177
+ # Create DataFrames
178
+ df_mod1 = pd.DataFrame(data_mod1, columns=mod1_features, index=sample_ids)
179
+ df_mod2 = pd.DataFrame(data_mod2, columns=mod2_features, index=sample_ids)
180
+
181
+ # Create annotation DataFrame with sample information
182
+ # Adding some metadata that correlates with the latent structure
183
+ group_assignments = np.argmax(latent_representation[:, :3], axis=1)
184
+ conditions = ["condition_" + str(g) for g in group_assignments]
185
+ batch_effects = np.random.choice(["batch_1", "batch_2", "batch_3"], size=n_samples)
186
+
187
+ annotation_df = pd.DataFrame(
188
+ {
189
+ "condition": conditions,
190
+ "batch": batch_effects,
191
+ "quality_score": np.random.uniform(0.6, 1.0, n_samples),
192
+ },
193
+ index=sample_ids,
194
+ )
195
+
196
+ data_package = DataPackage()
197
+ data_package.multi_bulk = {"transcriptomics": df_mod1, "proteomics": df_mod2}
198
+ data_package.annotation = {
199
+ "transcriptomics": annotation_df.copy(),
200
+ "proteomics": annotation_df.copy(),
201
+ }
202
+
203
+ return data_package, df_mod1, df_mod2, annotation_df
204
+
205
+
206
+ def generate_default_bulk_bulk_example(
207
+ n_samples=500, n_features=200, random_seed=config.global_seed
208
+ ):
209
+ """NOT IMPLEMENTED YET"""
210
+ np.random.seed(random_seed)
211
+ raise NotImplementedError("BULK_TO_BULK example generation not yet implemented")
212
+
213
+
214
+ def generate_default_sc_sc_example(
215
+ n_cells=1000, n_features=500, random_seed=config.global_seed
216
+ ):
217
+ """NOT IMPLEMENTED YET"""
218
+ np.random.seed(random_seed)
219
+
220
+ raise NotImplementedError(
221
+ "SINGLE_CELL_TO_SINGLE_CELL example generation not yet implemented"
222
+ )
223
+
224
+
225
+ def generate_default_img_bulk_example(n_samples=100, random_seed=config.global_seed):
226
+ """NOT IMPLEMENTED YET"""
227
+ np.random.seed(random_seed)
228
+
229
+ # Not implemented yet
230
+ raise NotImplementedError("IMG_TO_BULK example generation not yet implemented")
231
+
232
+
233
+ def generate_default_sc_img_example(n_samples=100, random_seed=config.global_seed):
234
+ """NOT IMPLEMENTED YET"""
235
+ np.random.seed(random_seed)
236
+
237
+ raise NotImplementedError(
238
+ "SINGLE_CELL_TO_IMG example generation not yet implemented"
239
+ )
240
+
241
+
242
+ def generate_default_img_img_example(n_samples=100, random_seed=config.global_seed):
243
+ """NOT IMPLEMENTED YET"""
244
+ np.random.seed(random_seed)
245
+
246
+ raise NotImplementedError("IMG_TO_IMG example generation not yet implemented")
247
+
248
+
249
+ def generate_raw_datapackage(data_case: DataCase, **kwargs):
250
+ """Generate a raw DataPackage for the specified data case.
251
+
252
+ Args:
253
+ data_case: The type of data to generate
254
+ **kwargs: Additional arguments to pass to the specific generator function
255
+
256
+ Returns:
257
+ Raw data package ready for preprocessing
258
+ """
259
+ generator_map = {
260
+ DataCase.MULTI_BULK: generate_multi_bulk_example,
261
+ DataCase.MULTI_SINGLE_CELL: generate_multi_sc_example,
262
+ DataCase.BULK_TO_BULK: generate_default_bulk_bulk_example,
263
+ DataCase.SINGLE_CELL_TO_SINGLE_CELL: generate_default_sc_sc_example,
264
+ DataCase.IMG_TO_BULK: generate_default_img_bulk_example,
265
+ DataCase.SINGLE_CELL_TO_IMG: generate_default_sc_img_example,
266
+ DataCase.IMG_TO_IMG: generate_default_img_img_example,
267
+ }
268
+
269
+ if data_case not in generator_map:
270
+ raise ValueError(f"Unknown data case: {data_case}")
271
+
272
+ generator_function = generator_map[data_case]
273
+ return generator_function(**kwargs)
274
+
275
+
276
+ def generate_multi_sc_example(
277
+ n_cells=1000, n_genes=500, n_proteins=200, random_seed=config.global_seed
278
+ ):
279
+ """Generate example data for MULTI_SINGLE_CELL case.
280
+
281
+ Args:
282
+ n_cells: Number of cells to generate
283
+ n_genes: Number of genes for RNA modality
284
+ n_proteins: Number of proteins for protein modality
285
+ random_seed: Random seed for reproducibility
286
+
287
+ Returns:
288
+ DataPackage with multi_sc data
289
+ """
290
+ np.random.seed(random_seed)
291
+
292
+ cell_ids = [f"cell_{i}" for i in range(n_cells)]
293
+
294
+ gene_names = [f"gene_{i}" for i in range(n_genes)]
295
+ protein_names = [f"protein_{i}" for i in range(n_proteins)]
296
+
297
+ n_cell_types = 5
298
+ cell_type_probabilities = np.random.dirichlet(np.ones(n_cell_types), size=1)[0]
299
+ cell_types = np.random.choice(
300
+ np.arange(n_cell_types), size=n_cells, p=cell_type_probabilities
301
+ )
302
+
303
+ cell_metadata = pd.DataFrame(
304
+ {
305
+ "cell_type": [f"type_{t}" for t in cell_types],
306
+ "batch": np.random.choice(["batch1", "batch2", "batch3"], size=n_cells),
307
+ "donor": np.random.choice(
308
+ ["donor1", "donor2", "donor3", "donor4"], size=n_cells
309
+ ),
310
+ "cell_cycle": np.random.choice(["G1", "S", "G2M"], size=n_cells),
311
+ },
312
+ index=cell_ids,
313
+ )
314
+
315
+ # Generate RNA data (sparse count matrix with negative binomial distribution)
316
+ gene_programs = np.zeros((n_cell_types, n_genes))
317
+ for i in range(n_cell_types):
318
+ high_expr_genes = np.random.choice(
319
+ n_genes, size=int(n_genes * 0.2), replace=False
320
+ )
321
+ gene_programs[i, high_expr_genes] = np.random.gamma(
322
+ 5, 2, size=len(high_expr_genes)
323
+ )
324
+
325
+ lambda_values = np.zeros((n_cells, n_genes))
326
+ for i in range(n_cells):
327
+ lambda_values[i] = gene_programs[cell_types[i]] + np.random.gamma(
328
+ 0.5, 0.5, n_genes
329
+ )
330
+
331
+ seq_depth = np.random.lognormal(mean=8, sigma=0.5, size=n_cells)
332
+ lambda_values = lambda_values * seq_depth[:, np.newaxis]
333
+
334
+ rna_counts = np.random.poisson(lambda_values)
335
+ dropout_prob = 0.3
336
+ dropout_mask = np.random.binomial(1, 1 - dropout_prob, size=rna_counts.shape)
337
+ rna_counts = rna_counts * dropout_mask
338
+
339
+ # Generate protein data
340
+ protein_programs = np.zeros((n_cell_types, n_proteins))
341
+ for i in range(n_cell_types):
342
+ high_expr_proteins = np.random.choice(
343
+ n_proteins, size=int(n_proteins * 0.3), replace=False
344
+ )
345
+ protein_programs[i, high_expr_proteins] = np.random.gamma(
346
+ 3, 1, size=len(high_expr_proteins)
347
+ )
348
+
349
+ protein_levels = np.zeros((n_cells, n_proteins))
350
+ for i in range(n_cells):
351
+ protein_levels[i] = protein_programs[cell_types[i]] + np.random.gamma(
352
+ 0.2, 0.3, n_proteins
353
+ )
354
+
355
+ batch_effect = np.zeros(n_cells)
356
+ batch_map = {"batch1": 0.8, "batch2": 1.0, "batch3": 1.2}
357
+ for i in range(n_cells):
358
+ batch_effect[i] = batch_map[cell_metadata.iloc[i]["batch"]]
359
+
360
+ protein_levels = protein_levels * batch_effect[:, np.newaxis]
361
+ protein_levels = protein_levels + np.random.normal(
362
+ 0, 0.1, size=protein_levels.shape
363
+ )
364
+ protein_levels = np.maximum(0, protein_levels)
365
+
366
+ var_rna = pd.DataFrame(index=gene_names)
367
+ var_protein = pd.DataFrame(index=protein_names)
368
+
369
+ from scipy import sparse
370
+
371
+ # Convert to scipy sparse matrices to avoid copy issues
372
+ rna_counts_sparse = sparse.csr_matrix(rna_counts)
373
+ protein_levels_sparse = sparse.csr_matrix(protein_levels)
374
+
375
+ rna_adata = anndata.AnnData(X=rna_counts_sparse, obs=cell_metadata, var=var_rna)
376
+
377
+ protein_adata = anndata.AnnData(
378
+ X=protein_levels_sparse, obs=cell_metadata, var=var_protein
379
+ )
380
+
381
+ mdata = mudata.MuData({"rna": rna_adata, "protein": protein_adata})
382
+
383
+ data_package = DataPackage()
384
+ data_package.multi_sc = {"multi_sc": mdata}
385
+ # data_package.annotation = {"multi_sc": mdata.obs}
386
+
387
+ return data_package, rna_adata, mdata
388
+
389
+
390
+ # Pre-generated example data for direct import
391
+ config = DefaultConfig()
392
+ EXAMPLE_PROCESSED_DATA = generate_example_data(random_seed=config.global_seed)
393
+ EXAMPLE_MULTI_BULK, raw_rna, raw_protein, annotation = generate_raw_datapackage(
394
+ data_case=DataCase.MULTI_BULK
395
+ )
396
+ EXAMPLE_MULTI_SC, sample_adata, sample_mudata = generate_raw_datapackage(
397
+ data_case=DataCase.MULTI_SINGLE_CELL
398
+ )