machinegnostics 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.
Files changed (93) hide show
  1. __init__.py +0 -0
  2. machinegnostics/__init__.py +24 -0
  3. machinegnostics/magcal/__init__.py +37 -0
  4. machinegnostics/magcal/characteristics.py +460 -0
  5. machinegnostics/magcal/criteria_eval.py +268 -0
  6. machinegnostics/magcal/criterion.py +140 -0
  7. machinegnostics/magcal/data_conversion.py +381 -0
  8. machinegnostics/magcal/gcor.py +64 -0
  9. machinegnostics/magcal/gdf/__init__.py +2 -0
  10. machinegnostics/magcal/gdf/base_df.py +39 -0
  11. machinegnostics/magcal/gdf/base_distfunc.py +1202 -0
  12. machinegnostics/magcal/gdf/base_egdf.py +823 -0
  13. machinegnostics/magcal/gdf/base_eldf.py +830 -0
  14. machinegnostics/magcal/gdf/base_qgdf.py +1234 -0
  15. machinegnostics/magcal/gdf/base_qldf.py +1019 -0
  16. machinegnostics/magcal/gdf/cluster_analysis.py +456 -0
  17. machinegnostics/magcal/gdf/data_cluster.py +975 -0
  18. machinegnostics/magcal/gdf/data_intervals.py +853 -0
  19. machinegnostics/magcal/gdf/data_membership.py +536 -0
  20. machinegnostics/magcal/gdf/der_egdf.py +243 -0
  21. machinegnostics/magcal/gdf/distfunc_engine.py +841 -0
  22. machinegnostics/magcal/gdf/egdf.py +324 -0
  23. machinegnostics/magcal/gdf/eldf.py +297 -0
  24. machinegnostics/magcal/gdf/eldf_intv.py +609 -0
  25. machinegnostics/magcal/gdf/eldf_ma.py +627 -0
  26. machinegnostics/magcal/gdf/homogeneity.py +1218 -0
  27. machinegnostics/magcal/gdf/intv_engine.py +1523 -0
  28. machinegnostics/magcal/gdf/marginal_intv_analysis.py +558 -0
  29. machinegnostics/magcal/gdf/qgdf.py +289 -0
  30. machinegnostics/magcal/gdf/qldf.py +296 -0
  31. machinegnostics/magcal/gdf/scedasticity.py +197 -0
  32. machinegnostics/magcal/gdf/wedf.py +181 -0
  33. machinegnostics/magcal/gdf/z0_estimator.py +1047 -0
  34. machinegnostics/magcal/layer_base.py +42 -0
  35. machinegnostics/magcal/layer_history_base.py +74 -0
  36. machinegnostics/magcal/layer_io_process_base.py +238 -0
  37. machinegnostics/magcal/layer_param_base.py +448 -0
  38. machinegnostics/magcal/mg_weights.py +36 -0
  39. machinegnostics/magcal/sample_characteristics.py +532 -0
  40. machinegnostics/magcal/scale_optimization.py +185 -0
  41. machinegnostics/magcal/scale_param.py +313 -0
  42. machinegnostics/magcal/util/__init__.py +0 -0
  43. machinegnostics/magcal/util/dis_docstring.py +18 -0
  44. machinegnostics/magcal/util/logging.py +24 -0
  45. machinegnostics/magcal/util/min_max_float.py +34 -0
  46. machinegnostics/magnet/__init__.py +0 -0
  47. machinegnostics/metrics/__init__.py +28 -0
  48. machinegnostics/metrics/accu.py +61 -0
  49. machinegnostics/metrics/accuracy.py +67 -0
  50. machinegnostics/metrics/auto_correlation.py +183 -0
  51. machinegnostics/metrics/auto_covariance.py +204 -0
  52. machinegnostics/metrics/cls_report.py +130 -0
  53. machinegnostics/metrics/conf_matrix.py +93 -0
  54. machinegnostics/metrics/correlation.py +178 -0
  55. machinegnostics/metrics/cross_variance.py +167 -0
  56. machinegnostics/metrics/divi.py +82 -0
  57. machinegnostics/metrics/evalmet.py +109 -0
  58. machinegnostics/metrics/f1_score.py +128 -0
  59. machinegnostics/metrics/gmmfe.py +108 -0
  60. machinegnostics/metrics/hc.py +141 -0
  61. machinegnostics/metrics/mae.py +72 -0
  62. machinegnostics/metrics/mean.py +117 -0
  63. machinegnostics/metrics/median.py +122 -0
  64. machinegnostics/metrics/mg_r2.py +167 -0
  65. machinegnostics/metrics/mse.py +78 -0
  66. machinegnostics/metrics/precision.py +119 -0
  67. machinegnostics/metrics/r2.py +122 -0
  68. machinegnostics/metrics/recall.py +108 -0
  69. machinegnostics/metrics/rmse.py +77 -0
  70. machinegnostics/metrics/robr2.py +119 -0
  71. machinegnostics/metrics/std.py +144 -0
  72. machinegnostics/metrics/variance.py +101 -0
  73. machinegnostics/models/__init__.py +2 -0
  74. machinegnostics/models/classification/__init__.py +1 -0
  75. machinegnostics/models/classification/layer_history_log_reg.py +121 -0
  76. machinegnostics/models/classification/layer_io_process_log_reg.py +98 -0
  77. machinegnostics/models/classification/layer_mlflow_log_reg.py +107 -0
  78. machinegnostics/models/classification/layer_param_log_reg.py +275 -0
  79. machinegnostics/models/classification/mg_log_reg.py +273 -0
  80. machinegnostics/models/cross_validation.py +118 -0
  81. machinegnostics/models/data_split.py +106 -0
  82. machinegnostics/models/regression/__init__.py +2 -0
  83. machinegnostics/models/regression/layer_histroy_rob_reg.py +139 -0
  84. machinegnostics/models/regression/layer_io_process_rob_rig.py +88 -0
  85. machinegnostics/models/regression/layer_mlflow_rob_reg.py +134 -0
  86. machinegnostics/models/regression/layer_param_rob_reg.py +212 -0
  87. machinegnostics/models/regression/mg_lin_reg.py +253 -0
  88. machinegnostics/models/regression/mg_poly_reg.py +258 -0
  89. machinegnostics-0.0.1.dist-info/METADATA +246 -0
  90. machinegnostics-0.0.1.dist-info/RECORD +93 -0
  91. machinegnostics-0.0.1.dist-info/WHEEL +5 -0
  92. machinegnostics-0.0.1.dist-info/licenses/LICENSE +674 -0
  93. machinegnostics-0.0.1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,456 @@
1
+ '''
2
+ Cluster Analysis
3
+
4
+ Module for clustering-based bound estimation for interval analysis.
5
+ This class do cluster end-to-end cluster analysis to estimate bounds.
6
+
7
+ Authors: Nirmal Parmar
8
+ Machine Gnostics
9
+ '''
10
+
11
+ import numpy as np
12
+ import warnings
13
+ import logging
14
+ from machinegnostics.magcal.util.logging import get_logger
15
+ from machinegnostics.magcal import ELDF, EGDF, DataCluster, DataHomogeneity
16
+
17
+ class ClusterAnalysis:
18
+ """
19
+ ClusterAnalysis - End-to-End Clustering-Based Bound Estimation for Interval Analysis
20
+
21
+ The `ClusterAnalysis` class provides a high-level, automated workflow for estimating the main cluster bounds of a dataset using Gnostic Distribution Functions (GDFs) and advanced clustering analysis. It is designed for robust, interpretable, and reproducible interval estimation in scientific, engineering, and data science applications.
22
+
23
+ This class orchestrates the entire process of:
24
+ 1. Fitting a GDF (typically ELDF/EGDF) to the data,
25
+ 2. Assessing data homogeneity,
26
+ 3. Performing cluster boundary detection using the DataCluster algorithm,
27
+ 4. Returning interpretable lower and upper cluster bounds (LCB, UCB) for the main data cluster.
28
+
29
+ **Key Features:**
30
+ - Fully automated pipeline for cluster-based bound estimation
31
+ - Integrates GDF fitting, homogeneity testing, and cluster analysis
32
+ - Supports both local (ELDF) and global (EGDF) GDFs
33
+ - Handles weighted data, bounded/unbounded domains, and advanced parameterization
34
+ - Detailed error/warning logging and reproducible parameter tracking
35
+ - Optional memory-efficient operation via flushing intermediate results
36
+ - Visualization support for both GDF and cluster analysis results
37
+
38
+ Parameters
39
+ ----------
40
+ verbose : bool, optional (default=False)
41
+ If True, prints detailed logs and progress information during processing.
42
+ catch : bool, optional (default=True)
43
+ If True, stores intermediate results and diagnostic information for further analysis.
44
+ derivative_threshold : float, optional (default=0.01)
45
+ Threshold for derivative-based cluster boundary detection (used by DataCluster).
46
+ slope_percentile : int, optional (default=70)
47
+ Percentile threshold for slope-based boundary detection (used by DataCluster).
48
+ DLB : float, optional
49
+ Data Lower Bound. If None, inferred from data.
50
+ DUB : float, optional
51
+ Data Upper Bound. If None, inferred from data.
52
+ LB : float, optional
53
+ Lower probable bound for the distribution.
54
+ UB : float, optional
55
+ Upper probable bound for the distribution.
56
+ S : float or str, optional (default='auto')
57
+ Scale parameter for the GDF. Use 'auto' for automatic estimation.
58
+ varS : bool, optional (default=False)
59
+ If True, allows variable scale parameter during optimization.
60
+ z0_optimize : bool, optional (default=True)
61
+ If True, optimizes the location parameter Z0 during fitting.
62
+ tolerance : float, optional (default=1e-5)
63
+ Convergence tolerance for optimization.
64
+ data_form : str, optional (default='a')
65
+ Data processing form: 'a' for additive, 'm' for multiplicative.
66
+ n_points : int, optional (default=1000)
67
+ Number of points for GDF evaluation and PDF generation.
68
+ homogeneous : bool, optional (default=True)
69
+ If True, assumes data is homogeneous; triggers warnings if not.
70
+ weights : np.ndarray, optional
71
+ Prior weights for data points. If None, uniform weights are used.
72
+ wedf : bool, optional (default=False)
73
+ If True, uses Weighted Empirical Distribution Function.
74
+ opt_method : str, optional (default='L-BFGS-B')
75
+ Optimization method for parameter estimation.
76
+ max_data_size : int, optional (default=1000)
77
+ Maximum data size for smooth GDF generation.
78
+ flush : bool, optional (default=False)
79
+ If True, flushes intermediate results after fitting to save memory.
80
+
81
+ Attributes
82
+ ----------
83
+ LCB : float or None
84
+ Lower Cluster Bound estimated from the data (main cluster lower edge).
85
+ UCB : float or None
86
+ Upper Cluster Bound estimated from the data (main cluster upper edge).
87
+ params : dict
88
+ Dictionary containing all parameters, intermediate results, errors, and warnings.
89
+ _fitted : bool
90
+ Indicates whether the analysis has been successfully completed.
91
+
92
+ Methods
93
+ -------
94
+ fit(data: np.ndarray, plot: bool = False) -> tuple
95
+ Runs the full cluster analysis pipeline on the input data.
96
+ Returns (LCB, UCB) as the main cluster bounds.
97
+ results() -> dict
98
+ Returns a dictionary with the estimated bounds and key results.
99
+ plot() -> None
100
+ Visualizes the fitted GDF and cluster analysis results (if not flushed).
101
+
102
+ Workflow
103
+ --------
104
+ 1. Initialize ClusterAnalysis with desired parameters (no data required).
105
+ 2. Call `fit(data)` to perform the complete analysis and estimate cluster bounds.
106
+ 3. Access results via `results()` or visualize with `plot()`.
107
+
108
+ Example
109
+ -------
110
+ >>> from machinegnostics.magcal import ClusterAnalysis
111
+ >>> data = np.random.normal(0, 1, 1000)
112
+ >>> ca = ClusterAnalysis(verbose=True)
113
+ >>> LCB, UCB = ca.fit(data)
114
+ >>> print(f"Main cluster bounds: LCB={LCB:.3f}, UCB={UCB:.3f}")
115
+ >>> ca.plot()
116
+ >>> results = ca.results()
117
+ >>> print(results)
118
+
119
+ Notes
120
+ -----
121
+ - The class is designed for robust, interpretable cluster-based bound estimation.
122
+ - Works best with local GDFs (ELDF); global GDFs (EGDF) are supported but less flexible for clustering.
123
+ - If `homogeneous=True` but the data is found to be heterogeneous, a warning is issued.
124
+ - All intermediate parameters, errors, and warnings are tracked in `params` for reproducibility.
125
+ - For large datasets or memory-constrained environments, set `flush=True` to save memory (but disables plotting).
126
+
127
+ Raises
128
+ ------
129
+ RuntimeError
130
+ If results or plot are requested before fitting.
131
+ Exception
132
+ If any step in the pipeline fails (errors are logged in `params`).
133
+
134
+ References
135
+ ----------
136
+ - Gnostic Distribution Function theory and clustering methods (see mathematical gnostics literature).
137
+ - For details on the underlying algorithms, see the documentation for ELDF, EGDF, and DataCluster classes.
138
+ """
139
+
140
+ def __init__(self,
141
+ verbose: bool = False,
142
+ catch: bool = True,
143
+ derivative_threshold: float = 0.01,
144
+ slope_percentile: int = 70,
145
+ DLB: float = None,
146
+ DUB: float = None,
147
+ LB: float = None,
148
+ UB: float = None,
149
+ S: str = 'auto',
150
+ varS: bool = False,
151
+ z0_optimize: bool = True,
152
+ tolerance: float = 0.00001,
153
+ data_form: str = 'a',
154
+ n_points: int = 1000,
155
+ homogeneous: bool = True,
156
+ weights: np.ndarray = None,
157
+ wedf: bool = False,
158
+ opt_method: str = 'L-BFGS-B',
159
+ max_data_size: int = 1000,
160
+ flush: bool = False
161
+ ):
162
+ ELDF.__init__(self)
163
+ self.verbose = verbose
164
+ self.catch = catch
165
+ self.derivative_threshold = derivative_threshold
166
+ self.slope_percentile = slope_percentile
167
+ self.DLB = DLB
168
+ self.DUB = DUB
169
+ self.LB = LB
170
+ self.UB = UB
171
+ self.S = S
172
+ self.varS = varS
173
+ self.z0_optimize = z0_optimize
174
+ self.tolerance = tolerance
175
+ self.data_form = data_form
176
+ self.n_points = n_points
177
+ self.homogeneous = homogeneous
178
+ self.weights = weights
179
+ self.wedf = wedf
180
+ self.opt_method = opt_method
181
+ self.max_data_size = max_data_size
182
+ self.flush = flush
183
+
184
+ self._fitted = False
185
+
186
+ self.LCB = None
187
+ self.UCB = None
188
+
189
+ self.params = {}
190
+ self.params['error'] = []
191
+ self.params['warnings'] = []
192
+
193
+ # append arguments to params
194
+ if self.catch:
195
+ self.params['ClusterAnalysis'] = {
196
+ 'verbose': self.verbose,
197
+ 'catch': self.catch,
198
+ 'derivative_threshold': self.derivative_threshold,
199
+ 'slope_percentile': self.slope_percentile,
200
+ 'DLB': self.DLB,
201
+ 'DUB': self.DUB,
202
+ 'LB': self.LB,
203
+ 'UB': self.UB,
204
+ 'S': self.S,
205
+ 'varS': self.varS,
206
+ 'z0_optimize': self.z0_optimize,
207
+ 'tolerance': self.tolerance,
208
+ 'data_form': self.data_form,
209
+ 'n_points': self.n_points,
210
+ 'homogeneous': self.homogeneous,
211
+ 'weights': self.weights,
212
+ 'wedf': self.wedf,
213
+ 'opt_method': self.opt_method,
214
+ 'max_data_size': self.max_data_size,
215
+ 'flush': self.flush
216
+ }
217
+
218
+ # logger setup
219
+ self.logger = get_logger(self.__class__.__name__, logging.DEBUG if verbose else logging.WARNING)
220
+ self.logger.debug(f"{self.__class__.__name__} initialized:")
221
+
222
+ def _add_warning(self, warning: str):
223
+ self.params['warnings'].append(warning)
224
+ self.logger.warning(warning)
225
+
226
+ def _add_error(self, error: str):
227
+ self.params['error'].append(error)
228
+ self.logger.error(error)
229
+
230
+ def fit(self, data: np.ndarray, plot: bool = False) -> tuple:
231
+ """
232
+ Fit the ClusterAnalysis model to the input data and estimate main cluster bounds.
233
+
234
+ This method performs a comprehensive, automated clustering-based interval analysis on the provided data.
235
+ It integrates Gnostic Distribution Function (GDF) fitting, homogeneity assessment, and advanced cluster boundary detection
236
+ to robustly estimate the lower and upper bounds (LCB, UCB) of the main data cluster.
237
+
238
+ The process is designed to be robust, interpretable, and reproducible, handling both homogeneous and heterogeneous data,
239
+ supporting weighted and bounded datasets, and providing detailed diagnostics and error handling.
240
+ All intermediate results, warnings, and errors are tracked in the `params` attribute for transparency.
241
+
242
+ Parameters
243
+ ----------
244
+ data : np.ndarray
245
+ Input data array for interval analysis. Should be 1D and numeric.
246
+ plot : bool, optional (default=False)
247
+ If True, generates plots for the fitted GDF and cluster analysis.
248
+
249
+ Returns
250
+ -------
251
+ tuple
252
+ (LCB, UCB): The estimated lower and upper bounds of the main data cluster.
253
+ Returns (None, None) if fitting fails.
254
+
255
+ Notes
256
+ -----
257
+ - If the data is found to be heterogeneous but `homogeneous=True`, a warning is issued.
258
+ - If `flush=True`, intermediate objects are deleted after fitting to save memory (disables plotting).
259
+ - All errors and warnings are logged in `params` for reproducibility and debugging.
260
+
261
+ Raises
262
+ ------
263
+ Exception
264
+ Any error during the fitting process is caught, logged, and (LCB, UCB) is set to (None, None).
265
+
266
+ Example
267
+ -------
268
+ >>> ca = ClusterAnalysis(verbose=True)
269
+ >>> LCB, UCB = ca.fit(data)
270
+ >>> print(f"Cluster bounds: LCB={LCB}, UCB={UCB}")
271
+ """
272
+ self.logger.info("Starting ClusterAnalysis fit process...")
273
+ try:
274
+ kwrgs_egdf = {
275
+ "DLB": self.DLB,
276
+ "DUB": self.DUB,
277
+ "LB": self.LB,
278
+ "UB": self.UB,
279
+ "S": self.S,
280
+ "z0_optimize": self.z0_optimize,
281
+ "tolerance": self.tolerance,
282
+ "data_form": self.data_form,
283
+ "n_points": self.n_points,
284
+ "homogeneous": self.homogeneous,
285
+ "catch": self.catch,
286
+ "weights": self.weights,
287
+ "wedf": self.wedf,
288
+ "opt_method": self.opt_method,
289
+ "verbose": self.verbose,
290
+ "max_data_size": self.max_data_size,
291
+ "flush": self.flush
292
+ }
293
+ # estimate egdf
294
+ self.logger.info("ClusterAnalysis: Fitting EGDF...")
295
+ self._egdf = EGDF(**kwrgs_egdf)
296
+ self._egdf.fit(data, plot=False)
297
+ if self.catch:
298
+ self.params['EGDF'] = self._egdf.params
299
+
300
+ # check data homogeneity
301
+ self._data_homogeneity = DataHomogeneity(gdf=self._egdf,
302
+ verbose=self.verbose,
303
+ catch=self.catch,
304
+ flush=self.flush)
305
+ is_homogeneous = self._data_homogeneity.fit(plot=False)
306
+ if self.catch:
307
+ self.params['DataHomogeneity'] = self._data_homogeneity.params
308
+
309
+ # if self.homogeneous is True, and is_homogeneous is False, raise a warning for user, that user understanding for data may not be correct
310
+ if self.homogeneous and not is_homogeneous:
311
+ warning_msg = "Data is not homogeneous, but 'homogeneous' parameter is set to True. User understanding for data may not be correct."
312
+ self._add_warning(warning_msg)
313
+ warnings.warn(warning_msg)
314
+
315
+ # fit eldf
316
+ self.logger.info("ClusterAnalysis: Fitting ELDF...")
317
+ self._eldf = ELDF(DLB=self.DLB,
318
+ DUB=self.DUB,
319
+ LB=self.LB,
320
+ UB=self.UB,
321
+ S=self.S,
322
+ varS=self.varS,
323
+ z0_optimize=self.z0_optimize,
324
+ tolerance=self.tolerance,
325
+ data_form=self.data_form,
326
+ n_points=self.n_points,
327
+ homogeneous=self.homogeneous,
328
+ catch=self.catch,
329
+ weights=self.weights,
330
+ wedf=self.wedf,
331
+ opt_method=self.opt_method,
332
+ verbose=self.verbose,
333
+ max_data_size=self.max_data_size,
334
+ flush=self.flush)
335
+ self._eldf.fit(data, plot=False)
336
+ if self.catch:
337
+ self.params['ELDF'] = self._eldf.params
338
+
339
+ # get cluster bounds
340
+ self.logger.info("ClusterAnalysis: Estimating cluster bounds...")
341
+
342
+ # note for user, if is_homogeneous is False, LCB and UCB will provide main cluster of the data.
343
+ if not is_homogeneous:
344
+ info_msg = "Data is not homogeneous, LCB and UCB will provide bounds for the main cluster of the data."
345
+ self._add_warning(info_msg)
346
+ self.logger.info(f'ClusterAnalysis: Info: {info_msg}')
347
+
348
+ self._data_cluster = DataCluster(gdf=self._eldf,
349
+ verbose=self.verbose,
350
+ catch=self.catch,
351
+ derivative_threshold=self.derivative_threshold, slope_percentile=self.slope_percentile)
352
+ self.LCB, self.UCB = self._data_cluster.fit(plot=plot)
353
+ if self.catch:
354
+ self.params['DataCluster'] = self._data_cluster.params
355
+
356
+ # save results
357
+ self._fitted = True
358
+ if self.catch:
359
+ self.params['results'] = {
360
+ 'LCB': self.LCB,
361
+ 'UCB': self.UCB
362
+ }
363
+
364
+ # flush
365
+ if self.flush:
366
+ self._egdf = None
367
+ self._eldf = None
368
+ self._data_homogeneity = None
369
+ self._data_cluster = None
370
+ # deleter respective params to save memory
371
+ # keep erros and warnings
372
+ if self.catch:
373
+ if 'EGDF' in self.params:
374
+ del self.params['EGDF']
375
+ if 'ELDF' in self.params:
376
+ del self.params['ELDF']
377
+ if 'DataHomogeneity' in self.params:
378
+ del self.params['DataHomogeneity']
379
+ if 'DataCluster' in self.params:
380
+ del self.params['DataCluster']
381
+ self.logger.info("ClusterAnalysis: Data flushed to save memory.")
382
+
383
+ self.logger.info(f'ClusterAnalysis: Fitting completed. LCB: {self.LCB}, UCB: {self.UCB}')
384
+ return self.LCB, self.UCB
385
+
386
+ except Exception as e:
387
+ self._add_error(str(e))
388
+ self.logger.error(f'ClusterAnalysis: Error during fit: {e}')
389
+ return None, None
390
+
391
+ def results(self) -> dict:
392
+ """
393
+ Get the results of the Cluster Analysis.
394
+
395
+ Returns
396
+ -------
397
+ dict
398
+ A dictionary containing the estimated lower and upper cluster bounds:
399
+ {
400
+ 'LCB': float,
401
+ 'UCB': float
402
+ }
403
+
404
+ Raises
405
+ ------
406
+ RuntimeError
407
+ If the model has not been fitted yet.
408
+
409
+ Notes
410
+ -----
411
+ - Call this method after `fit()` to retrieve the main cluster bounds.
412
+ - The returned values are floats or None if fitting failed.
413
+ """
414
+ self.logger.info("Retrieving ClusterAnalysis results...")
415
+ if not self._fitted:
416
+ self.logger.error("ClusterAnalysis: The model is not fitted yet. Please call the 'fit' method first.")
417
+ raise RuntimeError("ClusterAnalysis: The model is not fitted yet. Please call the 'fit' method first.")
418
+ return {
419
+ 'LCB': float(self.LCB),
420
+ 'UCB': float(self.UCB)
421
+ }
422
+
423
+ def plot(self) -> None:
424
+ """
425
+ Plot the ELDF and DataCluster results.
426
+
427
+ This method visualizes the fitted Empirical Local Distribution Function (ELDF)
428
+ and the detected cluster boundaries from DataCluster. It is only available if
429
+ the model has been fitted and intermediate data has not been flushed.
430
+
431
+ Returns
432
+ -------
433
+ None
434
+
435
+ Raises
436
+ ------
437
+ RuntimeError
438
+ If the model has not been fitted yet, or if `flush=True` was set and intermediate data is unavailable.
439
+
440
+ Notes
441
+ -----
442
+ - Call this method after `fit()` to visualize the results.
443
+ - If `flush=True` was set during initialization, plotting is disabled to save memory.
444
+ """
445
+ if not self._fitted:
446
+ self.logger.error("ClusterAnalysis: The model is not fitted yet. Please call the 'fit' method first.")
447
+ raise RuntimeError("ClusterAnalysis: The model is not fitted yet. Please call the 'fit' method first.")
448
+ if self.flush:
449
+ self.logger.error("ClusterAnalysis: Data has been flushed. Cannot plot. Please set 'flush' to False during initialization to enable plotting.")
450
+ raise RuntimeError("ClusterAnalysis: Data has been flushed. Cannot plot. Please set 'flush' to False during initialization to enable plotting.")
451
+
452
+ # Plot ELDF
453
+ self._eldf.plot(plot='both')
454
+
455
+ # Plot DataCluster
456
+ self._data_cluster.plot()