bs-python-utils 1.0__tar.gz → 1.1__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.
Files changed (91) hide show
  1. {bs_python_utils-1.0 → bs_python_utils-1.1}/PKG-INFO +10 -2
  2. {bs_python_utils-1.0 → bs_python_utils-1.1}/README.md +7 -0
  3. bs_python_utils-1.1/bs_python_utils/core/Timer.py +81 -0
  4. bs_python_utils-1.1/bs_python_utils/core/__init__.py +1 -0
  5. bs_python_utils-1.1/bs_python_utils/core/bs_logging.py +168 -0
  6. bs_python_utils-1.1/bs_python_utils/core/bs_mathstr.py +121 -0
  7. bs_python_utils-1.1/bs_python_utils/core/bs_mem.py +157 -0
  8. bs_python_utils-1.1/bs_python_utils/core/bsutils.py +410 -0
  9. bs_python_utils-1.1/bs_python_utils/data_anal/__init__.py +1 -0
  10. bs_python_utils-1.1/bs_python_utils/data_anal/pandas_utils.py +245 -0
  11. bs_python_utils-1.1/bs_python_utils/data_anal/sklearn_utils.py +123 -0
  12. bs_python_utils-1.1/bs_python_utils/numerical/__init__.py +1 -0
  13. bs_python_utils-1.1/bs_python_utils/numerical/bs_sparse_gaussian.py +74 -0
  14. bs_python_utils-1.1/bs_python_utils/numerical/bsnputils.py +1221 -0
  15. bs_python_utils-1.1/bs_python_utils/numerical/bssputils.py +82 -0
  16. bs_python_utils-1.1/bs_python_utils/numerical/chebyshev.py +550 -0
  17. bs_python_utils-1.1/bs_python_utils/opt/__init__.py +1 -0
  18. bs_python_utils-1.1/bs_python_utils/opt/bs_opt.py +576 -0
  19. bs_python_utils-1.1/bs_python_utils/stats/__init__.py +1 -0
  20. bs_python_utils-1.1/bs_python_utils/stats/bivariate_quantiles.py +342 -0
  21. bs_python_utils-1.1/bs_python_utils/stats/bsstats.py +502 -0
  22. bs_python_utils-1.1/bs_python_utils/stats/distance_covariances.py +263 -0
  23. bs_python_utils-1.1/bs_python_utils/viz/__init__.py +1 -0
  24. bs_python_utils-1.1/bs_python_utils/viz/bs_altair.py +1111 -0
  25. bs_python_utils-1.1/bs_python_utils/viz/bs_plots.py +62 -0
  26. bs_python_utils-1.1/bs_python_utils/viz/bs_seaborn.py +210 -0
  27. bs_python_utils-1.1/bs_python_utils/viz/bsmplutils.py +95 -0
  28. bs_python_utils-1.1/bs_python_utils/viz/streamlit_utils.py +100 -0
  29. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils.egg-info/PKG-INFO +10 -2
  30. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils.egg-info/SOURCES.txt +26 -0
  31. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils.egg-info/requires.txt +1 -0
  32. {bs_python_utils-1.0 → bs_python_utils-1.1}/pyproject.toml +10 -4
  33. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bivariate_quantiles.py +20 -0
  34. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_random_draws.py +2 -2
  35. {bs_python_utils-1.0 → bs_python_utils-1.1}/LICENSE +0 -0
  36. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec13.txt +0 -0
  37. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec17.txt +0 -0
  38. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid1prec9.txt +0 -0
  39. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec13.txt +0 -0
  40. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec17.txt +0 -0
  41. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid2prec9.txt +0 -0
  42. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec13.txt +0 -0
  43. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec17.txt +0 -0
  44. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid3prec9.txt +0 -0
  45. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec13.txt +0 -0
  46. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec17.txt +0 -0
  47. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid4prec9.txt +0 -0
  48. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec13.txt +0 -0
  49. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec17.txt +0 -0
  50. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/GaussHermiteSparseGrids/GHsparseGrid5prec9.txt +0 -0
  51. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/Timer.py +0 -0
  52. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/__init__.py +0 -0
  53. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bivariate_quantiles.py +0 -0
  54. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_altair.py +0 -0
  55. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_logging.py +0 -0
  56. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_mathstr.py +0 -0
  57. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_mem.py +0 -0
  58. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_opt.py +0 -0
  59. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_plots.py +0 -0
  60. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_seaborn.py +0 -0
  61. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bs_sparse_gaussian.py +0 -0
  62. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bsmplutils.py +0 -0
  63. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bsnputils.py +0 -0
  64. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bssputils.py +0 -0
  65. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bsstats.py +0 -0
  66. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/bsutils.py +0 -0
  67. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/chebyshev.py +0 -0
  68. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/distance_covariances.py +0 -0
  69. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_altair.py +0 -0
  70. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_distance_covariances.py +0 -0
  71. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_mem.py +0 -0
  72. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_mpl.py +0 -0
  73. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_opt.py +0 -0
  74. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_seaborn.py +0 -0
  75. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_sklearn.py +0 -0
  76. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/examples/examples_sparse_gaussian.py +0 -0
  77. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/pandas_utils.py +0 -0
  78. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/sklearn_utils.py +0 -0
  79. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils/streamlit_utils.py +0 -0
  80. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils.egg-info/dependency_links.txt +0 -0
  81. {bs_python_utils-1.0 → bs_python_utils-1.1}/bs_python_utils.egg-info/top_level.txt +0 -0
  82. {bs_python_utils-1.0 → bs_python_utils-1.1}/setup.cfg +0 -0
  83. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bs_opt.py +0 -0
  84. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bs_sparse_gaussian.py +0 -0
  85. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bsnputils.py +0 -0
  86. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bssputils.py +0 -0
  87. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bsstats.py +0 -0
  88. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_bsutils.py +0 -0
  89. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_chebyshev.py +0 -0
  90. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_nptri.py +0 -0
  91. {bs_python_utils-1.0 → bs_python_utils-1.1}/tests/test_sklearn_utils.py +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bs-python-utils
3
- Version: 1.0
3
+ Version: 1.1
4
4
  Summary: Utilities programs for my Python code
5
- Requires-Python: >=3.12
5
+ Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: altair>=5.5.0
@@ -26,14 +26,19 @@ Requires-Dist: streamlit>=1.49.1
26
26
  Requires-Dist: vega-datasets>=0.9.0
27
27
  Requires-Dist: colorama>=0.4.6
28
28
  Requires-Dist: pyarrow>=23.0.0
29
+ Requires-Dist: scipy-stubs~=1.16.2
29
30
  Dynamic: license-file
30
31
 
31
32
  ## bs-python-utils
32
33
 
33
34
  [![PyPI version](https://img.shields.io/pypi/v/bs-python-utils)](https://pypi.org/project/bs-python-utils/)
35
+ <!--
34
36
  [![Release](https://img.shields.io/github/v/release/bsalanie/bs-python-utils)](https://github.com/bsalanie/bs-python-utils/releases)
37
+ -->
35
38
  [![Build status](https://img.shields.io/github/actions/workflow/status/bsalanie/bs-python-utils/main.yml?branch=main)](https://github.com/bsalanie/bs-python-utils/actions/workflows/main.yml?query=branch%3Amain)
39
+ <!--
36
40
  [![Tests](https://img.shields.io/github/actions/workflow/status/bsalanie/bs-python-utils/tests.yml?branch=main&label=tests)](https://github.com/bsalanie/bs-python-utils/actions/workflows/tests.yml?query=branch%3Amain)
41
+ -->
37
42
  [![Commit activity](https://img.shields.io/github/commit-activity/m/bsalanie/bs-python-utils)](https://github.com/bsalanie/bs-python-utils/commits/main)
38
43
  ![GitHub last commit](https://img.shields.io/github/last-commit/bsalanie/bs-python-utils)
39
44
  [![License](https://img.shields.io/github/license/bsalanie/bs-python-utils)](https://img.shields.io/github/license/bsalanie/bs-python-utils)
@@ -45,6 +50,9 @@ Dynamic: license-file
45
50
 
46
51
  ### Release notes
47
52
 
53
+ #### 1.0.1 (May 9, 2026)
54
+ Fixed badges.
55
+
48
56
  #### 1.0 (May 9, 2026)
49
57
  Created subpackages (no change in the API).
50
58
 
@@ -1,9 +1,13 @@
1
1
  ## bs-python-utils
2
2
 
3
3
  [![PyPI version](https://img.shields.io/pypi/v/bs-python-utils)](https://pypi.org/project/bs-python-utils/)
4
+ <!--
4
5
  [![Release](https://img.shields.io/github/v/release/bsalanie/bs-python-utils)](https://github.com/bsalanie/bs-python-utils/releases)
6
+ -->
5
7
  [![Build status](https://img.shields.io/github/actions/workflow/status/bsalanie/bs-python-utils/main.yml?branch=main)](https://github.com/bsalanie/bs-python-utils/actions/workflows/main.yml?query=branch%3Amain)
8
+ <!--
6
9
  [![Tests](https://img.shields.io/github/actions/workflow/status/bsalanie/bs-python-utils/tests.yml?branch=main&label=tests)](https://github.com/bsalanie/bs-python-utils/actions/workflows/tests.yml?query=branch%3Amain)
10
+ -->
7
11
  [![Commit activity](https://img.shields.io/github/commit-activity/m/bsalanie/bs-python-utils)](https://github.com/bsalanie/bs-python-utils/commits/main)
8
12
  ![GitHub last commit](https://img.shields.io/github/last-commit/bsalanie/bs-python-utils)
9
13
  [![License](https://img.shields.io/github/license/bsalanie/bs-python-utils)](https://img.shields.io/github/license/bsalanie/bs-python-utils)
@@ -15,6 +19,9 @@
15
19
 
16
20
  ### Release notes
17
21
 
22
+ #### 1.0.1 (May 9, 2026)
23
+ Fixed badges.
24
+
18
25
  #### 1.0 (May 9, 2026)
19
26
  Created subpackages (no change in the API).
20
27
 
@@ -0,0 +1,81 @@
1
+ """
2
+ Utilities to time code:
3
+
4
+ * a `Timer` class that can be used as a context manager
5
+ * a `timeit` decorator for functions.
6
+ """
7
+
8
+ import time
9
+ from functools import wraps
10
+ from typing import Any, Callable, Iterable
11
+
12
+
13
+ def timeit(func: Callable) -> Callable:
14
+ """
15
+ Decorator to time a function
16
+ """
17
+
18
+ @wraps(func)
19
+ def wrapper(*args: Iterable, **kwargs: dict) -> Any:
20
+ start = time.perf_counter()
21
+ result = func(*args, **kwargs)
22
+ end = time.perf_counter()
23
+ print(f"{func.__name__} executed in {end - start:.3f} seconds")
24
+ return result
25
+
26
+ return wrapper
27
+
28
+
29
+ class Timer:
30
+ """
31
+ A timer that can be started, stopped, and reset as needed by the user.
32
+ It keeps track of the total elapsed time in the `elapsed` attribute::
33
+
34
+ Examples:
35
+ >>> with Timer() as t:
36
+ >>> ....
37
+ >>> print(f"... took {t.elapsed} seconds")
38
+
39
+ use `Timer(time.process_time)` to get only CPU time.
40
+
41
+ can also do:
42
+
43
+ Examples:
44
+ >>> t = Timer()
45
+ >>> t.start()
46
+ >>> t.stop()
47
+ >>> t.start() # will add to the same counter
48
+ >>> t.stop()
49
+ >>> print(f"{t.elapsed} seconds total")
50
+ """
51
+
52
+ def __init__(self, func: Callable = time.perf_counter) -> None:
53
+ self.elapsed = 0.0
54
+ self._func = func
55
+ self._start = None
56
+
57
+ def start(self) -> None:
58
+ if self._start is not None:
59
+ raise RuntimeError("Already started")
60
+ self._start = self._func()
61
+
62
+ def stop(self) -> None:
63
+ if self._start is None:
64
+ raise RuntimeError("Not started")
65
+ end = self._func()
66
+ self.elapsed += end - self._start
67
+ self._start = None
68
+
69
+ def reset(self) -> None:
70
+ self.elapsed = 0.0
71
+
72
+ @property
73
+ def running(self) -> bool:
74
+ return self._start is not None
75
+
76
+ def __enter__(self) -> Any:
77
+ self.start()
78
+ return self
79
+
80
+ def __exit__(self, *args: Iterable) -> None:
81
+ self.stop()
@@ -0,0 +1 @@
1
+ # core: general utilities — bsutils, Timer, bs_logging, bs_mathstr, bs_mem
@@ -0,0 +1,168 @@
1
+ """Utilities for logging:
2
+
3
+ * `get_logger` creates and returns a logger with colored output
4
+ * `init_logger` initializes and returns a customized logger
5
+ * `log_execution` is a decorator to log entry into and ext from a function.
6
+ """
7
+
8
+ import functools
9
+ import logging
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Callable
13
+
14
+ from colorama import Fore, Style, init
15
+
16
+ # Initialize colorama
17
+ init(autoreset=True)
18
+
19
+ COLOR_MAP = {
20
+ "DEBUG": Fore.CYAN,
21
+ "INFO": Fore.GREEN,
22
+ "WARNING": Fore.YELLOW,
23
+ "ERROR": Fore.RED,
24
+ "CRITICAL": Fore.MAGENTA,
25
+ }
26
+
27
+
28
+ class ColorFormatter(logging.Formatter):
29
+ def format(self, record):
30
+ # Mirror the standard Formatter logic so we still get message, asctime, and exception text
31
+ record.message = record.getMessage()
32
+ if self.usesTime():
33
+ record.asctime = self.formatTime(record, self.datefmt)
34
+
35
+ color = COLOR_MAP.get(record.levelname, "")
36
+ level = f"{color}{record.levelname}{Style.RESET_ALL}"
37
+ output = f"{record.asctime} [{record.name}] {level}: {record.message}"
38
+
39
+ if record.exc_info and not record.exc_text:
40
+ record.exc_text = self.formatException(record.exc_info)
41
+ if record.exc_text:
42
+ output = f"{output}\n{record.exc_text}"
43
+
44
+ return output
45
+
46
+
47
+ def get_logger(
48
+ name: str,
49
+ level: int = logging.INFO,
50
+ log_to_file: bool = False,
51
+ ) -> logging.Logger:
52
+ """Create and return a logger with colored output.
53
+
54
+ Args:
55
+ name: Name of the logger.
56
+ level: Minimum logging level.
57
+ log_to_file: If True, also log to a file named '{name}.log'.
58
+
59
+ Returns:
60
+ Configured logger instance.
61
+
62
+ Examples:
63
+ >>> logger = get_logger("my_logger", level=logging.DEBUG, log_to_file=True)
64
+ >>> logger.debug("This is a debug message.")
65
+ """
66
+ logger = logging.getLogger(name)
67
+ if logger.handlers:
68
+ return logger # Already configured
69
+ logger.setLevel(level)
70
+ formatter = ColorFormatter("%(asctime)s", datefmt="%H:%M:%S")
71
+ stream_handler = logging.StreamHandler(sys.stdout)
72
+ stream_handler.setFormatter(formatter)
73
+ logger.addHandler(stream_handler)
74
+ if log_to_file:
75
+ file_handler = logging.FileHandler(f"{name}.log")
76
+ file_handler.setFormatter(formatter)
77
+ logger.addHandler(file_handler)
78
+ return logger
79
+
80
+
81
+ def init_logger(
82
+ logger_name: str,
83
+ log_level_for_console: str = "info",
84
+ log_level_for_file: str = "debug",
85
+ save_dir: str | None = None,
86
+ ) -> logging.Logger:
87
+ """
88
+ Initialize a logger
89
+
90
+ Args:
91
+ logger_name: name for the logger
92
+ log_level_for_console: minimum level of messages logged to the
93
+ console logging
94
+ log_level_for_file:
95
+ save_dir:
96
+
97
+ Returns:
98
+ the logger
99
+
100
+ Examples:
101
+ >>> logger_dir = "logs"
102
+ >>> logger_name = "check_log"
103
+ >>> logger = init_logger(logger_name, save_dir=logger_dir)
104
+
105
+ This will create two logs:
106
+
107
+ * one printed to console where we run the code (the
108
+ `StreamHandler`),
109
+ * and one that will be saved to file `save_dir/logger_name.txt`
110
+ (the `FileHandler`).
111
+
112
+ `'logger.propagate = False'` makes sure that the logs sent to file
113
+ will not be printed to console.
114
+
115
+ We use the `Formatter` class to define the format of the logs.
116
+ Here:
117
+
118
+ * The time of the log in a human-readable format, `asctime`
119
+ * `levelname` is the level of the log, one out of `INFO, DEBUG, WARNING, ERROR, CRITICAL`.
120
+ * The name of the file, `filename`, from which the log was generated,
121
+ and the line number, `lineno`.
122
+ * Lastly, the message itself — `message`.
123
+
124
+ The default has only `INFO` logs and above (i.e., also
125
+ `WARNING, ERROR` and `CRITICAL`)
126
+ displayed in the console; the file will also include `DEBUG` logs.
127
+ """
128
+ logger = logging.getLogger()
129
+ logger.setLevel(level=logging.DEBUG)
130
+ logger.propagate = False
131
+
132
+ formatter = logging.Formatter(
133
+ "%(asctime)s [%(levelname)s] %(filename)s %(lineno)d - %(message)s",
134
+ "%Y-%m-%d %H:%M:%S",
135
+ )
136
+
137
+ ch = logging.StreamHandler()
138
+ ch.setLevel(log_level_for_console.upper())
139
+ ch.setFormatter(formatter)
140
+ logger.addHandler(ch)
141
+
142
+ if save_dir is not None:
143
+ Path(save_dir).mkdir(exist_ok=True, parents=True)
144
+ fh = logging.FileHandler(save_dir + f"/{logger_name}.txt")
145
+ fh.setLevel(log_level_for_file.upper())
146
+ fh.setFormatter(formatter)
147
+ logger.addHandler(fh)
148
+
149
+ return logger
150
+
151
+
152
+ def log_execution(func: Callable) -> Callable:
153
+ """Decorator to log the execution of a function.
154
+ Only records entry to and exit from the function, to the console.
155
+ """
156
+ loglevel = logging.info
157
+
158
+ @functools.wraps(func)
159
+ def wrapper(*args, **kwargs):
160
+ loglevel(f"Executing {func.__name__}")
161
+ result = func(*args, **kwargs)
162
+ loglevel(f"Finished executing {func.__name__}")
163
+ return result
164
+
165
+ return wrapper
166
+
167
+ return wrapper
168
+ return wrapper
@@ -0,0 +1,121 @@
1
+ """Some useful strings for math formulae.
2
+
3
+ Note:
4
+ if the math looks strange in the documentation, just reload the page.
5
+
6
+ Attributes:
7
+ * str_beta0: the LaTeX string $\\beta_0$
8
+ * str_beta1: the LaTeX string $\\beta_1$
9
+ * str_pi: the LaTeX string $\\pi$
10
+ * str_sigma: the LaTeX string $\\sigma$
11
+ * str_sigma2: the LaTeX string $\\sigma^2$
12
+ * uni_beta0: the Unicode string $\\beta_0$
13
+ * uni_beta1: the Unicode string $\\beta_1$
14
+ * uni_pi: the Unicode string $\\pi$
15
+ * uni_sigma: the Unicode string $\\sigma$
16
+ * uni_sigma2: the Unicode string $\\sigma^2$
17
+ * uni_s2: the Unicode string $s^2$
18
+ * uni_R2: the Unicode string $R^2$
19
+ * sub_sub_scripts: a dictionary of unicodes for subscripts and superscripts;
20
+ e.g $a^b$ would be `"a" + sub_sup_scripts['b'][0]`
21
+ """
22
+
23
+ # LaTeX strings
24
+ str_beta0 = r"$\beta_0$"
25
+ str_beta1 = r"$\beta_1$"
26
+ str_pi = r"$\pi$"
27
+ str_sigma = r"$\sigma$"
28
+ str_sigma2 = r"$\sigma^2$"
29
+
30
+ # * unicode
31
+ uni_beta0 = "\N{GREEK SMALL LETTER BETA}\N{SUBSCRIPT ZERO}"
32
+ uni_beta1 = "\N{GREEK SMALL LETTER BETA}\N{SUBSCRIPT ONE}"
33
+ uni_pi = "\N{GREEK SMALL LETTER PI}"
34
+ uni_sigma = "\N{GREEK SMALL LETTER SIGMA}"
35
+ uni_sigma2 = "\N{GREEK SMALL LETTER SIGMA}\N{SUPERSCRIPT TWO}"
36
+ uni_s2 = "s\N{SUPERSCRIPT TWO}"
37
+ uni_R2 = "R\N{SUPERSCRIPT TWO}"
38
+
39
+
40
+ sub_sup_scripts = {
41
+ # superscript subscript
42
+ "0": ("\u2070", "\u2080"),
43
+ "1": ("\u00b9", "\u2081"),
44
+ "2": ("\u00b2", "\u2082"),
45
+ "3": ("\u00b3", "\u2083"),
46
+ "4": ("\u2074", "\u2084"),
47
+ "5": ("\u2075", "\u2085"),
48
+ "6": ("\u2076", "\u2086"),
49
+ "7": ("\u2077", "\u2087"),
50
+ "8": ("\u2078", "\u2088"),
51
+ "9": ("\u2079", "\u2089"),
52
+ "a": ("\u1d43", "\u2090"),
53
+ "b": ("\u1d47", "?"),
54
+ "c": ("\u1d9c", "?"),
55
+ "d": ("\u1d48", "?"),
56
+ "e": ("\u1d49", "\u2091"),
57
+ "f": ("\u1da0", "?"),
58
+ "g": ("\u1d4d", "?"),
59
+ "h": ("\u02b0", "\u2095"),
60
+ "i": ("\u2071", "\u1d62"),
61
+ "j": ("\u02b2", "\u2c7c"),
62
+ "k": ("\u1d4f", "\u2096"),
63
+ "l": ("\u02e1", "\u2097"),
64
+ "m": ("\u1d50", "\u2098"),
65
+ "n": ("\u207f", "\u2099"),
66
+ "o": ("\u1d52", "\u2092"),
67
+ "p": ("\u1d56", "\u209a"),
68
+ "q": ("?", "?"),
69
+ "r": ("\u02b3", "\u1d63"),
70
+ "s": ("\u02e2", "\u209b"),
71
+ "t": ("\u1d57", "\u209c"),
72
+ "u": ("\u1d58", "\u1d64"),
73
+ "v": ("\u1d5b", "\u1d65"),
74
+ "w": ("\u02b7", "?"),
75
+ "x": ("\u02e3", "\u2093"),
76
+ "y": ("\u02b8", "?"),
77
+ "z": ("?", "?"),
78
+ "A": ("\u1d2c", "?"),
79
+ "B": ("\u1d2e", "?"),
80
+ "C": ("?", "?"),
81
+ "D": ("\u1d30", "?"),
82
+ "E": ("\u1d31", "?"),
83
+ "F": ("?", "?"),
84
+ "G": ("\u1d33", "?"),
85
+ "H": ("\u1d34", "?"),
86
+ "I": ("\u1d35", "?"),
87
+ "J": ("\u1d36", "?"),
88
+ "K": ("\u1d37", "?"),
89
+ "L": ("\u1d38", "?"),
90
+ "M": ("\u1d39", "?"),
91
+ "N": ("\u1d3a", "?"),
92
+ "O": ("\u1d3c", "?"),
93
+ "P": ("\u1d3e", "?"),
94
+ "Q": ("?", "?"),
95
+ "R": ("\u1d3f", "?"),
96
+ "S": ("?", "?"),
97
+ "T": ("\u1d40", "?"),
98
+ "U": ("\u1d41", "?"),
99
+ "V": ("\u2c7d", "?"),
100
+ "W": ("\u1d42", "?"),
101
+ "X": ("?", "?"),
102
+ "Y": ("?", "?"),
103
+ "Z": ("?", "?"),
104
+ "+": ("\u207a", "\u208a"),
105
+ "-": ("\u207b", "\u208b"),
106
+ "=": ("\u207c", "\u208c"),
107
+ "(": ("\u207d", "\u208d"),
108
+ ")": ("\u207e", "\u208e"),
109
+ ":alpha": ("\u1d45", "?"),
110
+ ":beta": ("\u1d5d", "\u1d66"),
111
+ ":gamma": ("\u1d5e", "\u1d67"),
112
+ ":delta": ("\u1d5f", "?"),
113
+ ":epsilon": ("\u1d4b", "?"),
114
+ ":theta": ("\u1dbf", "?"),
115
+ ":iota": ("\u1da5", "?"),
116
+ ":pho": ("?", "\u1d68"),
117
+ ":phi": ("\u1db2", "?"),
118
+ ":psi": ("\u1d60", "\u1d69"),
119
+ ":chi": ("\u1d61", "\u1d6a"),
120
+ ":coffee": ("\u2615", "\u2615"),
121
+ }
@@ -0,0 +1,157 @@
1
+ """Reports on memory usage:
2
+
3
+ * `mem_usage`: prints the top `n` largest global items in memory
4
+ * `memory_display_top`: prints the top `n` largest memory allocations
5
+ since tracing started
6
+ * `memory_display_top_diffs`: prints the top `n` largest memory
7
+ allocations since the last snapshot.
8
+ """
9
+
10
+ import linecache
11
+ import sys
12
+ import tracemalloc
13
+
14
+ import pandas as pd
15
+
16
+ from bs_python_utils.core.bsutils import print_stars
17
+
18
+
19
+ def _obj_size_fmt(num: int) -> str:
20
+ """
21
+ format sizes from bytes to appropriate strings depending on size
22
+
23
+ Args:
24
+ num: size of object in bytes
25
+
26
+ Returns:
27
+ its formatted size
28
+ """
29
+ if num < 10**3:
30
+ return "{:.2f}{}".format(num, "B")
31
+ elif (num >= 10**3) & (num < 10**6):
32
+ return "{:.2f}{}".format(num / (1.024 * 10**3), "KB")
33
+ elif (num >= 10**6) & (num < 10**9):
34
+ return "{:.2f}{}".format(num / (1.024 * 10**6), "MB")
35
+ else:
36
+ return "{:.2f}{}".format(num / (1.024 * 10**9), "GB")
37
+
38
+
39
+ def memory_usage(n: int | None = 10) -> None:
40
+ """
41
+ prints the top `n` largest global items in memory
42
+
43
+ Args:
44
+ n: we report the size of the largest `n` global items
45
+
46
+ Returns:
47
+ nothing.
48
+ """
49
+ memory_usage_by_variable = pd.DataFrame(
50
+ {k: sys.getsizeof(v) for (k, v) in globals().items()}, index=["Size"]
51
+ )
52
+ memory_usage_by_variable = memory_usage_by_variable.T
53
+ total_usage = _obj_size_fmt(memory_usage_by_variable["Size"].sum())
54
+ memory_usage_by_variable = memory_usage_by_variable.sort_values(
55
+ by="Size", ascending=False
56
+ ).head(n)
57
+ memory_usage_by_variable["Size"] = memory_usage_by_variable["Size"].apply(
58
+ lambda x: _obj_size_fmt(x)
59
+ )
60
+ print_stars(
61
+ f"Currently used memory = {total_usage}\n\t\t\t\t Top {n} global objects:"
62
+ )
63
+ print(memory_usage_by_variable)
64
+
65
+ return
66
+
67
+
68
+ def memory_display_top(
69
+ snapshot: tracemalloc.Snapshot, key_type: str = "lineno", limit: int | None = 5
70
+ ) -> None:
71
+ """
72
+ prints out the lines with the top `limit` allocations of memory since
73
+ `tracemalloc.start()`
74
+
75
+ Args:
76
+ snapshot: obtained from tracemalloc.take_snapshot()
77
+ key_type: 'lineno' gives file and line number; 'traceback' gives all
78
+ limit: how many top allocations we want
79
+
80
+ Returns:
81
+ just prints.
82
+
83
+ Examples:
84
+ >>> tracemalloc.start()
85
+ >>> .... execute ...
86
+ >>> snapshot = tracemalloc.take_snapshot()
87
+ >>> memory_display_top(snapshot)
88
+ """
89
+
90
+ top_stats = snapshot.statistics(key_type)
91
+
92
+ print_stars(f"Top {limit} memory allocations")
93
+ for index, stat in enumerate(top_stats[:limit], 1):
94
+ frame = stat.traceback[0]
95
+ print(
96
+ "#%s: %s:%s: %.1f KiB"
97
+ % (index, frame.filename, frame.lineno, stat.size / 1024)
98
+ )
99
+ line = linecache.getline(frame.filename, frame.lineno).strip()
100
+ if line:
101
+ print(" %s" % line)
102
+
103
+ other = top_stats[limit:]
104
+ if other:
105
+ size = sum(stat.size for stat in other)
106
+ print(f"{len(other)} other: {size / 1024:.1f} KiB")
107
+ total = sum(stat.size for stat in top_stats)
108
+ print("Total allocated size: %.1f KiB" % (total / 1024))
109
+
110
+
111
+ def memory_display_top_diffs(
112
+ snapshot1: tracemalloc.Snapshot,
113
+ snapshot2: tracemalloc.Snapshot,
114
+ key_type: str = "lineno",
115
+ limit: int = 5,
116
+ ) -> None:
117
+ """
118
+ prints out the lines with the top `limit` allocations between the two snapshots
119
+
120
+ Args:
121
+ snapshot1: previous snapshot
122
+ snapshot2: new snapshot
123
+ key_type: 'lineno' gives file and line number; 'traceback' gives all
124
+ limit: how many top allocations we want
125
+
126
+ Returns:
127
+ just prints.
128
+
129
+ Examples:
130
+ >>> tracemalloc.start()
131
+ >>> .... execute ...
132
+ >>> snapshot1 = tracemalloc.take_snapshot()
133
+ >>> .... execute ...
134
+ >>> snapshot2 = tracemalloc.take_snapshot()
135
+ >>> memory_display_top_diffs(snapshot1, snapshot2)
136
+
137
+ """
138
+
139
+ top_stats = snapshot2.compare_to(snapshot1, key_type)
140
+
141
+ print_stars(f"Top {limit} new memory allocations")
142
+ for index, stat in enumerate(top_stats[:limit], 1):
143
+ frame = stat.traceback[0]
144
+ print(
145
+ "#%s: %s:%s: %.1f KiB"
146
+ % (index, frame.filename, frame.lineno, stat.size / 1024)
147
+ )
148
+ line = linecache.getline(frame.filename, frame.lineno).strip()
149
+ if line:
150
+ print(" %s" % line)
151
+
152
+ other = top_stats[limit:]
153
+ if other:
154
+ size = sum(stat.size for stat in other)
155
+ print(f"{len(other)} other: {size / 1024:.1f} KiB")
156
+ total = sum(stat.size for stat in top_stats)
157
+ print("Total allocated size: %.1f KiB" % (total / 1024))