dragon-ml-toolbox 3.11.0__tar.gz → 3.12.0__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 (31) hide show
  1. {dragon_ml_toolbox-3.11.0/dragon_ml_toolbox.egg-info → dragon_ml_toolbox-3.12.0}/PKG-INFO +1 -1
  2. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0/dragon_ml_toolbox.egg-info}/PKG-INFO +1 -1
  3. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/GUI_tools.py +147 -17
  4. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/pyproject.toml +1 -1
  5. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/LICENSE +0 -0
  6. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/LICENSE-THIRD-PARTY.md +0 -0
  7. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/README.md +0 -0
  8. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/dragon_ml_toolbox.egg-info/SOURCES.txt +0 -0
  9. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/dragon_ml_toolbox.egg-info/dependency_links.txt +0 -0
  10. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/dragon_ml_toolbox.egg-info/requires.txt +0 -0
  11. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/dragon_ml_toolbox.egg-info/top_level.txt +0 -0
  12. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ETL_engineering.py +0 -0
  13. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/MICE_imputation.py +0 -0
  14. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ML_callbacks.py +0 -0
  15. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ML_evaluation.py +0 -0
  16. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ML_trainer.py +0 -0
  17. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ML_tutorial.py +0 -0
  18. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/PSO_optimization.py +0 -0
  19. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/RNN_forecast.py +0 -0
  20. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/VIF_factor.py +0 -0
  21. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/__init__.py +0 -0
  22. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/_pytorch_models.py +0 -0
  23. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/data_exploration.py +0 -0
  24. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/datasetmaster.py +0 -0
  25. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/ensemble_learning.py +0 -0
  26. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/handle_excel.py +0 -0
  27. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/keys.py +0 -0
  28. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/logger.py +0 -0
  29. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/path_manager.py +0 -0
  30. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/ml_tools/utilities.py +0 -0
  31. {dragon_ml_toolbox-3.11.0 → dragon_ml_toolbox-3.12.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragon-ml-toolbox
3
- Version: 3.11.0
3
+ Version: 3.12.0
4
4
  Summary: A collection of tools for data science and machine learning projects.
5
5
  Author-email: Karl Loza <luigiloza@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dragon-ml-toolbox
3
- Version: 3.11.0
3
+ Version: 3.12.0
4
4
  Summary: A collection of tools for data science and machine learning projects.
5
5
  Author-email: Karl Loza <luigiloza@gmail.com>
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  import traceback
4
4
  import FreeSimpleGUI as sg
5
5
  from functools import wraps
6
- from typing import Any, Dict, Tuple, List, Literal, Union, Any, Optional, Callable
6
+ from typing import Any, Dict, Tuple, List, Literal, Union, Optional, Callable
7
7
  from .utilities import _script_info
8
8
  import numpy as np
9
9
  from .logger import _LOGGER
@@ -104,11 +104,13 @@ class ConfigManager:
104
104
  'max_size': ''
105
105
  }
106
106
  config['Layout'] = {
107
- '; Default size for continuous input boxes (width,height in characters).': '',
107
+ '; Default size for continuous input boxes (width,height in characters/rows).': '',
108
108
  'input_size_cont': '16,1',
109
- '; Default size for combo/binary boxes (width,height in characters).': '',
109
+ '; Default size for combo/binary boxes (width,height in characters/rows).': '',
110
110
  'input_size_binary': '14,1',
111
- '; Default size for buttons (width,height in characters).': '',
111
+ '; Size for multiselect listboxes (width,height in characters/rows).': '',
112
+ 'input_size_multi': '14,4',
113
+ '; Default size for buttons (width,height in characters/rows).': '',
112
114
  'button_size': '15,2'
113
115
  }
114
116
  config['Fonts'] = {
@@ -303,6 +305,57 @@ class GUIFactory:
303
305
 
304
306
  # Default to 'grid' layout
305
307
  return [columns[i:i + features_per_column] for i in range(0, len(columns), features_per_column)]
308
+
309
+ def generate_multiselect_layout(
310
+ self,
311
+ data_dict: Dict[str, Union[List[Any], Tuple[Any, ...]]],
312
+ layout_mode: Literal["grid", "row"] = 'grid',
313
+ features_per_column: int = 4
314
+ ) -> List[List[sg.Column]]:
315
+ """
316
+ Generates a layout for features using Listbox elements for multiple selections.
317
+
318
+ This allows the user to select zero or more options from a list without
319
+ being able to input custom text.
320
+
321
+ Args:
322
+ data_dict (dict): Keys are feature names, values are lists of options.
323
+ layout_mode (str): 'grid' for a multi-row grid layout, or 'row' for a single horizontal row.
324
+ features_per_column (int): Number of features per column when `layout_mode` is 'grid'.
325
+
326
+ Returns:
327
+ A list of lists of sg.Column elements, ready to be used in a window layout.
328
+ """
329
+ cfg = self.config
330
+ bg_color = sg.theme_background_color()
331
+ label_font = (cfg.fonts.font_family, cfg.fonts.label_size, cfg.fonts.label_style) # type: ignore
332
+
333
+ columns = []
334
+ for name, values in data_dict.items():
335
+ label = sg.Text(name, font=label_font, background_color=bg_color, key=f"_text_{name}")
336
+
337
+ # Use sg.Listbox for multiple selections.
338
+ element = sg.Listbox(
339
+ values,
340
+ key=name,
341
+ select_mode=sg.LISTBOX_SELECT_MODE_MULTIPLE,
342
+ size=cfg.layout.input_size_multi, # type: ignore
343
+ no_scrollbar=False
344
+ )
345
+ # -------------------
346
+
347
+ layout = [[label], [element]]
348
+ # Add a small spacer for consistent vertical alignment.
349
+ layout.append([sg.Text(" ", font=(cfg.fonts.font_family, 2), background_color=bg_color)]) # type: ignore
350
+
351
+ # Each feature is wrapped in a Column element for proper alignment.
352
+ columns.append(sg.Column(layout, background_color=bg_color))
353
+
354
+ if layout_mode == 'row':
355
+ return [columns] # A single row containing all columns
356
+
357
+ # Default to 'grid' layout
358
+ return [columns[i:i + features_per_column] for i in range(0, len(columns), features_per_column)]
306
359
 
307
360
  # --- Window Creation ---
308
361
  def create_window(self, title: str, layout: List[List[sg.Element]], **kwargs) -> sg.Window:
@@ -384,6 +437,7 @@ class FeatureMaster:
384
437
  targets: Dict[str, str],
385
438
  continuous_features: Optional[Dict[str, Tuple[str, float, float]]] = None,
386
439
  binary_features: Optional[Dict[str, str]] = None,
440
+ multi_binary_features: Optional[Dict[str, Dict[str, str]]] = None,
387
441
  one_hot_features: Optional[Dict[str, Dict[str, str]]] = None,
388
442
  categorical_features: Optional[List[Tuple[str, str, Dict[str, int]]]] = None) -> None:
389
443
  """
@@ -410,6 +464,14 @@ class FeatureMaster:
410
464
  A dictionary for binary (True/False) features.
411
465
  - **key** (str): The name to be displayed in the GUI (e.g., for a checkbox).
412
466
  - **value** (str): The model's internal feature name.
467
+
468
+ multi_binary_features (Dict[str, Dict[str, str]]):
469
+ A dictionary for features where multiple binary-like options can be
470
+ selected at once (e.g., from a multi-select listbox).
471
+ - **key** (str): The name for the group to be displayed in the GUI.
472
+ - **value** (Dict[str, str]): A nested dictionary where:
473
+ - key (str): The user-selectable option.
474
+ - value (str): The corresponding model's internal feature name.
413
475
 
414
476
  one_hot_features (Dict[str, Dict[str, str]]):
415
477
  A dictionary for features that will be one-hot encoded from a single
@@ -418,8 +480,7 @@ class FeatureMaster:
418
480
  for a dropdown menu).
419
481
  - **value** (Dict[str, str]): A nested dictionary where:
420
482
  - key (str): The user-selectable option (e.g., 'Category A').
421
- - value (str): The corresponding model column name that will be
422
- set to 1.
483
+ - value (str): The corresponding model column name.
423
484
 
424
485
  categorical_features (List[Tuple[str, str, Dict[str, int]]]):
425
486
  A list for ordinal or label-encoded categorical features.
@@ -431,7 +492,7 @@ class FeatureMaster:
431
492
  options to their corresponding integer values.
432
493
  """
433
494
  # Validation
434
- if continuous_features is None and binary_features is None and one_hot_features is None and categorical_features is None:
495
+ if continuous_features is None and binary_features is None and one_hot_features is None and categorical_features is None and multi_binary_features is None:
435
496
  raise ValueError("No features provided.")
436
497
 
437
498
  # Targets
@@ -454,6 +515,15 @@ class FeatureMaster:
454
515
  else:
455
516
  self._binary_values, self._binary_mapping = None, None
456
517
  self.has_binary = False
518
+
519
+ # multi-binary features
520
+ if multi_binary_features is not None:
521
+ self._multi_binary_values = self._handle_multi_binary_features(multi_binary_features)
522
+ self._multi_binary_mapping = multi_binary_features
523
+ self.has_multi_binary = True
524
+ else:
525
+ self._multi_binary_values, self._multi_binary_mapping = None, None
526
+ self.has_multi_binary = False
457
527
 
458
528
  # one-hot features
459
529
  if one_hot_features is not None:
@@ -493,6 +563,14 @@ class FeatureMaster:
493
563
  gui_values: dict[str, tuple[Literal["False"],Literal["True"]]] = {gui_key: ("False", "True") for gui_key in binary_features.keys()}
494
564
  # Map GUI name to Model name (same as input)
495
565
  return gui_values
566
+
567
+ def _handle_multi_binary_features(self, multi_binary_features: Dict[str, Dict[str, str]]):
568
+ # Make dictionary GUI name: range values
569
+ gui_values: dict[str, tuple[str,...]] = {
570
+ gui_key: tuple(nested_dict.keys())
571
+ for gui_key, nested_dict in multi_binary_features.items()}
572
+ # Map GUI name to Model name and preserve internal mapping (same as input)
573
+ return gui_values
496
574
 
497
575
  def _handle_one_hot_features(self, one_hot_features: Dict[str, Dict[str,str]]):
498
576
  # Make dictionary GUI name: range values
@@ -514,6 +592,8 @@ class FeatureMaster:
514
592
  all_dict.update(self._continuous_mapping)
515
593
  if self._binary_mapping is not None:
516
594
  all_dict.update(self._binary_mapping)
595
+ if self._multi_binary_mapping is not None:
596
+ all_dict.update(self._multi_binary_mapping)
517
597
  if self._one_hot_mapping is not None:
518
598
  all_dict.update(self._one_hot_mapping)
519
599
  if self._categorical_mapping is not None:
@@ -595,6 +675,28 @@ class FeatureMaster:
595
675
  """
596
676
  if self._binary_values is not None:
597
677
  return self._binary_values
678
+
679
+ @property
680
+ def multi_binary(self):
681
+ """
682
+ The mapping for multi-binary features.
683
+
684
+ Structure:
685
+ {"GUI NAME": {"GUI OPTION 1": "model_column"}}
686
+ """
687
+ if self._multi_binary_mapping is not None:
688
+ return self._multi_binary_mapping
689
+
690
+ @property
691
+ def multi_binary_gui(self):
692
+ """
693
+ The GUI options for multi-binary feature groups.
694
+
695
+ Structure:
696
+ Dict[str, Tuple[str, ...]]
697
+ """
698
+ if self._multi_binary_values is not None:
699
+ return self._multi_binary_values
598
700
 
599
701
  @property
600
702
  def one_hot(self):
@@ -697,7 +799,7 @@ class GUIHandler:
697
799
  Maps GUI name to model expected name and casts the value to float.
698
800
  """
699
801
  try:
700
- model_name = self.master.continuous[gui_feature]
802
+ model_name = self.master.continuous[gui_feature] # type: ignore
701
803
  float_value = float(chosen_value)
702
804
  except KeyError as e:
703
805
  _LOGGER.error(f"No matching name for '{gui_feature}' defined as continuous.")
@@ -713,8 +815,8 @@ class GUIHandler:
713
815
  Maps GUI name to model expected name and casts the value to binary (0,1).
714
816
  """
715
817
  try:
716
- model_name = self.master.binary[gui_feature]
717
- binary_mapping_keys = self.master.binary_gui[gui_feature]
818
+ model_name = self.master.binary[gui_feature] # type: ignore
819
+ binary_mapping_keys = self.master.binary_gui[gui_feature] # type: ignore
718
820
  except KeyError as e:
719
821
  _LOGGER.error(f"No matching name for '{gui_feature}' defined as binary.")
720
822
  raise e
@@ -725,13 +827,36 @@ class GUIHandler:
725
827
  }
726
828
  result = mapping_dict[chosen_value]
727
829
  return model_name, result
830
+
831
+ def _process_multi_binary(self, gui_feature: str, chosen_values: list[str]) -> dict[str, int]:
832
+ """
833
+ Maps GUI names to model expected names and casts values to multi-binary encoding.
834
+
835
+ For a given feature group, this sets all selected options to 1 and all
836
+ unselected options to 0.
837
+ """
838
+ try:
839
+ # Get the mapping for the group
840
+ multi_binary_mapping = self.master.multi_binary[gui_feature] # type: ignore
841
+ except KeyError as e:
842
+ _LOGGER.error(f"No matching name for '{gui_feature}' defined as multi-binary.")
843
+ raise e
844
+ else:
845
+ # Start with all possible features for this group set to 0 (unselected)
846
+ results = {model_key: 0 for model_key in multi_binary_mapping.values()}
847
+ # Set the features for the chosen options to 1
848
+ for chosen_option in chosen_values:
849
+ model_name = multi_binary_mapping[chosen_option]
850
+ results[model_name] = 1
851
+
852
+ return results
728
853
 
729
854
  def _process_one_hot(self, gui_feature: str, chosen_value: str) -> Dict[str,int]:
730
855
  """
731
856
  Maps GUI names to model expected names and casts values to one-hot encoding.
732
857
  """
733
858
  try:
734
- one_hot_mapping = self.master.one_hot[gui_feature]
859
+ one_hot_mapping = self.master.one_hot[gui_feature] # type: ignore
735
860
  except KeyError as e:
736
861
  _LOGGER.error(f"No matching name for '{gui_feature}' defined as one-hot.")
737
862
  raise e
@@ -748,7 +873,7 @@ class GUIHandler:
748
873
  Maps GUI name to model expected name and casts the value to a categorical number.
749
874
  """
750
875
  try:
751
- categorical_tuple = self.master.categorical[gui_feature]
876
+ categorical_tuple = self.master.categorical[gui_feature] # type: ignore
752
877
  except KeyError as e:
753
878
  _LOGGER.error(f"No matching name for '{gui_feature}' defined as categorical.")
754
879
  raise e
@@ -804,25 +929,31 @@ class GUIHandler:
804
929
 
805
930
  if self.master.has_continuous:
806
931
  processed_subset = self._call_subprocess(window_values=window_values,
807
- master_feature=self.master.continuous,
932
+ master_feature=self.master.continuous, # type: ignore
808
933
  processor=self._process_continuous)
809
934
  processed_features.update(processed_subset)
810
935
 
811
936
  if self.master.has_binary:
812
937
  processed_subset = self._call_subprocess(window_values=window_values,
813
- master_feature=self.master.binary,
938
+ master_feature=self.master.binary, # type: ignore
814
939
  processor=self._process_binary)
815
940
  processed_features.update(processed_subset)
941
+
942
+ if self.master.has_multi_binary:
943
+ processed_subset = self._call_subprocess(window_values=window_values,
944
+ master_feature=self.master.multi_binary, # type: ignore
945
+ processor=self._process_multi_binary)
946
+ processed_features.update(processed_subset)
816
947
 
817
948
  if self.master.has_one_hot:
818
949
  processed_subset = self._call_subprocess(window_values=window_values,
819
- master_feature=self.master.one_hot,
950
+ master_feature=self.master.one_hot, # type: ignore
820
951
  processor=self._process_one_hot)
821
952
  processed_features.update(processed_subset)
822
953
 
823
954
  if self.master.has_categorical:
824
955
  processed_subset = self._call_subprocess(window_values=window_values,
825
- master_feature=self.master.categorical,
956
+ master_feature=self.master.categorical, # type: ignore
826
957
  processor=self._process_categorical)
827
958
  processed_features.update(processed_subset)
828
959
 
@@ -836,7 +967,6 @@ class GUIHandler:
836
967
  raise RuntimeError(f"Configuration Error: Implemented methods failed to generate the required model feature: '{e}'")
837
968
 
838
969
  return np.array(final_vector, dtype=np.float32)
839
-
840
970
 
841
971
  def info():
842
972
  _script_info(__all__)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dragon-ml-toolbox"
3
- version = "3.11.0"
3
+ version = "3.12.0"
4
4
  description = "A collection of tools for data science and machine learning projects."
5
5
  authors = [
6
6
  { name = "Karl Loza", email = "luigiloza@gmail.com" }