pyaging 0.0.1__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 pyaging might be problematic. Click here for more details.

pyaging/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ # pyaging/__init__.py
2
+
3
+ from . import logger
4
+ from . import data
5
+ from . import utils
6
+ from . import models
7
+ from . import preprocess as pp
8
+ from . import predict as pred
9
+
10
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ # pyaging/data/__init__.py
2
+
3
+ from ._data import *
pyaging/data/_data.py ADDED
@@ -0,0 +1,75 @@
1
+ import os
2
+ import torch
3
+ import ntpath
4
+ import os
5
+ from urllib.request import urlretrieve
6
+ from functools import wraps
7
+ from ..utils import progress, download
8
+ from ..logger import LoggerManager, main_tqdm, silence_logger
9
+
10
+
11
+ def download_example_data(
12
+ data_type: str, dir: str = "pyaging_data", verbose: bool = True
13
+ ) -> None:
14
+ """
15
+ Downloads example datasets for various types of biological data used in aging studies.
16
+
17
+ This function facilitates the download of example datasets for different types of biological data,
18
+ including methylation, histone mark, RNA-seq, and ATAC-seq data. It is designed to provide quick
19
+ access to standard datasets for users to test and explore the functionalities of the pyaging package.
20
+
21
+ Parameters
22
+ ----------
23
+ data_type : str
24
+ The type of data to download. Valid options are 'methylation', 'histone_mark', 'rnaseq', and 'atac'.
25
+
26
+ dir : str
27
+ The directory to deposit the downloaded file. Defaults to "pyaging_data".
28
+
29
+ verbose: bool
30
+ Whether to log the output to console with the logger. Defaults to True.
31
+
32
+ Raises
33
+ ------
34
+ ValueError
35
+ If the specified data_type is not implemented, a ValueError is raised with a message suggesting
36
+ the user to request its implementation.
37
+
38
+ Notes
39
+ -----
40
+ The function maps the specified data_type to its corresponding URL and then calls the `download`
41
+ function to retrieve the dataset. The datasets are sourced from AWS S3
42
+ and are chosen to represent typical data formats and structures used in aging research.
43
+
44
+ The downloaded data can be used for various analyses, including testing the pyaging package's
45
+ functionalities, learning data processing techniques, or as a benchmark for custom analyses.
46
+
47
+ Examples
48
+ --------
49
+ >>> download_example_data("methylation")
50
+ # This will download the example methylation dataset to the local system.
51
+
52
+ """
53
+ logger = LoggerManager.gen_logger("download_example_data")
54
+ if not verbose:
55
+ silence_logger("download_example_data")
56
+ logger.first_info("Starting download_example_data function")
57
+
58
+ data_type_to_url = {
59
+ "GSE139307": "https://pyaging.s3.amazonaws.com/example_data/GSE139307.pkl",
60
+ "GSE223748": "https://pyaging.s3.amazonaws.com/example_data/GSE223748_subset.pkl",
61
+ "ENCFF386QWG": "https://pyaging.s3.amazonaws.com/example_data/ENCFF386QWG.bigWig",
62
+ "GSE65765": "https://pyaging.s3.amazonaws.com/example_data/GSE65765_CPM.pkl",
63
+ "atac_example": "https://pyaging.s3.amazonaws.com/example_data/atac_example.pkl",
64
+ }
65
+
66
+ if data_type not in list(data_type_to_url.keys()):
67
+ logger.error(
68
+ f"Example data {data_type} has not yet been implemented in pyaging.",
69
+ indent_level=2,
70
+ )
71
+
72
+ url = data_type_to_url[data_type]
73
+ download(url, dir, logger, indent_level=1)
74
+
75
+ logger.done()
@@ -0,0 +1,3 @@
1
+ # pyaging/logger/__init__.py
2
+
3
+ from ._logger import *
@@ -0,0 +1,477 @@
1
+ # The code in this file is mostly taken from the dynamo-release package
2
+
3
+ import functools
4
+ import logging
5
+ import sys
6
+ import time
7
+ from contextlib import contextmanager
8
+
9
+
10
+ def silence_logger(name):
11
+ """Given a logger name, silence it completely.
12
+
13
+ :param name: name of the logger
14
+ :type name: str
15
+ """
16
+ package_logger = logging.getLogger(name)
17
+ package_logger.setLevel(logging.CRITICAL + 100)
18
+ package_logger.propagate = False
19
+
20
+
21
+ def set_logger_level(name, level):
22
+ """Given a logger name, silence it completely.
23
+
24
+ :param name: name of the logger
25
+ :type name: str
26
+ """
27
+ package_logger = logging.getLogger(name)
28
+ package_logger.setLevel(level)
29
+
30
+
31
+ def format_logging_message(msg, logging_level, indent_level=1, indent_space_num=6):
32
+ indent_str = "-" * indent_space_num
33
+ prefix = indent_str * indent_level
34
+ prefix = "|" + prefix[1:]
35
+ if logging_level == logging.INFO:
36
+ prefix += ">"
37
+ elif logging_level == logging.WARNING:
38
+ prefix += "?"
39
+ elif logging_level == logging.CRITICAL:
40
+ prefix += "!!"
41
+ elif logging_level == logging.DEBUG:
42
+ prefix += ">>>"
43
+ new_msg = prefix + " " + str(msg)
44
+ return new_msg
45
+
46
+
47
+ class Logger:
48
+ """Dynamo-specific logger that sets up logging for the package."""
49
+
50
+ FORMAT = "%(message)s"
51
+
52
+ def __init__(self, namespace="main", level=None):
53
+ self.namespace = namespace
54
+ self.logger = logging.getLogger(namespace)
55
+ self.previous_timestamp = time.time() # in seconds
56
+ self.time_passed = 0
57
+ self.warning_logged = False
58
+ self.report_hook_percent_state = None
59
+ self.indent_level = 1
60
+ # TODO add file handler in future
61
+ # e.g. logging.StreamHandler(None) if log_file_path is None else logging.FileHandler(name)
62
+
63
+ # ensure only one stream handler exists in one logger instance
64
+ if len(self.logger.handlers) == 0:
65
+ self.logger_stream_handler = logging.StreamHandler(sys.stdout)
66
+ self.logger_stream_handler.setFormatter(logging.Formatter(self.FORMAT))
67
+ self.logger.addHandler(self.logger_stream_handler)
68
+ else:
69
+ self.logger_stream_handler = self.logger.handlers[0]
70
+
71
+ self.logger.propagate = False
72
+ self.log_time()
73
+
74
+ # Other global initialization
75
+ silence_logger("anndata")
76
+ silence_logger("h5py")
77
+ silence_logger("numba")
78
+ silence_logger("pysam")
79
+ silence_logger("pystan")
80
+
81
+ if not (level is None):
82
+ self.logger.setLevel(level)
83
+ else:
84
+ self.logger.setLevel(logging.INFO)
85
+
86
+ def namespaced(self, namespace):
87
+ """Function decorator to set the logging namespace for the duration of
88
+ the function.
89
+
90
+ :param namespace: the namespace
91
+ :type namespace: str
92
+ """
93
+
94
+ def wrapper(func):
95
+ @functools.wraps(func)
96
+ def inner(*args, **kwargs):
97
+ previous = self.namespace
98
+ try:
99
+ self.namespace = namespace
100
+ return func(*args, **kwargs)
101
+ finally:
102
+ self.namespace = previous
103
+
104
+ return inner
105
+
106
+ return wrapper
107
+
108
+ @contextmanager
109
+ def namespaced_context(self, namespace):
110
+ """Context manager to set the logging namespace.
111
+
112
+ :param namespace: the namespace
113
+ :type namespace: str
114
+ """
115
+ previous = self.namespace
116
+ self.namespace = namespace
117
+ yield
118
+ self.namespace = previous
119
+
120
+ def namespace_message(self, message):
121
+ """Add namespace information at the beginning of the logging message.
122
+
123
+ :param message: the logging message
124
+ :type message: str
125
+
126
+ :return: namespaced message
127
+ :rtype: string
128
+ """
129
+ return f"[{self.namespace}] {message}"
130
+
131
+ def setLevel(self, *args, **kwargs):
132
+ return self.logger.setLevel(*args, **kwargs)
133
+
134
+ def debug(self, message, indent_level=1, *args, **kwargs):
135
+ message = format_logging_message(
136
+ message, logging.DEBUG, indent_level=indent_level
137
+ )
138
+ return self.logger.debug(message, *args, **kwargs)
139
+
140
+ def info(self, message, indent_level=1, *args, **kwargs):
141
+ message = format_logging_message(
142
+ message, logging.INFO, indent_level=indent_level
143
+ )
144
+ return self.logger.info(message, *args, **kwargs)
145
+
146
+ def first_info(self, message, indent_level=1, *args, **kwargs):
147
+ self.start_time = time.time() # Store the start time
148
+ message = format_logging_message(
149
+ "🏗️ " + message, logging.INFO, indent_level=indent_level
150
+ )
151
+ return self.logger.info(message, *args, **kwargs)
152
+
153
+ def done(self, indent_level=1, *args, **kwargs):
154
+ if hasattr(self, "start_time"):
155
+ elapsed_time = time.time() - self.start_time
156
+ message = format_logging_message(
157
+ f"🎉 Done! [%.4fs]" % (elapsed_time),
158
+ logging.INFO,
159
+ indent_level=indent_level,
160
+ )
161
+ else:
162
+ message = format_logging_message(
163
+ "🎉 Done!", logging.INFO, indent_level=indent_level
164
+ )
165
+
166
+ return self.logger.info(message, *args, **kwargs)
167
+
168
+ def warning(self, message, indent_level=1, *args, **kwargs):
169
+ self.warning_logged = True
170
+ message = format_logging_message(
171
+ message, logging.WARNING, indent_level=indent_level
172
+ )
173
+ return self.logger.warning(message, *args, **kwargs)
174
+
175
+ def exception(self, message, indent_level=1, *args, **kwargs):
176
+ message = format_logging_message(
177
+ message, logging.ERROR, indent_level=indent_level
178
+ )
179
+ return self.logger.exception(message, *args, **kwargs)
180
+
181
+ def critical(self, message, indent_level=1, *args, **kwargs):
182
+ message = format_logging_message(
183
+ message, logging.CRITICAL, indent_level=indent_level
184
+ )
185
+ return self.logger.critical(message, *args, **kwargs)
186
+
187
+ def error(self, message, indent_level=1, *args, **kwargs):
188
+ message = format_logging_message(
189
+ "🛑 " + message, logging.ERROR, indent_level=indent_level
190
+ )
191
+ return self.logger.error(message, *args, **kwargs)
192
+
193
+ def info_insert_adata(
194
+ self,
195
+ key,
196
+ adata_attr="obsm",
197
+ log_level=logging.NOTSET,
198
+ indent_level=1,
199
+ *args,
200
+ **kwargs,
201
+ ):
202
+ message = "<insert> %s to %s in AnnData Object." % (key, adata_attr)
203
+ if log_level == logging.NOTSET or log_level == logging.DEBUG:
204
+ self.debug(message, indent_level=indent_level, *args, **kwargs)
205
+ elif log_level == logging.INFO:
206
+ self.info(message, indent_level=indent_level, *args, **kwargs)
207
+ elif log_level == logging.WARN:
208
+ self.warning(message, indent_level=indent_level, *args, **kwargs)
209
+ elif log_level == logging.ERROR:
210
+ self.error(message, indent_level=indent_level, *args, **kwargs)
211
+ elif log_level == logging.CRITICAL:
212
+ self.critical(message, indent_level=indent_level, *args, **kwargs)
213
+ else:
214
+ raise NotImplementedError
215
+
216
+ def info_insert_adata_var(self, key, log_level, indent_level, *args, **kwargs):
217
+ return self.info_insert_adata(
218
+ self,
219
+ key,
220
+ adata_attr="var",
221
+ log_level=log_level,
222
+ indent_level=indent_level,
223
+ *args,
224
+ **kwargs,
225
+ )
226
+
227
+ def info_insert_adata_obsm(self, key, log_level, indent_level, *args, **kwargs):
228
+ return self.info_insert_adata(
229
+ self,
230
+ key,
231
+ adata_attr="obsm",
232
+ log_level=log_level,
233
+ indent_level=indent_level,
234
+ *args,
235
+ **kwargs,
236
+ )
237
+
238
+ def info_insert_adata_uns(self, key, log_level, indent_level, *args, **kwargs):
239
+ return self.info_insert_adata(
240
+ self,
241
+ key,
242
+ adata_attr="uns",
243
+ log_level=log_level,
244
+ indent_level=indent_level,
245
+ *args,
246
+ **kwargs,
247
+ )
248
+
249
+ def log_time(self):
250
+ now = time.time()
251
+ self.time_passed = now - self.previous_timestamp
252
+ self.previous_timestamp = now
253
+ return self.time_passed
254
+
255
+ def report_progress(
256
+ self, percent=None, count=None, total=None, progress_name="", indent_level=1
257
+ ):
258
+ if percent is None:
259
+ assert (not count is None) and (not total is None)
260
+ percent = count / total * 100
261
+ saved_terminator = self.logger_stream_handler.terminator
262
+ if percent != 100:
263
+ self.logger_stream_handler.terminator = ""
264
+ if progress_name != "":
265
+ progress_name = "[" + str(progress_name) + "] "
266
+ message = "\r" + format_logging_message(
267
+ "%sin progress: %.4f%%" % (progress_name, percent),
268
+ logging_level=logging.INFO,
269
+ indent_level=indent_level,
270
+ )
271
+ self.logger.info(message)
272
+ self.logger_stream_handler.flush()
273
+ self.logger_stream_handler.terminator = saved_terminator
274
+
275
+ def start_progress(self, message, indent_level=1, *args, **kwargs):
276
+ self.log_time()
277
+ message = format_logging_message(
278
+ "⚙️ " + message, logging.INFO, indent_level=indent_level
279
+ )
280
+ self.logger.info(message, *args, **kwargs)
281
+ self.logger_stream_handler.flush()
282
+
283
+ def finish_progress(self, progress_name="", time_unit="s", indent_level=1):
284
+ # self.log_time()
285
+ # self.report_progress(percent=100, progress_name=progress_name)#
286
+
287
+ saved_terminator = self.logger_stream_handler.terminator
288
+ self.logger_stream_handler.terminator = ""
289
+ self.logger_stream_handler.flush()
290
+ self.logger_stream_handler.terminator = saved_terminator #
291
+
292
+ if self.warning_logged:
293
+ token = "⚠️"
294
+ else:
295
+ token = "✅" #
296
+
297
+ if time_unit == "s":
298
+ self.info(
299
+ token + " %s [%.4fs]" % (progress_name, self.time_passed),
300
+ indent_level=indent_level,
301
+ )
302
+ elif time_unit == "ms":
303
+ self.info(
304
+ token + " %s [%.4fms]" % (progress_name, self.time_passed * 1e3),
305
+ indent_level=indent_level,
306
+ )
307
+ else:
308
+ raise NotImplementedError
309
+ # self.logger.info("|")
310
+ self.logger_stream_handler.flush()
311
+ self.warning_logged = False #
312
+
313
+ def request_report_hook(self, bn: int, rs: int, ts: int) -> None:
314
+ """A callback required by the request lib:
315
+ The reporthook argument should be a callable that accepts a block number, a read size, and the
316
+ total file size of the URL target. The data argument should be valid URL encoded data.
317
+
318
+ Parameters
319
+ ----------
320
+ bs :
321
+ block number
322
+ rs :
323
+ read size
324
+ ts :
325
+ total size
326
+ """
327
+ if self.report_hook_percent_state is None:
328
+ self.report_hook_percent_state = 0
329
+
330
+ if ts == -1:
331
+ return
332
+
333
+ cur_percent = rs * bn / ts
334
+
335
+ if cur_percent - self.report_hook_percent_state > 0.01:
336
+ self.report_progress(
337
+ count=rs * bn, total=ts, indent_level=self.indent_level
338
+ )
339
+ self.report_hook_percent_state = cur_percent
340
+ if rs * bn >= ts:
341
+ self.report_hook_percent_state = None
342
+ self.report_progress(
343
+ count=100, total=100, indent_level=self.indent_level
344
+ ) # 100%
345
+ # self.finish_progress(progress_name="download")
346
+
347
+
348
+ class LoggerManager:
349
+ DEBUG = logging.DEBUG
350
+ INFO = logging.INFO
351
+ CRITICAL = logging.CRITICAL
352
+ EXCEPTION = logging.ERROR
353
+
354
+ main_logger = Logger("dynamo")
355
+ temp_timer_logger = Logger("dynamo-temp-timer-logger")
356
+
357
+ @staticmethod
358
+ def get_main_logger():
359
+ return LoggerManager.main_logger
360
+
361
+ @staticmethod
362
+ def gen_logger(namespace: str):
363
+ return Logger(namespace)
364
+
365
+ @staticmethod
366
+ def get_temp_timer_logger():
367
+ return LoggerManager.temp_timer_logger
368
+
369
+ @staticmethod
370
+ def progress_logger(generator, logger=None, progress_name="", indent_level=1):
371
+ if logger is None:
372
+ logger = LoggerManager.get_temp_timer_logger()
373
+ iterator = iter(generator)
374
+ logger.log_time()
375
+ i = 0
376
+ prev_progress_percent = 0
377
+ while i < len(generator):
378
+ i += 1
379
+ new_progress_percent = i / len(generator) * 100
380
+ # report every `interval` percent
381
+ if (
382
+ new_progress_percent - prev_progress_percent > 1
383
+ or new_progress_percent >= 100
384
+ ):
385
+ logger.report_progress(
386
+ count=i,
387
+ total=len(generator),
388
+ progress_name=progress_name,
389
+ indent_level=indent_level,
390
+ )
391
+ prev_progress_percent = new_progress_percent
392
+ yield next(iterator)
393
+ # logger.finish_progress(progress_name=progress_name, indent_level=indent_level)
394
+
395
+
396
+ def main_info(message, indent_level=1):
397
+ LoggerManager.main_logger.info(message, indent_level)
398
+
399
+
400
+ def main_debug(message, indent_level=1):
401
+ LoggerManager.main_logger.debug(message, indent_level)
402
+
403
+
404
+ def main_warning(message, indent_level=1):
405
+ LoggerManager.main_logger.warning(message, indent_level)
406
+
407
+
408
+ def main_exception(message, indent_level=1):
409
+ LoggerManager.main_logger.exception(message, indent_level)
410
+
411
+
412
+ def main_critical(message, indent_level=1):
413
+ LoggerManager.main_logger.critical(message, indent_level)
414
+
415
+
416
+ def main_tqdm(generator, desc="", indent_level=1, logger=LoggerManager().main_logger):
417
+ """a TQDM style wrapper for logging something like a loop.
418
+ e.g.
419
+ for item in main_tqdm(alist, desc=""):
420
+ do something
421
+
422
+ Parameters
423
+ ----------
424
+ generator : [type]
425
+ same as what you put in tqdm
426
+ desc : str, optional
427
+ description of your progress
428
+ """
429
+ return LoggerManager.progress_logger(
430
+ generator, logger=logger, progress_name=desc, indent_level=indent_level
431
+ )
432
+
433
+
434
+ def main_log_time():
435
+ LoggerManager.main_logger.log_time()
436
+
437
+
438
+ def main_silence():
439
+ LoggerManager.main_logger.setLevel(logging.CRITICAL + 100)
440
+
441
+
442
+ def main_finish_progress(progress_name=""):
443
+ LoggerManager.main_logger.finish_progress(progress_name=progress_name)
444
+
445
+
446
+ def main_info_insert_adata(key, adata_attr="obsm", indent_level=1, *args, **kwargs):
447
+ LoggerManager.main_logger.info_insert_adata(
448
+ key, adata_attr=adata_attr, indent_level=indent_level, *args, **kwargs
449
+ )
450
+
451
+
452
+ def main_info_insert_adata_var(key, indent_level=1, *args, **kwargs):
453
+ main_info_insert_adata(key, "var", indent_level, *args, **kwargs)
454
+
455
+
456
+ def main_info_insert_adata_uns(key, indent_level=1, *args, **kwargs):
457
+ main_info_insert_adata(key, "uns", indent_level, *args, **kwargs)
458
+
459
+
460
+ def main_info_insert_adata_obsm(key, indent_level=1, *args, **kwargs):
461
+ main_info_insert_adata(key, "obsm", indent_level, *args, **kwargs)
462
+
463
+
464
+ def main_info_insert_adata_obs(key, indent_level=1, *args, **kwargs):
465
+ main_info_insert_adata(key, "obs", indent_level, *args, **kwargs)
466
+
467
+
468
+ def main_info_insert_adata_layer(key, indent_level=1, *args, **kwargs):
469
+ main_info_insert_adata(key, "layers", indent_level, *args, **kwargs)
470
+
471
+
472
+ def main_info_verbose_timeit(msg):
473
+ LoggerManager.main_logger.info(msg)
474
+
475
+
476
+ def main_set_level(level):
477
+ set_logger_level("dynamo", level)
@@ -0,0 +1,3 @@
1
+ # pyaging/models/__init__.py
2
+
3
+ from ._models import *