plot-misc 2.0.2__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.
Files changed (35) hide show
  1. plot_misc/__init__.py +1 -0
  2. plot_misc/_version.py +1 -0
  3. plot_misc/barchart.py +523 -0
  4. plot_misc/constants.py +118 -0
  5. plot_misc/errors.py +328 -0
  6. plot_misc/example_data/__init__.py +1 -0
  7. plot_misc/example_data/example_datasets/bar_points.tsv.gz +0 -0
  8. plot_misc/example_data/example_datasets/barchart.tsv.gz +0 -0
  9. plot_misc/example_data/example_datasets/calibration_bins.tsv.gz +0 -0
  10. plot_misc/example_data/example_datasets/calibration_data.tsv.gz +0 -0
  11. plot_misc/example_data/example_datasets/forest_data.tsv.gz +0 -0
  12. plot_misc/example_data/example_datasets/group_bar.tsv.gz +0 -0
  13. plot_misc/example_data/example_datasets/heatmap_data.tsv.gz +0 -0
  14. plot_misc/example_data/example_datasets/incidence_matrix_data.tsv.gz +0 -0
  15. plot_misc/example_data/example_datasets/lollipop_data.tsv.gz +0 -0
  16. plot_misc/example_data/example_datasets/mace_associations.tsv.gz +0 -0
  17. plot_misc/example_data/example_datasets/net_benefit.tsv.gz +0 -0
  18. plot_misc/example_data/example_datasets/string_data.txt +1 -0
  19. plot_misc/example_data/example_datasets/volcano.tsv.gz +0 -0
  20. plot_misc/example_data/examples.py +637 -0
  21. plot_misc/forest.py +1478 -0
  22. plot_misc/heatmap.py +369 -0
  23. plot_misc/incidencematrix.py +394 -0
  24. plot_misc/machine_learning.py +1143 -0
  25. plot_misc/piechart.py +197 -0
  26. plot_misc/utils/__init__.py +1 -0
  27. plot_misc/utils/colour.py +171 -0
  28. plot_misc/utils/formatting.py +369 -0
  29. plot_misc/utils/utils.py +1151 -0
  30. plot_misc/volcano.py +203 -0
  31. plot_misc-2.0.2.dist-info/METADATA +107 -0
  32. plot_misc-2.0.2.dist-info/RECORD +35 -0
  33. plot_misc-2.0.2.dist-info/WHEEL +5 -0
  34. plot_misc-2.0.2.dist-info/licenses/LICENSE +18 -0
  35. plot_misc-2.0.2.dist-info/top_level.txt +1 -0
plot_misc/errors.py ADDED
@@ -0,0 +1,328 @@
1
+ """
2
+ This module provides utility functions and error classes to support safe and
3
+ consistent handling of inputs throughout the plot-misc codebase. It includes
4
+ mechanisms for enforcing type constraints, checking object shapes and content,
5
+ and raising informative exceptions when expectations are not met.
6
+
7
+ Classes
8
+ -------
9
+ InputValidationError
10
+ A custom exception raised when validation of a parameter fails.
11
+
12
+ Error_MSG
13
+ A container of templated error messages for validation routines.
14
+
15
+ Functions
16
+ ---------
17
+ is_type(param, types, param_name=None)
18
+ Verifies that a parameter is an instance of a given type or set of types.
19
+
20
+ is_df(df)
21
+ Confirms whether an object is a pandas DataFrame.
22
+
23
+ are_columns_in_df(df, expected_columns, warning=False)
24
+ Checks that specific column names exist in a DataFrame.
25
+
26
+ is_series_type(column, types)
27
+ Confirms all elements of a Series or DataFrame match a specified type.
28
+
29
+ same_len(object1, object2, object_names=None)
30
+ Validates that two objects have the same length.
31
+
32
+ string_to_list(object)
33
+ Wraps strings in a list; leaves other objects unchanged.
34
+
35
+ """
36
+
37
+ import inspect
38
+ import warnings
39
+ import pandas as pd
40
+ from typing import (
41
+ Any,
42
+ Type,
43
+ Set,
44
+ )
45
+ from packaging import version
46
+
47
+
48
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49
+ class InputValidationError(Exception):
50
+ """
51
+ Custom exception for signalling input validation failures.
52
+ """
53
+ pass
54
+
55
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56
+ # error messages
57
+ class Error_MSG(object):
58
+ """
59
+ Container for predefined error message templates.
60
+
61
+ Attributes
62
+ ----------
63
+ MISSING_DF : str
64
+ Message template for missing values in a DataFrame.
65
+ INVALID_STRING : str
66
+ Message for invalid string values.
67
+ INVALID_EXACT_LENGTH : str
68
+ Message for enforcing exact list or array length.
69
+ """
70
+ MISSING_DF = '`{}` contains missing values.'
71
+ INVALID_STRING = '`{}` should be limited to `{}`.'
72
+ INVALID_EXACT_LENGTH = '`{}` needs to contain exactly {} elements, not {}.'
73
+
74
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75
+ def _get_param_name(param:Any) -> str | None:
76
+ """
77
+ Attempt to infer the variable name of a parameter from the caller's scope.
78
+ """
79
+ frame = inspect.currentframe().f_back.f_back
80
+ param_names =\
81
+ [name for name, value in frame.f_locals.items() if value is param]
82
+ return param_names[0] if param_names else None
83
+
84
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85
+ def is_type(param: Any, types: tuple[Type] | Type,
86
+ param_name: str | None = None,) -> bool:
87
+ """
88
+ Checks if a given parameter matches any of the supplied types
89
+
90
+ Parameters
91
+ ----------
92
+ param : `any`
93
+ Object to test.
94
+ types: `type` or `tuple` [`type`]
95
+ Expected type(s) of the object.
96
+ param_name : `str` or `None`
97
+ Name of the parameter. Will attempt to infer the parameter name if set
98
+ to `NoneType`.
99
+
100
+ Returns
101
+ -------
102
+ bool
103
+ True if type matches.
104
+
105
+ Raises
106
+ ------
107
+ InputValidationError
108
+ If the parameter does not match any of the expected types.
109
+ """
110
+ if not isinstance(param, types):
111
+ if param_name is None:
112
+ param_name = _get_param_name(param)
113
+ else:
114
+ warnings.warn('`param_name` will be depricated.',
115
+ DeprecationWarning,
116
+ stacklevel=2,
117
+ )
118
+ raise InputValidationError(
119
+ f"Expected any of [{types}], "
120
+ f"got {type(param)}; Please see parameter: `{param_name}`."
121
+ )
122
+ return True
123
+
124
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125
+ def is_df(df: Any) -> bool:
126
+ """
127
+ Check if objects is a pd.DataFrame.
128
+
129
+ Parameters
130
+ ----------
131
+ df : `Any`
132
+ Object to test.
133
+
134
+ Returns
135
+ -------
136
+ bool
137
+ True if the object is a DataFrame.
138
+
139
+ Raises
140
+ ------
141
+ InputValidationError
142
+ If the object is not a DataFrame.
143
+ """
144
+ return is_type(df, pd.DataFrame)
145
+
146
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
147
+ # NOTE add test for expected_columns being a str and that it raises an error
148
+ # with the intact tsring rather than each letter individually.
149
+ def are_columns_in_df(
150
+ df: pd.DataFrame, expected_columns: list[str] | str, warning: bool=False,
151
+ ) -> bool:
152
+ """
153
+ Check if all expected columns are present in a given pandas.DataFrame.
154
+
155
+ Parameters
156
+ ----------
157
+ df : `pandas.DataFrame`
158
+ DataFrame to test.
159
+ expected_columns: `str` or `list` [`str`]
160
+ Expected column name(s). For easy of use any `None` entries will be
161
+ filtered out before applying the test.
162
+ warning : bool, default False
163
+ raises a warning instead of an error.
164
+
165
+ Returns
166
+ -------
167
+ bool
168
+ True if all columns are present, False if missing and `warning=True`.
169
+
170
+ Raises
171
+ ------
172
+ InputValidationError
173
+ If any columns are missing and `warning=False`.
174
+ """
175
+ # constant
176
+ message = "The following columns are missing from the pandas.DataFrame: {}"
177
+ res = True
178
+ # filtering out any potential None names
179
+ expected_columns = string_to_list(expected_columns)
180
+ expected_columns = [c for c in expected_columns if c is not None]
181
+ # tests
182
+ expected_columns_set: Set[str] = set(expected_columns) if isinstance(
183
+ expected_columns, list
184
+ ) else set([expected_columns])
185
+ missing_columns = expected_columns_set - set(df.columns)
186
+ # return
187
+ if missing_columns:
188
+ if warning == False:
189
+ raise InputValidationError(
190
+ message.format(missing_columns)
191
+ )
192
+ else:
193
+ warnings.warn(
194
+ message.format(missing_columns)
195
+ )
196
+ res = False
197
+ return res
198
+
199
+
200
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201
+ def is_series_type(column: pd.Series | pd.DataFrame, types: tuple[Type] | Type,
202
+ ) -> bool:
203
+ """
204
+ Check whether each element of a Series or DataFrame matches a given type.
205
+
206
+ Parameters
207
+ ----------
208
+ column : `pd.Series` or `pd.DataFrame`
209
+ Data structure to validate.
210
+ types : `type` or `tuple` [`tupe`]
211
+ Allowed types for individual elements.
212
+
213
+ Returns
214
+ -------
215
+ bool
216
+ True if all elements match given types.
217
+
218
+ Raises
219
+ ------
220
+ InputValidationError
221
+ If any element fails the type check.
222
+
223
+ Notes
224
+ -----
225
+ Instead of testing the dtypes, the function will look over each
226
+ element and test these individually.
227
+ """
228
+ # check input
229
+ is_type(column, (pd.DataFrame, pd.Series))
230
+ # run tests
231
+ if isinstance(column, pd.Series):
232
+ [is_type(col, types) for col in column]
233
+ elif isinstance(column, pd.DataFrame):
234
+ if version.parse('2.0.3') <= version.parse(pd.__version__):
235
+ # iteritems got depricated.
236
+ column.iteritems = column.items
237
+ for _, col in column.items():
238
+ [is_type(co, types) for co in col]
239
+ # return
240
+ return True
241
+
242
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
243
+ def same_len(object1: Any, object2: Any, object_names: list[str] | None = None,
244
+ ) -> bool:
245
+ """
246
+ Assert that two objects have the same length.
247
+
248
+ Parameters
249
+ ----------
250
+ object1 : Any
251
+ First object.
252
+ object2 : Any
253
+ Second object.
254
+ object_names : list of str, optional
255
+ Names of the two objects (for error message).
256
+
257
+ Returns
258
+ -------
259
+ bool
260
+ True if lengths match.
261
+
262
+ Notes
263
+ -----
264
+ both object1 and object2 should have a `len` method.
265
+
266
+ Raises
267
+ ------
268
+ ValueError
269
+ If lengths do not match or `object_names` is invalid.
270
+ """
271
+ n1 = len(object1)
272
+ n2 = len(object2)
273
+ if object_names is None:
274
+ object_names = ['object1', 'object2']
275
+ elif len(object_names) !=2:
276
+ raise ValueError('`object_names` should be `NoneType` or contain '
277
+ 'two strings')
278
+ # the actual test
279
+ if n1 != n2:
280
+ raise ValueError("The length of `{0}`: {1}, does not match the length "
281
+ "of `{2}`: {3}.".format(object_names[0], n1,
282
+ object_names[1], n2)
283
+ )
284
+ return True
285
+
286
+
287
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288
+ def string_to_list(object:Any) -> Any | list[str]:
289
+ """
290
+ Checks if `object` is a string and wraps this in a list, returns the
291
+ original object if it is not a string.
292
+
293
+ Parameters
294
+ ----------
295
+ object : Any
296
+ Object to check.
297
+
298
+ Returns
299
+ -------
300
+ list[str] or Any
301
+ List if input is string; otherwise the input unchanged.
302
+ """
303
+ if isinstance(object, str):
304
+ return [object]
305
+ else:
306
+ return object
307
+
308
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309
+ def number_to_list(object:Any) -> Any | list[str]:
310
+ """
311
+ Checks if `object` is a float or int and wraps this in a list, returns the
312
+ original object if it is not a string.
313
+
314
+ Parameters
315
+ ----------
316
+ object : Any
317
+ Object to check.
318
+
319
+ Returns
320
+ -------
321
+ list[str] or Any
322
+ List if input is string; otherwise the input unchanged.
323
+ """
324
+ if isinstance(object, (int, float)):
325
+ return [object]
326
+ else:
327
+ return object
328
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+ an example data string