halib 0.1.61__py3-none-any.whl → 0.1.66__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.
@@ -0,0 +1,121 @@
1
+ # -------------------------------
2
+ # Metrics Backend Interface
3
+ # -------------------------------
4
+ import inspect
5
+ from typing import Dict, Union, List, Any
6
+ from abc import ABC, abstractmethod
7
+
8
+ class MetricsBackend(ABC):
9
+ """Interface for pluggable metrics computation backends."""
10
+
11
+ def __init__(self, metrics_info: Union[List[str], Dict[str, Any]]):
12
+ """
13
+ Initialize the backend with optional metrics_info.
14
+ """
15
+ self.metric_info = metrics_info
16
+ self.validate_metrics_info(self.metric_info)
17
+
18
+ @property
19
+ def metric_names(self) -> List[str]:
20
+ """
21
+ Return a list of metric names.
22
+ If metric_info is a dict, return its keys; if it's a list, return it directly.
23
+ """
24
+ if isinstance(self.metric_info, dict):
25
+ return list(self.metric_info.keys())
26
+ elif isinstance(self.metric_info, list):
27
+ return self.metric_info
28
+ else:
29
+ raise TypeError("metric_info must be a list or a dict")
30
+
31
+ def validate_metrics_info(self, metrics_info):
32
+ if isinstance(metrics_info, list):
33
+ return metrics_info
34
+ elif isinstance(metrics_info, dict):
35
+ return {k: v for k, v in metrics_info.items() if isinstance(k, str)}
36
+ else:
37
+ raise TypeError(
38
+ "metrics_info must be a list of strings or a dict with string keys"
39
+ )
40
+
41
+ @abstractmethod
42
+ def compute_metrics(
43
+ self, metrics_info: Union[List[str], Dict[str, Any]], metrics_data_dict: Dict[str, Any], *args, **kwargs
44
+ ) -> Dict[str, Any]:
45
+ pass
46
+
47
+ def calc_metrics(
48
+ self, metrics_data_dict: Dict[str, Any], *args, **kwargs
49
+ ) -> Dict[str, Any]:
50
+ """
51
+ Calculate metrics based on the provided metrics_info and data.
52
+ This method should be overridden by subclasses to implement specific metric calculations.
53
+ """
54
+ # prevalidate the metrics_data_dict
55
+ for metric in self.metric_names:
56
+ if metric not in metrics_data_dict:
57
+ raise ValueError(f"Metric '{metric}' not found in provided data.")
58
+ # Call the abstract method to compute metrics
59
+ return self.compute_metrics(self.metric_info, metrics_data_dict, *args, **kwargs)
60
+
61
+ class TorchMetricsBackend(MetricsBackend):
62
+ """TorchMetrics-based backend implementation."""
63
+
64
+ def __init__(self, metrics_info: Union[List[str], Dict[str, Any]]):
65
+ try:
66
+ import torch
67
+ from torchmetrics import Metric
68
+ except ImportError:
69
+ raise ImportError(
70
+ "TorchMetricsBackend requires torch and torchmetrics to be installed."
71
+ )
72
+ self.metric_info = metrics_info
73
+ self.torch = torch
74
+ self.Metric = Metric
75
+ self.validate_metrics_info(metrics_info)
76
+
77
+ def validate_metrics_info(self, metrics_info):
78
+ if not isinstance(metrics_info, dict):
79
+ raise TypeError(
80
+ "TorchMetricsBackend requires metrics_info as a dict {name: MetricInstance}"
81
+ )
82
+ for k, v in metrics_info.items():
83
+ if not isinstance(k, str):
84
+ raise TypeError(f"Key '{k}' is not a string")
85
+ if not isinstance(v, self.Metric):
86
+ raise TypeError(f"Value for key '{k}' must be a torchmetrics.Metric")
87
+ return metrics_info
88
+
89
+ def compute_metrics(self, metrics_info, metrics_data_dict, *args, **kwargs):
90
+ out_dict = {}
91
+ for metric, metric_instance in metrics_info.items():
92
+ if metric not in metrics_data_dict:
93
+ raise ValueError(f"Metric '{metric}' not found in provided data.")
94
+
95
+ metric_data = metrics_data_dict[metric]
96
+ sig = inspect.signature(metric_instance.update)
97
+ expected_args = list(sig.parameters.values())
98
+
99
+ if isinstance(metric_data, dict):
100
+ args = [metric_data[param.name] for param in expected_args]
101
+ elif isinstance(metric_data, (list, tuple)):
102
+ args = metric_data
103
+ else:
104
+ raise TypeError(f"Unsupported data format for metric '{metric}'")
105
+
106
+ if len(expected_args) == 1:
107
+ metric_instance.update(args)
108
+ else:
109
+ metric_instance.update(*args)
110
+
111
+ computed_value = metric_instance.compute()
112
+ if isinstance(computed_value, self.torch.Tensor):
113
+ computed_value = (
114
+ computed_value.item()
115
+ if computed_value.numel() == 1
116
+ else computed_value.tolist()
117
+ )
118
+
119
+
120
+ out_dict[metric] = computed_value
121
+ return out_dict
@@ -8,65 +8,31 @@ from functools import wraps
8
8
  from rich.pretty import pprint
9
9
 
10
10
  from abc import ABC, abstractmethod
11
+ from collections import OrderedDict
11
12
 
12
13
  from ..filetype import csvfile
13
14
  from ..common import now_str
14
15
  from ..research.perftb import PerfTB
16
+ from ..research.metrics import *
15
17
 
16
- # try to import torch, and torchmetrics
17
- try:
18
- import torch
19
- import torchmetrics
20
- from torchmetrics import Metric
21
- except ImportError:
22
- raise ImportError("Please install torch and torchmetrics to use this module.")
18
+ # # try to import torch, and torchmetrics
19
+ # try:
20
+ # import torch
21
+ # import torchmetrics
22
+ # from torchmetrics import Metric
23
+ # except ImportError:
24
+ # raise ImportError("Please install torch and torchmetrics to use this module.")
23
25
 
24
- def validate_torch_metrics(fn):
25
- @wraps(fn)
26
- def wrapper(self, *args, **kwargs):
27
- result = fn(self, *args, **kwargs)
28
-
29
- if not isinstance(result, dict):
30
- raise TypeError("torch_metrics() must return a dictionary")
31
-
32
- for k, v in result.items():
33
- if not isinstance(k, str):
34
- raise TypeError(f"Key '{k}' is not a string")
35
- if not isinstance(v, Metric):
36
- raise TypeError(
37
- f"Value for key '{k}' is not a torchmetrics.Metric (got {type(v).__name__})"
38
- )
39
-
40
- return result
41
-
42
- return wrapper
43
- def valid_custom_fields(fn):
44
- @wraps(fn)
45
- def wrapper(self, *args, **kwargs):
46
- rs = fn(self, *args, **kwargs)
47
- if not isinstance(rs, tuple) or len(rs) != 2:
48
- raise ValueError("Function must return a tuple (outdict, custom_fields)")
49
- outdict, custom_fields = rs
50
- if not isinstance(outdict, dict):
51
- raise TypeError("Output must be a dictionary")
52
- if not isinstance(custom_fields, list):
53
- raise TypeError("Custom fields must be a list")
54
- for field in custom_fields:
55
- if not isinstance(field, str):
56
- raise TypeError(f"Custom field '{field}' is not a string")
57
- return outdict, custom_fields
58
-
59
- return wrapper
60
26
 
61
27
  REQUIRED_COLS = ["experiment", "dataset"]
62
28
  CSV_FILE_POSTFIX = "__perf"
63
29
 
64
30
  class PerfCalc(ABC): # Abstract base class for performance calculation
65
31
  @abstractmethod
66
- def get_exp_torch_metrics(self):
32
+ def get_experiment_name(self):
67
33
  """
68
- Return a dictionary of torchmetrics to be used for performance calculation.
69
- Example: {"accuracy": Accuracy(), "precision": Precision()}
34
+ Return the name of the experiment.
35
+ This function should be overridden by the subclass if needed.
70
36
  """
71
37
  pass
72
38
 
@@ -79,20 +45,22 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
79
45
  pass
80
46
 
81
47
  @abstractmethod
82
- def prepare_exp_data_for_metrics(self, metric_names, *args, **kwargs):
48
+ def get_metric_backend(self) -> MetricsBackend:
83
49
  """
84
- Prepare the data for metrics.
85
- This function should be overridden by the subclass if needed.
86
- Must return a dictionary with keys as metric names and values as the data to be used for those metrics.
87
- NOTE: that the data (for each metric) must be in the format expected by the torchmetrics instance (for that metric). E.g: {"accuracy": {"preds": [...], "target": [...]}, ...} since torchmetrics expects the data in a specific format.
50
+ Return a list of metric names to be used for performance calculation OR a dictionaray with keys as metric names and values as metric instances of torchmetrics.Metric. For example: {"accuracy": Accuracy(), "precision": Precision()}
51
+
88
52
  """
89
53
  pass
90
54
 
55
+ # ! can be override, but ONLY if torchmetrics are used
56
+ # Prepare the exp data for torch metrics.
91
57
  @abstractmethod
92
- def get_experiment_name(self):
58
+ def prepare_metrics_data_dict(self, metric_names, *args, **kwargs):
93
59
  """
94
- Return the name of the experiment.
60
+ Prepare the data for metrics.
95
61
  This function should be overridden by the subclass if needed.
62
+ Must return a dictionary with keys as metric names and values as the data to be used for those metrics.
63
+ NOTE: that the data (for each metric) must be in the format expected by the torchmetrics instance (for that metric). E.g: {"accuracy": {"preds": [...], "target": [...]}, ...} since torchmetrics expects the data in a specific format.
96
64
  """
97
65
  pass
98
66
 
@@ -102,63 +70,55 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
102
70
  """
103
71
  return outdict, []
104
72
 
73
+ def __valid_calc_custom_fields(self, fun_results):
74
+ if not isinstance(fun_results, tuple) or len(fun_results) != 2:
75
+ raise ValueError("Function must return a tuple (outdict, custom_fields)")
76
+ outdict, custom_fields = fun_results
77
+ if not isinstance(outdict, dict):
78
+ raise TypeError("Output must be a dictionary")
79
+ if not isinstance(custom_fields, list):
80
+ raise TypeError("Custom fields must be a list")
81
+ for field in custom_fields:
82
+ if not isinstance(field, str):
83
+ raise TypeError(f"Custom field '{field}' is not a string")
84
+ return outdict, custom_fields
85
+
86
+ # ! only need to override this method if torchmetrics are not used
87
+ def calc_exp_perf_metrics(self, metric_names, *args, **kwargs):
88
+ metrics_backend = self.get_metric_backend()
89
+ out_dict = {"dataset": self.get_dataset_name(), "experiment": self.get_experiment_name()}
90
+ out_dict, custom_fields = self.__valid_calc_custom_fields(self.calc_exp_outdict_custom_fields(
91
+ outdict=out_dict, *args, **kwargs
92
+ ))
93
+ metrics_data_dict = self.prepare_metrics_data_dict(
94
+ metric_names, *args, **kwargs
95
+ )
96
+ metric_results = metrics_backend.calc_metrics(
97
+ metrics_data_dict=metrics_data_dict, *args, **kwargs
98
+ )
99
+ metric_results_prefix = {
100
+ f"metric_{k}": v for k, v in metric_results.items()
101
+ }
102
+ out_dict.update(metric_results_prefix)
103
+ ordered_cols = REQUIRED_COLS + custom_fields + list(metric_results_prefix.keys())
104
+ out_dict = OrderedDict(
105
+ (col, out_dict[col]) for col in ordered_cols if col in out_dict
106
+ )
107
+ return out_dict
108
+
105
109
  #! custom kwargs:
106
110
  #! outfile - if provided, will save the output to a CSV file with the given path
107
111
  #! outdir - if provided, will save the output to a CSV file in the given directory with a generated filename
108
112
  #! return_df - if True, will return a DataFrame instead of a dictionary
109
113
 
110
- def calculate_exp_perf_metrics(self, *args, **kwargs):
114
+ def calc_save_exp_perfs(self, *args, **kwargs):
111
115
  """
112
116
  Calculate the metrics.
113
117
  This function should be overridden by the subclass if needed.
114
118
  Must return a dictionary with keys as metric names and values as the calculated metrics.
115
119
  """
116
- metric_names = list(self.get_exp_torch_metrics().keys())
117
- out_dict = {metric: None for metric in metric_names}
118
- out_dict['dataset'] = self.get_dataset_name()
119
- out_dict['experiment'] = self.get_experiment_name()
120
- out_dict, custom_fields = self.calc_exp_outdict_custom_fields(
121
- outdict=out_dict, *args, **kwargs
122
- )
123
- torch_metrics_dict = self.get_exp_torch_metrics()
124
- all_metric_data = self.prepare_exp_data_for_metrics(
125
- metric_names, *args, **kwargs
126
- )
127
- metric_col_names = []
128
- for metric in metric_names:
129
- if metric not in all_metric_data:
130
- raise ValueError(f"Metric '{metric}' not found in provided data.")
131
- tmetric = torch_metrics_dict[metric] # torchmetrics instance
132
- metric_data = all_metric_data[metric] # should be a dict of args/kwargs
133
- # Inspect expected parameters for the metric's update() method
134
- sig = inspect.signature(tmetric.update)
135
- expected_args = list(sig.parameters.values())
136
- # Prepare args in correct order
137
- if isinstance(metric_data, dict):
138
- # Match dict keys to parameter names
139
- args = [metric_data[param.name] for param in expected_args]
140
- elif isinstance(metric_data, (list, tuple)):
141
- args = metric_data
142
- else:
143
- raise TypeError(f"Unsupported data format for metric '{metric}'")
144
-
145
- # Call update and compute
146
- if len(expected_args) == 1:
147
- tmetric.update(args) # pass as single argument
148
- else:
149
- tmetric.update(*args) # unpack multiple arguments
150
- computed_value = tmetric.compute()
151
- # ensure the computed value converted to a scala value or list array
152
- if isinstance(computed_value, torch.Tensor):
153
- if computed_value.numel() == 1:
154
- computed_value = computed_value.item()
155
- else:
156
- computed_value = computed_value.tolist()
157
- col_name = f"metric_{metric}" if 'metric_' not in metric else metric
158
- metric_col_names.append(col_name)
159
- out_dict[col_name] = computed_value
160
-
161
- # check if any kwargs named "outfile"
120
+ metric_names = self.get_metric_backend().metric_names
121
+ out_dict = self.calc_exp_perf_metrics(metric_names=metric_names, *args, **kwargs)
162
122
  csv_outfile = kwargs.get("outfile", None)
163
123
  if csv_outfile is not None:
164
124
  filePathNoExt, _ = os.path.splitext(csv_outfile)
@@ -171,7 +131,8 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
171
131
 
172
132
  # convert out_dict to a DataFrame
173
133
  df = pd.DataFrame([out_dict])
174
- ordered_cols = REQUIRED_COLS + custom_fields + metric_col_names
134
+ # get the orders of the columns as the orders or the keys in out_dict
135
+ ordered_cols = list(out_dict.keys())
175
136
  df = df[ordered_cols] # reorder columns
176
137
 
177
138
  if csv_outfile:
@@ -182,9 +143,17 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
182
143
  else:
183
144
  return out_dict, csv_outfile
184
145
 
146
+ @staticmethod
147
+ def default_exp_csv_filter_fn(exp_file_name: str) -> bool:
148
+ """
149
+ Default filter function for experiments.
150
+ Returns True if the experiment name does not start with "test_" or "debug_".
151
+ """
152
+ return "__perf.csv" in exp_file_name
153
+
185
154
  @classmethod
186
155
  def gen_perf_report_for_multip_exps(
187
- cls, indir: str, exp_perf_csv_pattern="__perf", csv_sep=';'
156
+ cls, indir: str, exp_csv_filter_fn=default_exp_csv_filter_fn, csv_sep=";"
188
157
  ) -> PerfTB:
189
158
  """
190
159
  Generate a performance report by scanning experiment subdirectories.
@@ -221,6 +190,9 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
221
190
  assert len(metric_cols) > 0, "No metric columns found in the DataFrame. Ensure that the CSV files contain metric columns starting with 'metric_'."
222
191
  final_cols = REQUIRED_COLS + metric_cols
223
192
  df = df[final_cols]
193
+ # !hahv debug
194
+ pprint('------ Final DataFrame Columns ------')
195
+ csvfile.fn_display_df(df)
224
196
  # ! validate all rows in df before returning
225
197
  # make sure all rows will have at least values for REQUIRED_COLS and at least one metric column
226
198
  for index, row in df.iterrows():
@@ -289,12 +261,12 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
289
261
  ]
290
262
  if len(exp_dirs) == 0:
291
263
  csv_perf_files = glob.glob(
292
- os.path.join(indir, f"*{exp_perf_csv_pattern}*.csv")
264
+ os.path.join(indir, f"*.csv")
293
265
  )
294
266
  csv_perf_files = [
295
267
  file_item
296
268
  for file_item in csv_perf_files
297
- if exp_perf_csv_pattern in file_item
269
+ if exp_csv_filter_fn(file_item)
298
270
  ]
299
271
  else:
300
272
  # multiple experiment directories found
@@ -302,33 +274,22 @@ class PerfCalc(ABC): # Abstract base class for performance calculation
302
274
  for exp_dir in exp_dirs:
303
275
  # pprint(f"Searching in experiment directory: {exp_dir}")
304
276
  matched = glob.glob(
305
- os.path.join(exp_dir, f"*{exp_perf_csv_pattern}*.csv")
277
+ os.path.join(exp_dir, f"*.csv")
306
278
  )
279
+ matched = [
280
+ file_item
281
+ for file_item in matched
282
+ if exp_csv_filter_fn(file_item)
283
+ ]
307
284
  csv_perf_files.extend(matched)
308
285
 
309
286
  assert (
310
287
  len(csv_perf_files) > 0
311
- ), f"No CSV files matching pattern '{exp_perf_csv_pattern}' found in the experiment directories."
288
+ ), f"No CSV files matching pattern '{exp_csv_filter_fn}' found in the experiment directories."
312
289
 
313
- assert len(csv_perf_files) > 0, f"No CSV files matching pattern '{exp_perf_csv_pattern}' found in the experiment directories."
290
+ assert len(csv_perf_files) > 0, f"No CSV files matching pattern '{exp_csv_filter_fn}' found in the experiment directories."
314
291
 
315
292
  all_exp_perf_df = get_df_for_all_exp_perf(csv_perf_files, csv_sep=csv_sep)
316
293
  csvfile.fn_display_df(all_exp_perf_df)
317
294
  perf_tb = mk_perftb_report(all_exp_perf_df)
318
295
  return perf_tb
319
-
320
-
321
- def main():
322
- indir = "./zreport/test"
323
- report_outfile = "./zreport/all.csv"
324
- exp_perf_csv_pattern = "__perf"
325
- csv_sep = ";"
326
- perftb = PerfCalc.gen_perf_report_for_multip_exps(
327
- indir, exp_perf_csv_pattern, csv_sep
328
- )
329
- perftb.to_csv(report_outfile, sep=csv_sep)
330
- inspect(perftb)
331
- perftb.plot(save_path="./zreport/test_csv.svg", open_plot=True)
332
-
333
- if __name__ == "__main__":
334
- main()
halib/utils/video.py CHANGED
@@ -5,15 +5,17 @@ from ..system import filesys as fs
5
5
 
6
6
 
7
7
  class VideoUtils:
8
+
8
9
  @staticmethod
9
- def get_video_meta_dict(video_path):
10
+ def _default_meta_extractor(video_path):
11
+ """Default video metadata extractor function."""
10
12
  # Open the video file
11
13
  cap = cv2.VideoCapture(video_path)
12
14
 
13
15
  # Check if the video was opened successfully
14
16
  if not cap.isOpened():
15
17
  print(f"Error: Could not open video file {video_path}")
16
- return None, None
18
+ return None
17
19
 
18
20
  # Get the frame count
19
21
  frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
@@ -23,6 +25,7 @@ class VideoUtils:
23
25
 
24
26
  # Release the video capture object
25
27
  cap.release()
28
+
26
29
  meta_dict = {
27
30
  "video_path": video_path,
28
31
  "frame_count": frame_count,
@@ -31,6 +34,17 @@ class VideoUtils:
31
34
  return meta_dict
32
35
 
33
36
  @staticmethod
37
+ def get_video_meta_dict(video_path, meta_dict_extractor_func=None):
38
+ assert os.path.exists(video_path), f"Video file {video_path} does not exist"
39
+ if meta_dict_extractor_func and callable(meta_dict_extractor_func):
40
+ assert meta_dict_extractor_func.__code__.co_argcount == 1, "meta_dict_extractor_func must take exactly one argument (video_path)"
41
+ meta_dict = meta_dict_extractor_func(video_path)
42
+ assert isinstance(meta_dict, dict), "meta_dict_extractor_func must return a dictionary"
43
+ assert 'video_path' in meta_dict, "meta_dict must contain 'video_path'"
44
+ else:
45
+ meta_dict = VideoUtils._default_meta_extractor(video_path=video_path)
46
+ return meta_dict
47
+ @staticmethod
34
48
  def get_video_dir_meta_df(video_dir, video_exts=['.mp4', '.avi', '.mov', '.mkv'], search_recursive=False, csv_outfile=None):
35
49
  assert os.path.exists(video_dir), f"Video directory {video_dir} does not exist"
36
50
  video_files = fs.filter_files_by_extension(video_dir, video_exts, recursive=search_recursive)
@@ -1,180 +1,190 @@
1
- Metadata-Version: 2.1
2
- Name: halib
3
- Version: 0.1.61
4
- Summary: Small library for common tasks
5
- Author: Hoang Van Ha
6
- Author-email: hoangvanhauit@gmail.com
7
- License: UNKNOWN
8
- Platform: UNKNOWN
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.8
13
- Description-Content-Type: text/markdown
14
- License-File: LICENSE.txt
15
- Requires-Dist: arrow
16
- Requires-Dist: click
17
- Requires-Dist: enlighten
18
- Requires-Dist: kaleido (==0.1.*)
19
- Requires-Dist: loguru
20
- Requires-Dist: more-itertools
21
- Requires-Dist: moviepy
22
- Requires-Dist: networkx
23
- Requires-Dist: numpy
24
- Requires-Dist: omegaconf
25
- Requires-Dist: opencv-python
26
- Requires-Dist: pandas
27
- Requires-Dist: Pillow
28
- Requires-Dist: Pyarrow
29
- Requires-Dist: pycurl
30
- Requires-Dist: python-telegram-bot
31
- Requires-Dist: requests
32
- Requires-Dist: rich
33
- Requires-Dist: scikit-learn
34
- Requires-Dist: matplotlib
35
- Requires-Dist: seaborn
36
- Requires-Dist: plotly
37
- Requires-Dist: pygwalker
38
- Requires-Dist: tabulate
39
- Requires-Dist: itables
40
- Requires-Dist: timebudget
41
- Requires-Dist: tqdm
42
- Requires-Dist: tube-dl
43
- Requires-Dist: wandb
44
- Requires-Dist: dataclass-wizard
45
-
46
- Helper package for coding and automation
47
-
48
- **Version 0.1.61**
49
-
50
- + add `util/video`: add `VideoUtils` class to handle common video-related tasks
51
- + add `util/gpu_mon`: add `GPUMonitor` class to monitor GPU usage and performance
52
-
53
- **Version 0.1.59**
54
-
55
- + add `util/perfcalc`: abstract class for performance calculation. This class need to be inherited and implemented with specific performance calculation logic.
56
-
57
- **Version 0.1.55**
58
-
59
- + add `util/dataclass_util` to help dynamically create `dataclass` classes from dictionary or YAML file, including support for nested dataclasses. From there, we can use `dataclass_wizard` to create a list of `dataclass` classes with the help from ChatGPT.
60
-
61
- **Version 0.1.52**
62
-
63
- + add `research/perftb` module to allow creating and managing performance tables for experiments, including filtering by datasets, metrics, and experiments.
64
-
65
- **Version 0.1.50**
66
-
67
- + add `pprint_local_path` to print local path (file/directory) in clickable link (as file URI)
68
-
69
- + add `research` package to help with research tasks, including `benchquery` for benchmarking queries from dataframe
70
- + add `wandb` module to allow easy sync offline data to Weights & Biases (wandb) and batch clear wandb runs.
71
-
72
- **Version 0.1.47**
73
- + add `pprint_box` to print object/string in a box frame (like in `inspect`)
74
-
75
- **Version 0.1.46**
76
- + filter the warning message of `UserWarning: Unable to import Axes3D.`
77
- + auto_wrap_text for `fn_display_df` to avoid long text in the table
78
-
79
- **Version 0.1.42**
80
- + add <rich_color.py>: add basic color list (for easy usage)
81
-
82
- **Version 0.1.41**
83
- + add <rich_color.py> to display rich color information in <rich> python package (rcolor_str, rcolor_pallet_all, etc.)
84
-
85
- **Version 0.1.40**
86
-
87
- + update <csvfile.py> to use `itables` and `pygwalker` to display dataframe in jupyter notebook.
88
-
89
- **Version 0.1.38**
90
-
91
- + add <torchloader.py> to search for best cfg for torch dataloader (num_workers, batch_size, pin_memory, et.)
92
-
93
- **Version 0.1.37**
94
-
95
- + add <dataset.py> to help split classification dataset into train/val(test)
96
- ---
97
- **Version 0.1.33**
98
-
99
- + add `plot.py` module to plot DL model training history (with columlns: epoch, train_accuracy, val_accuracy, train_loss, val_loss) using `seaborn` and `matplotlib`
100
- ---
101
- **Version 0.1.29**
102
-
103
- + for `tele_noti` module, `kaleido==0.1.*` is required for plotly since `kaleido 0.2.*` is not working (taking for ever to generate image)
104
- ---
105
- **Version 0.1.24**
106
-
107
- + rename `sys` to `system` to avoid conflict with built-in `sys` module
108
- + add `tele_noti` module to send notification to telegram after a specific interval for training progress monitoring
109
- ---
110
- **Version 0.1.22**
111
-
112
- + add `cuda.py` module to check CUDA availability (for both pytorch and tensorflow)
113
- ---
114
- **Version 0.1.21**
115
-
116
- + using `networkx` and `omegaconf` to allow yaml file inheritance and override
117
- ---
118
- **Version 0.1.15**
119
-
120
- + `__init__.py`: add common logging library; also `console_log` decorator to log function (start and end)
121
-
122
- ---
123
-
124
- **Version 0.1.10**
125
-
126
- + filesys: fix typo on "is_exit" to "is_exist"
127
- + gdrive: now support uploading file to folder and return direct link (shareable link)
128
-
129
- **Version 0.1.9**
130
-
131
- + add dependencies requirement.txt
132
-
133
- **Version 0.1.8**
134
-
135
- Fix bugs:
136
-
137
- + [performance] instead of inserting directly new rows into table dataframe, first insert it into in-memory `row_pool_dict`, that fill data in that dict into the actual dataframe when needed.
138
-
139
- ---
140
-
141
- **Version 0.1.7**
142
-
143
- Fix bugs:
144
-
145
- + fix insert into table so slow by allowing insert multiple rows at once
146
-
147
- ---
148
-
149
- **Version 0.1.6**
150
-
151
- New features:
152
-
153
- + add DFCreator for manipulating table (DataFrame) - create, insert row, display, write to file
154
-
155
- ---
156
-
157
- **Version 0.1.5**
158
-
159
- New Features
160
-
161
- + add cmd module
162
- + new package structure
163
-
164
- ---
165
-
166
- **Version 0.1.4**
167
-
168
- New Features
169
-
170
- + add support to create Bitbucket Project from template
171
-
172
- ---
173
-
174
- **Version 0.1.2**
175
-
176
- New Features
177
-
178
- + add support to upload local to google drive.
179
-
180
-
1
+ Metadata-Version: 2.4
2
+ Name: halib
3
+ Version: 0.1.66
4
+ Summary: Small library for common tasks
5
+ Author: Hoang Van Ha
6
+ Author-email: hoangvanhauit@gmail.com
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE.txt
13
+ Requires-Dist: arrow
14
+ Requires-Dist: click
15
+ Requires-Dist: enlighten
16
+ Requires-Dist: kaleido==0.1.*
17
+ Requires-Dist: loguru
18
+ Requires-Dist: more-itertools
19
+ Requires-Dist: moviepy
20
+ Requires-Dist: networkx
21
+ Requires-Dist: numpy
22
+ Requires-Dist: omegaconf
23
+ Requires-Dist: opencv-python
24
+ Requires-Dist: pandas
25
+ Requires-Dist: Pillow
26
+ Requires-Dist: Pyarrow
27
+ Requires-Dist: pycurl
28
+ Requires-Dist: python-telegram-bot
29
+ Requires-Dist: requests
30
+ Requires-Dist: rich
31
+ Requires-Dist: scikit-learn
32
+ Requires-Dist: matplotlib
33
+ Requires-Dist: seaborn
34
+ Requires-Dist: plotly
35
+ Requires-Dist: pygwalker
36
+ Requires-Dist: tabulate
37
+ Requires-Dist: itables
38
+ Requires-Dist: timebudget
39
+ Requires-Dist: tqdm
40
+ Requires-Dist: tube_dl
41
+ Requires-Dist: wandb
42
+ Requires-Dist: dataclass-wizard
43
+ Dynamic: author
44
+ Dynamic: author-email
45
+ Dynamic: classifier
46
+ Dynamic: description
47
+ Dynamic: description-content-type
48
+ Dynamic: license-file
49
+ Dynamic: requires-dist
50
+ Dynamic: requires-python
51
+ Dynamic: summary
52
+
53
+ Helper package for coding and automation
54
+
55
+ **Version 0.1.66**
56
+
57
+ + now use `uv` for venv management
58
+ + `research/perfcalc`: support both torchmetrics and custom metrics for performance calculation
59
+
60
+ **Version 0.1.61**
61
+
62
+ + add `util/video`: add `VideoUtils` class to handle common video-related tasks
63
+ + add `util/gpu_mon`: add `GPUMonitor` class to monitor GPU usage and performance
64
+
65
+ **Version 0.1.59**
66
+
67
+ + add `util/perfcalc`: abstract class for performance calculation. This class need to be inherited and implemented with specific performance calculation logic.
68
+
69
+ **Version 0.1.55**
70
+
71
+ + add `util/dataclass_util` to help dynamically create `dataclass` classes from dictionary or YAML file, including support for nested dataclasses. From there, we can use `dataclass_wizard` to create a list of `dataclass` classes with the help from ChatGPT.
72
+
73
+ **Version 0.1.52**
74
+
75
+ + add `research/perftb` module to allow creating and managing performance tables for experiments, including filtering by datasets, metrics, and experiments.
76
+
77
+ **Version 0.1.50**
78
+
79
+ + add `pprint_local_path` to print local path (file/directory) in clickable link (as file URI)
80
+
81
+ + add `research` package to help with research tasks, including `benchquery` for benchmarking queries from dataframe
82
+ + add `wandb` module to allow easy sync offline data to Weights & Biases (wandb) and batch clear wandb runs.
83
+
84
+ **Version 0.1.47**
85
+ + add `pprint_box` to print object/string in a box frame (like in `inspect`)
86
+
87
+ **Version 0.1.46**
88
+ + filter the warning message of `UserWarning: Unable to import Axes3D.`
89
+ + auto_wrap_text for `fn_display_df` to avoid long text in the table
90
+
91
+ **Version 0.1.42**
92
+ + add <rich_color.py>: add basic color list (for easy usage)
93
+
94
+ **Version 0.1.41**
95
+ + add <rich_color.py> to display rich color information in <rich> python package (rcolor_str, rcolor_pallet_all, etc.)
96
+
97
+ **Version 0.1.40**
98
+
99
+ + update <csvfile.py> to use `itables` and `pygwalker` to display dataframe in jupyter notebook.
100
+
101
+ **Version 0.1.38**
102
+
103
+ + add <torchloader.py> to search for best cfg for torch dataloader (num_workers, batch_size, pin_memory, et.)
104
+
105
+ **Version 0.1.37**
106
+
107
+ + add <dataset.py> to help split classification dataset into train/val(test)
108
+ ---
109
+ **Version 0.1.33**
110
+
111
+ + add `plot.py` module to plot DL model training history (with columlns: epoch, train_accuracy, val_accuracy, train_loss, val_loss) using `seaborn` and `matplotlib`
112
+ ---
113
+ **Version 0.1.29**
114
+
115
+ + for `tele_noti` module, `kaleido==0.1.*` is required for plotly since `kaleido 0.2.*` is not working (taking for ever to generate image)
116
+ ---
117
+ **Version 0.1.24**
118
+
119
+ + rename `sys` to `system` to avoid conflict with built-in `sys` module
120
+ + add `tele_noti` module to send notification to telegram after a specific interval for training progress monitoring
121
+ ---
122
+ **Version 0.1.22**
123
+
124
+ + add `cuda.py` module to check CUDA availability (for both pytorch and tensorflow)
125
+ ---
126
+ **Version 0.1.21**
127
+
128
+ + using `networkx` and `omegaconf` to allow yaml file inheritance and override
129
+ ---
130
+ **Version 0.1.15**
131
+
132
+ + `__init__.py`: add common logging library; also `console_log` decorator to log function (start and end)
133
+
134
+ ---
135
+
136
+ **Version 0.1.10**
137
+
138
+ + filesys: fix typo on "is_exit" to "is_exist"
139
+ + gdrive: now support uploading file to folder and return direct link (shareable link)
140
+
141
+ **Version 0.1.9**
142
+
143
+ + add dependencies requirement.txt
144
+
145
+ **Version 0.1.8**
146
+
147
+ Fix bugs:
148
+
149
+ + [performance] instead of inserting directly new rows into table dataframe, first insert it into in-memory `row_pool_dict`, that fill data in that dict into the actual dataframe when needed.
150
+
151
+ ---
152
+
153
+ **Version 0.1.7**
154
+
155
+ Fix bugs:
156
+
157
+ + fix insert into table so slow by allowing insert multiple rows at once
158
+
159
+ ---
160
+
161
+ **Version 0.1.6**
162
+
163
+ New features:
164
+
165
+ + add DFCreator for manipulating table (DataFrame) - create, insert row, display, write to file
166
+
167
+ ---
168
+
169
+ **Version 0.1.5**
170
+
171
+ New Features
172
+
173
+ + add cmd module
174
+ + new package structure
175
+
176
+ ---
177
+
178
+ **Version 0.1.4**
179
+
180
+ New Features
181
+
182
+ + add support to create Bitbucket Project from template
183
+
184
+ ---
185
+
186
+ **Version 0.1.2**
187
+
188
+ New Features
189
+
190
+ + add support to upload local to google drive.
@@ -30,7 +30,8 @@ halib/online/projectmake.py,sha256=Zrs96WgXvO4nIrwxnCOletL4aTBge-EoF0r7hpKO1w8,4
30
30
  halib/research/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  halib/research/benchquery.py,sha256=FuKnbWQtCEoRRtJAfN-zaN-jPiO_EzsakmTOMiqi7GQ,4626
32
32
  halib/research/dataset.py,sha256=QU0Hr5QFb8_XlvnOMgC9QJGIpwXAZ9lDd0RdQi_QRec,6743
33
- halib/research/perfcalc.py,sha256=qKgoDbXQtyOpRgOjSMUus7r4mgyiH8Gu9o59sd48SAE,15147
33
+ halib/research/metrics.py,sha256=hvXaAtgzdqwDxcBQdEJgorWQgf9gLk2LK4oKD_8FiM4,4792
34
+ halib/research/perfcalc.py,sha256=9FoUuYJRXyoAakfTM1T2q039Wiw88fdw34eV2inktDM,13803
34
35
  halib/research/perftb.py,sha256=vazU-dYBJhfc4sK4zFgxOvzeXGi-5TyPHCt20ItiWhY,30463
35
36
  halib/research/plot.py,sha256=-pDUk4z3C_GnyJ5zWmf-mGMdT4gaipVJWzIgcpIPiRk,9448
36
37
  halib/research/torchloader.py,sha256=yqUjcSiME6H5W210363HyRUrOi3ISpUFAFkTr1w4DCw,6503
@@ -47,9 +48,9 @@ halib/utils/dict_op.py,sha256=wYE6Iw-_CnCWdMg9tpJ2Y2-e2ESkW9FxmdBkZkbUh80,299
47
48
  halib/utils/gpu_mon.py,sha256=vD41_ZnmPLKguuq9X44SB_vwd9JrblO4BDzHLXZhhFY,2233
48
49
  halib/utils/listop.py,sha256=Vpa8_2fI0wySpB2-8sfTBkyi_A4FhoFVVvFiuvW8N64,339
49
50
  halib/utils/tele_noti.py,sha256=-4WXZelCA4W9BroapkRyIdUu9cUVrcJJhegnMs_WpGU,5928
50
- halib/utils/video.py,sha256=mJQFHFbijmu3lo0NZk4Ed64Tfa1u5KxNJpd_TuckpPU,2228
51
- halib-0.1.61.dist-info/LICENSE.txt,sha256=qZssdna4aETiR8znYsShUjidu-U4jUT9Q-EWNlZ9yBQ,1100
52
- halib-0.1.61.dist-info/METADATA,sha256=1s7ikn1n88Ix9c_rB4OqSY9hmQ12sJMM-LE_4aIDWNM,5039
53
- halib-0.1.61.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
54
- halib-0.1.61.dist-info/top_level.txt,sha256=7AD6PLaQTreE0Fn44mdZsoHBe_Zdd7GUmjsWPyQ7I-k,6
55
- halib-0.1.61.dist-info/RECORD,,
51
+ halib/utils/video.py,sha256=ZqzNVPgc1RZr_T0OlHvZ6SzyBpL7O27LtB86JMbBuR0,3059
52
+ halib-0.1.66.dist-info/licenses/LICENSE.txt,sha256=qZssdna4aETiR8znYsShUjidu-U4jUT9Q-EWNlZ9yBQ,1100
53
+ halib-0.1.66.dist-info/METADATA,sha256=mtxt5CPm_a3fHh6K0uLV9QY1p4nSOBtbgcnQaYyBTXY,5541
54
+ halib-0.1.66.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
+ halib-0.1.66.dist-info/top_level.txt,sha256=7AD6PLaQTreE0Fn44mdZsoHBe_Zdd7GUmjsWPyQ7I-k,6
56
+ halib-0.1.66.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.37.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5