dragon-ml-toolbox 12.9.2__py3-none-any.whl → 12.10.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragon-ml-toolbox
3
- Version: 12.9.2
3
+ Version: 12.10.0
4
4
  Summary: A collection of tools for data science and machine learning projects.
5
5
  Author-email: "Karl L. Loza Vidaurre" <luigiloza@gmail.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
- dragon_ml_toolbox-12.9.2.dist-info/licenses/LICENSE,sha256=L35WDmmLZNTlJvxF6Vy7Uy4SYNi6rCfWUqlTHpoRMoU,1081
2
- dragon_ml_toolbox-12.9.2.dist-info/licenses/LICENSE-THIRD-PARTY.md,sha256=iy2r_R7wjzsCbz_Q_jMsp_jfZ6oP8XW9QhwzRBH0mGY,1904
1
+ dragon_ml_toolbox-12.10.0.dist-info/licenses/LICENSE,sha256=L35WDmmLZNTlJvxF6Vy7Uy4SYNi6rCfWUqlTHpoRMoU,1081
2
+ dragon_ml_toolbox-12.10.0.dist-info/licenses/LICENSE-THIRD-PARTY.md,sha256=iy2r_R7wjzsCbz_Q_jMsp_jfZ6oP8XW9QhwzRBH0mGY,1904
3
3
  ml_tools/ETL_cleaning.py,sha256=2VBRllV8F-ZiPylPp8Az2gwn5ztgazN0BH5OKnRUhV0,20402
4
4
  ml_tools/ETL_engineering.py,sha256=KfYqgsxupAx6e_TxwO1LZXeu5mFkIhVXJrNjP3CzIZc,54927
5
5
  ml_tools/GUI_tools.py,sha256=Va6ig-dHULPVRwQYYtH3fvY5XPIoqRcJpRW8oXC55Hw,45413
@@ -23,7 +23,7 @@ ml_tools/__init__.py,sha256=q0y9faQ6e17XCQ7eUiCZ1FJ4Bg5EQqLjZ9f_l5REUUY,41
23
23
  ml_tools/_logger.py,sha256=dlp5cGbzooK9YSNSZYB4yjZrOaQUGW8PTrM411AOvL8,4717
24
24
  ml_tools/_script_info.py,sha256=21r83LV3RubsNZ_RTEUON6RbDf7Mh4_udweNcvdF_Fk,212
25
25
  ml_tools/constants.py,sha256=3br5Rk9cL2IUo638eJuMOGdbGQaWssaUecYEvSeRBLM,3322
26
- ml_tools/custom_logger.py,sha256=xot-VeZFigKjcVxADgzvI54vZO_MqMMejo7JmDED8Xo,5892
26
+ ml_tools/custom_logger.py,sha256=4RKhtzWf-PUsvjAEO94u8Ean4ByRGHlffxYMlvfUKbM,10061
27
27
  ml_tools/data_exploration.py,sha256=haddQFsXAWzuf84NLItcZ4Q7vzN3YWjFoh7lPlWUczo,50679
28
28
  ml_tools/ensemble_evaluation.py,sha256=FGHSe8LBI8_w8LjNeJWOcYQ1UK_mc6fVah8gmSvNVGg,26853
29
29
  ml_tools/ensemble_inference.py,sha256=0yLmLNj45RVVoSCLH1ZYJG9IoAhTkWUqEZmLOQTFGTY,9348
@@ -35,7 +35,7 @@ ml_tools/optimization_tools.py,sha256=P074YCuZzkqkONnAsM-Zb9DTX_i8cRkkJLpwAWz6CR
35
35
  ml_tools/path_manager.py,sha256=CyDU16pOKmC82jPubqJPT6EBt-u-3rGVbxyPIZCvDDY,18432
36
36
  ml_tools/serde.py,sha256=UIshIesHRFmxr8F6B3LxGG8bYc1HHK-nlE3kENSZL18,5288
37
37
  ml_tools/utilities.py,sha256=OcAyV1tEcYAfOWlGjRgopsjDLxU3DcI5EynzvWV4q3A,15754
38
- dragon_ml_toolbox-12.9.2.dist-info/METADATA,sha256=vwKDioQfPVheuLmZasMsZGFynib5C8FMc52Tn1Ql7k0,6166
39
- dragon_ml_toolbox-12.9.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- dragon_ml_toolbox-12.9.2.dist-info/top_level.txt,sha256=wm-oxax3ciyez6VoO4zsFd-gSok2VipYXnbg3TH9PtU,9
41
- dragon_ml_toolbox-12.9.2.dist-info/RECORD,,
38
+ dragon_ml_toolbox-12.10.0.dist-info/METADATA,sha256=BZeLa2SVzcXkYUOLk-NFZAQCxXht9gpQW5k4DWXy1VE,6167
39
+ dragon_ml_toolbox-12.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ dragon_ml_toolbox-12.10.0.dist-info/top_level.txt,sha256=wm-oxax3ciyez6VoO4zsFd-gSok2VipYXnbg3TH9PtU,9
41
+ dragon_ml_toolbox-12.10.0.dist-info/RECORD,,
ml_tools/custom_logger.py CHANGED
@@ -4,6 +4,8 @@ from typing import Union, List, Dict, Any
4
4
  import traceback
5
5
  import json
6
6
  import csv
7
+ from itertools import zip_longest
8
+ from collections import Counter
7
9
 
8
10
  from .path_manager import sanitize_filename, make_fullpath
9
11
  from ._script_info import _script_info
@@ -13,7 +15,8 @@ from ._logger import _LOGGER
13
15
  __all__ = [
14
16
  "custom_logger",
15
17
  "save_list_strings",
16
- "load_list_strings"
18
+ "load_list_strings",
19
+ "compare_lists"
17
20
  ]
18
21
 
19
22
 
@@ -177,5 +180,126 @@ def load_list_strings(text_file: Union[str,Path], verbose: bool=True) -> list[st
177
180
  return loaded_strings
178
181
 
179
182
 
183
+ class _RobustEncoder(json.JSONEncoder):
184
+ """
185
+ Custom JSON encoder to handle non-serializable objects.
186
+
187
+ This handles:
188
+ 1. `type` objects (e.g., <class 'int'>) which result from
189
+ `check_type_only=True`.
190
+ 2. Any other custom class or object by falling back to its
191
+ string representation.
192
+ """
193
+ def default(self, o):
194
+ if isinstance(o, type):
195
+ return str(o)
196
+ try:
197
+ return super().default(o)
198
+ except TypeError:
199
+ return str(o)
200
+
201
+ def compare_lists(
202
+ list_A: list,
203
+ list_B: list,
204
+ save_dir: Union[str, Path],
205
+ strict: bool = False,
206
+ check_type_only: bool = False
207
+ ) -> dict:
208
+ """
209
+ Compares two lists and saves a JSON report of the differences.
210
+
211
+ Args:
212
+ list_A (list): The first list to compare.
213
+ list_B (list): The second list to compare.
214
+ save_dir (str | Path): The directory where the resulting report will be saved.
215
+ strict (bool):
216
+ - If False: Performs a "bag" comparison. Order does not matter, but duplicates do.
217
+ - If True: Performs a strict, positional comparison.
218
+
219
+ check_type_only (bool):
220
+ - If False: Compares items using `==` (`__eq__` operator).
221
+ - If True: Compares only the `type()` of the items.
222
+
223
+ Returns:
224
+ A dictionary detailing the differences. (saved to `save_dir`).
225
+ """
226
+ MISSING_A_KEY = "missing_in_A"
227
+ MISSING_B_KEY = "missing_in_B"
228
+ MISMATCH_KEY = "mismatch"
229
+
230
+ results: dict[str, list] = {MISSING_A_KEY: [], MISSING_B_KEY: []}
231
+
232
+ # make directory
233
+ save_path = make_fullpath(input_path=save_dir, make=True, enforce="directory")
234
+
235
+ if strict:
236
+ # --- STRICT (Positional) Mode ---
237
+ results[MISMATCH_KEY] = []
238
+ sentinel = object()
239
+
240
+ if check_type_only:
241
+ compare_func = lambda a, b: type(a) == type(b)
242
+ else:
243
+ compare_func = lambda a, b: a == b
244
+
245
+ for index, (item_a, item_b) in enumerate(
246
+ zip_longest(list_A, list_B, fillvalue=sentinel)
247
+ ):
248
+ if item_a is sentinel:
249
+ results[MISSING_A_KEY].append({"index": index, "item": item_b})
250
+ elif item_b is sentinel:
251
+ results[MISSING_B_KEY].append({"index": index, "item": item_a})
252
+ elif not compare_func(item_a, item_b):
253
+ results[MISMATCH_KEY].append(
254
+ {
255
+ "index": index,
256
+ "list_A_item": item_a,
257
+ "list_B_item": item_b,
258
+ }
259
+ )
260
+
261
+ else:
262
+ # --- NON-STRICT (Bag) Mode ---
263
+ if check_type_only:
264
+ # Types are hashable, we can use Counter (O(N))
265
+ types_A_counts = Counter(type(item) for item in list_A)
266
+ types_B_counts = Counter(type(item) for item in list_B)
267
+
268
+ diff_A_B = types_A_counts - types_B_counts
269
+ for item_type, count in diff_A_B.items():
270
+ results[MISSING_B_KEY].extend([item_type] * count)
271
+
272
+ diff_B_A = types_B_counts - types_A_counts
273
+ for item_type, count in diff_B_A.items():
274
+ results[MISSING_A_KEY].extend([item_type] * count)
275
+
276
+ else:
277
+ # Items may be unhashable. Use O(N*M) .remove() method
278
+ temp_B = list(list_B)
279
+ missing_in_B = []
280
+
281
+ for item_a in list_A:
282
+ try:
283
+ temp_B.remove(item_a)
284
+ except ValueError:
285
+ missing_in_B.append(item_a)
286
+
287
+ results[MISSING_A_KEY] = temp_B
288
+ results[MISSING_B_KEY] = missing_in_B
289
+
290
+ # --- Save the Report ---
291
+ try:
292
+ full_path = save_path / "list_comparison.json"
293
+
294
+ # Write the report dictionary to the JSON file
295
+ with open(full_path, 'w', encoding='utf-8') as f:
296
+ json.dump(results, f, indent=4, cls=_RobustEncoder)
297
+
298
+ except Exception as e:
299
+ _LOGGER.error(f"Failed to save comparison report to {save_path}: \n{e}")
300
+
301
+ return results
302
+
303
+
180
304
  def info():
181
305
  _script_info(__all__)