PyDPEET 0.2.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.
Files changed (122) hide show
  1. pydpeet/__init__.py +56 -0
  2. pydpeet/citations/__init__.py +7 -0
  3. pydpeet/citations/citeme.py +397 -0
  4. pydpeet/citations/html_writer.py +137 -0
  5. pydpeet/dev_utils/__init__.py +7 -0
  6. pydpeet/dev_utils/generate_inits/__init__.py +7 -0
  7. pydpeet/dev_utils/generate_inits/generate_inits.py +327 -0
  8. pydpeet/io/__init__.py +7 -0
  9. pydpeet/io/configs/__init__.py +7 -0
  10. pydpeet/io/configs/config.py +227 -0
  11. pydpeet/io/configs/const.py +1 -0
  12. pydpeet/io/convert.py +594 -0
  13. pydpeet/io/device/__init__.py +7 -0
  14. pydpeet/io/device/arbin_4_23_PV090331/__init__.py +7 -0
  15. pydpeet/io/device/arbin_4_23_PV090331/formatter.py +22 -0
  16. pydpeet/io/device/arbin_4_23_PV090331/mapper.py +18 -0
  17. pydpeet/io/device/arbin_4_23_PV090331/reader.py +68 -0
  18. pydpeet/io/device/arbin_8_00_PV221201/__init__.py +7 -0
  19. pydpeet/io/device/arbin_8_00_PV221201/formatter.py +26 -0
  20. pydpeet/io/device/arbin_8_00_PV221201/mapper.py +18 -0
  21. pydpeet/io/device/arbin_8_00_PV221201/reader.py +66 -0
  22. pydpeet/io/device/basytec_6_3_1_0/__init__.py +7 -0
  23. pydpeet/io/device/basytec_6_3_1_0/formatter.py +18 -0
  24. pydpeet/io/device/basytec_6_3_1_0/mapper.py +18 -0
  25. pydpeet/io/device/basytec_6_3_1_0/reader.py +54 -0
  26. pydpeet/io/device/digatron_4_20_6_236/__init__.py +7 -0
  27. pydpeet/io/device/digatron_4_20_6_236/formatter.py +27 -0
  28. pydpeet/io/device/digatron_4_20_6_236/mapper.py +18 -0
  29. pydpeet/io/device/digatron_4_20_6_236/reader.py +45 -0
  30. pydpeet/io/device/digatron_eis_4_20_6_236/__init__.py +7 -0
  31. pydpeet/io/device/digatron_eis_4_20_6_236/formatter.py +39 -0
  32. pydpeet/io/device/digatron_eis_4_20_6_236/mapper.py +18 -0
  33. pydpeet/io/device/digatron_eis_4_20_6_236/reader.py +42 -0
  34. pydpeet/io/device/neware_8_0_0_516/__init__.py +7 -0
  35. pydpeet/io/device/neware_8_0_0_516/formatter.py +31 -0
  36. pydpeet/io/device/neware_8_0_0_516/mapper.py +18 -0
  37. pydpeet/io/device/neware_8_0_0_516/reader.py +397 -0
  38. pydpeet/io/device/parstat_2_63_3/__init__.py +7 -0
  39. pydpeet/io/device/parstat_2_63_3/formatter.py +34 -0
  40. pydpeet/io/device/parstat_2_63_3/mapper.py +18 -0
  41. pydpeet/io/device/parstat_2_63_3/reader.py +34 -0
  42. pydpeet/io/device/safion_1_9/__init__.py +7 -0
  43. pydpeet/io/device/safion_1_9/formatter.py +25 -0
  44. pydpeet/io/device/safion_1_9/mapper.py +18 -0
  45. pydpeet/io/device/safion_1_9/reader.py +89 -0
  46. pydpeet/io/device/zahner/__init__.py +7 -0
  47. pydpeet/io/device/zahner/formatter.py +73 -0
  48. pydpeet/io/device/zahner/mapper.py +34 -0
  49. pydpeet/io/device/zahner/reader.py +46 -0
  50. pydpeet/io/device/zahner_new/__init__.py +7 -0
  51. pydpeet/io/device/zahner_new/formatter.py +72 -0
  52. pydpeet/io/device/zahner_new/mapper.py +50 -0
  53. pydpeet/io/device/zahner_new/reader.py +46 -0
  54. pydpeet/io/map.py +69 -0
  55. pydpeet/io/read.py +50 -0
  56. pydpeet/io/utils/__init__.py +7 -0
  57. pydpeet/io/utils/ext_path.py +34 -0
  58. pydpeet/io/utils/formatter_utils.py +429 -0
  59. pydpeet/io/utils/load_custom_module.py +33 -0
  60. pydpeet/io/utils/timing.py +36 -0
  61. pydpeet/io/write.py +99 -0
  62. pydpeet/process/__init__.py +7 -0
  63. pydpeet/process/analyze/__init__.py +7 -0
  64. pydpeet/process/analyze/average.py +297 -0
  65. pydpeet/process/analyze/capacity.py +208 -0
  66. pydpeet/process/analyze/configs/__init__.py +7 -0
  67. pydpeet/process/analyze/configs/battery_config.py +38 -0
  68. pydpeet/process/analyze/configs/ocv_config.py +177 -0
  69. pydpeet/process/analyze/configs/step_analyzer_config.py +178 -0
  70. pydpeet/process/analyze/cycle.py +63 -0
  71. pydpeet/process/analyze/efficiency.py +182 -0
  72. pydpeet/process/analyze/energy.py +51 -0
  73. pydpeet/process/analyze/extract/__init__.py +7 -0
  74. pydpeet/process/analyze/extract/dva_ica.py +224 -0
  75. pydpeet/process/analyze/extract/ocv.py +196 -0
  76. pydpeet/process/analyze/power.py +32 -0
  77. pydpeet/process/analyze/resistance.py +87 -0
  78. pydpeet/process/analyze/soc.py +418 -0
  79. pydpeet/process/analyze/soh.py +72 -0
  80. pydpeet/process/analyze/utils.py +122 -0
  81. pydpeet/process/merge/__init__.py +7 -0
  82. pydpeet/process/merge/series.py +274 -0
  83. pydpeet/process/sequence/__init__.py +7 -0
  84. pydpeet/process/sequence/configs/__init__.py +7 -0
  85. pydpeet/process/sequence/configs/config.py +17 -0
  86. pydpeet/process/sequence/step_analyzer.py +440 -0
  87. pydpeet/process/sequence/utils/__init__.py +7 -0
  88. pydpeet/process/sequence/utils/annotate/__init__.py +7 -0
  89. pydpeet/process/sequence/utils/annotate/annotate_primitives.py +419 -0
  90. pydpeet/process/sequence/utils/configs/CONFIG_Fallback.py +231 -0
  91. pydpeet/process/sequence/utils/configs/CONFIG_preprocessing.py +243 -0
  92. pydpeet/process/sequence/utils/configs/__init__.py +7 -0
  93. pydpeet/process/sequence/utils/console_prints/__init__.py +7 -0
  94. pydpeet/process/sequence/utils/console_prints/log_time.py +30 -0
  95. pydpeet/process/sequence/utils/postprocessing/__init__.py +7 -0
  96. pydpeet/process/sequence/utils/postprocessing/df_primitives_correction.py +166 -0
  97. pydpeet/process/sequence/utils/postprocessing/filter_df.py +168 -0
  98. pydpeet/process/sequence/utils/postprocessing/generate_instructions.py +297 -0
  99. pydpeet/process/sequence/utils/preprocessing/__init__.py +7 -0
  100. pydpeet/process/sequence/utils/preprocessing/calculate_thresholds.py +27 -0
  101. pydpeet/process/sequence/utils/processing/__init__.py +7 -0
  102. pydpeet/process/sequence/utils/processing/analyze_segments.py +368 -0
  103. pydpeet/process/sequence/utils/processing/attempt_to_merge_neighboring_segments.py +122 -0
  104. pydpeet/process/sequence/utils/processing/check_CV_results.py +226 -0
  105. pydpeet/process/sequence/utils/processing/check_power_zero_watt_segments.py +85 -0
  106. pydpeet/process/sequence/utils/processing/check_zero_length.py +117 -0
  107. pydpeet/process/sequence/utils/processing/split_in_segments.py +97 -0
  108. pydpeet/process/sequence/utils/processing/supress_smaller_segments.py +99 -0
  109. pydpeet/process/sequence/utils/processing/widen_constant_segments.py +185 -0
  110. pydpeet/process/sequence/utils/visualize/__init__.py +7 -0
  111. pydpeet/process/sequence/utils/visualize/visualize_data.py +288 -0
  112. pydpeet/res/__init__.py +7 -0
  113. pydpeet/settings.py +0 -0
  114. pydpeet/utils/__init__.py +7 -0
  115. pydpeet/utils/logging_style.py +28 -0
  116. pydpeet/version.py +0 -0
  117. pydpeet-0.2.0.dist-info/METADATA +85 -0
  118. pydpeet-0.2.0.dist-info/RECORD +122 -0
  119. pydpeet-0.2.0.dist-info/WHEEL +5 -0
  120. pydpeet-0.2.0.dist-info/licenses/AUTHORS.md +0 -0
  121. pydpeet-0.2.0.dist-info/licenses/LICENCE.md +27 -0
  122. pydpeet-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,429 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+
7
+ def typecast(
8
+ df: pd.DataFrame,
9
+ column_name: str,
10
+ datatype,
11
+ ) -> pd.DataFrame:
12
+ """
13
+ Try to typecast a column in a DataFrame to a given type.
14
+
15
+ Parameters
16
+ ----------
17
+ data_frame : pandas.DataFrame
18
+ DataFrame to be modified
19
+ column_name : str
20
+ Name of the column to be typecast
21
+ datatype : type
22
+ datatype to which the column should be typecast
23
+
24
+ Returns
25
+ -------
26
+ pandas.DataFrame
27
+ Modified DataFrame
28
+ """
29
+ try:
30
+ if df is None:
31
+ raise ValueError(f"{df} is None")
32
+ if column_name is None:
33
+ raise ValueError(f"{column_name} is None")
34
+ if datatype is None:
35
+ raise ValueError(f"{datatype} is None")
36
+ if type(column_name) is not str:
37
+ raise ValueError(f"{column_name} is not a string")
38
+ if column_name not in df.columns:
39
+ raise ValueError(f"{column_name} is not in {df.columns}")
40
+ df[column_name] = df[column_name].astype(datatype)
41
+ except Exception:
42
+ logging.warning(f"Error converting column:{column_name} to {datatype.__name__}")
43
+
44
+ return df
45
+
46
+
47
+ def testtime_hours_to_seconds_with_string_interpretation(
48
+ df: pd.DataFrame,
49
+ astype_string: bool,
50
+ ) -> pd.DataFrame:
51
+ """
52
+ Convert "Test_Time[s]" column from hours to seconds with string interpretation. (assumes hours are in "HH:MM:SS.MS" format)
53
+
54
+ Parameters
55
+ ----------
56
+ data_frame : pandas.DataFrame
57
+ DataFrame to be modified
58
+ astype_string : bool, optional
59
+ Whether to convert the column to string type before applying the
60
+ conversion function. This is useful if the column contains both numeric
61
+ and string values.
62
+
63
+ Returns
64
+ -------
65
+ pandas.DataFrame
66
+ Modified DataFrame
67
+ """
68
+ try:
69
+ if df is None:
70
+ raise ValueError(f"{df} is None")
71
+ if not isinstance(astype_string, bool):
72
+ raise ValueError(f"{astype_string} is not a boolean")
73
+ if "Test_Time[s]" not in df.columns:
74
+ raise ValueError(f"Test_Time[s] is not in {df.columns}")
75
+ if astype_string:
76
+ df["Test_Time[s]"] = df["Test_Time[s]"].astype(str).apply(_time_to_seconds)
77
+ else:
78
+ df["Test_Time[s]"] = df["Test_Time[s]"].apply(_time_to_seconds)
79
+ except Exception:
80
+ logging.warning("Error fixing Test_Time[s]")
81
+
82
+ return df
83
+
84
+
85
+ def _time_to_seconds(time: str) -> float | None:
86
+ """
87
+ Convert a time string from the format "HH:MM:SS" to a total number of seconds.
88
+
89
+ Parameters
90
+ ----------
91
+ time : str
92
+ Time string to be converted, expected in the format "HH:MM:SS".
93
+ If the input is not in this format, attempts to convert it to a float.
94
+
95
+ Returns
96
+ -------
97
+ float
98
+ Total time in seconds. If the input is None or cannot be split into
99
+ hours, minutes, and seconds, returns the input as a float.
100
+
101
+ Raises
102
+ ------
103
+ ValueError
104
+ If the input time is None.
105
+ """
106
+ try:
107
+ if time is None:
108
+ raise ValueError(f"{time} is None")
109
+ h, m, s = time.split(":")
110
+ return float(s) + int(m) * 60 + int(h) * 3600
111
+ except Exception:
112
+ if time is None:
113
+ return time
114
+ return float(time)
115
+
116
+
117
+ def apply_convert_to_float_if_possible(
118
+ df: pd.DataFrame,
119
+ column_name: str,
120
+ ) -> pd.DataFrame:
121
+ """
122
+ Apply a function to a DataFrame column that tries to convert its values to float if possible.
123
+
124
+ This function tries to convert the values to a float, and returns the value as a
125
+ float if successful. If not successful, the value is returned as is.
126
+
127
+ Parameters
128
+ ----------
129
+ data_frame : pandas.DataFrame
130
+ DataFrame containing the column to be modified.
131
+ column_name : str
132
+ Name of the column in the DataFrame to be modified.
133
+
134
+ Returns
135
+ -------
136
+ pandas.DataFrame
137
+ Modified DataFrame with the column converted to float if possible.
138
+ """
139
+ try:
140
+ if df is None:
141
+ raise ValueError("dataFrame is None")
142
+ if column_name is None:
143
+ raise ValueError("column_name is None")
144
+ if column_name not in df.columns:
145
+ raise ValueError(f"{column_name} is not in {df.columns}")
146
+ df[column_name] = df[column_name].apply(_convert_to_float_if_possible)
147
+ except Exception:
148
+ logging.warning(f"Error applying convert_to_float_if_possible for {column_name}")
149
+
150
+ return df
151
+
152
+
153
+ def _convert_to_float_if_possible(x) -> float:
154
+ """
155
+ Try to convert a value to a float if possible.
156
+
157
+ This function takes a value, and tries to convert it to a float. If the
158
+ conversion is successful, the function returns the value as a float. If
159
+ not successful, the value is returned as is.
160
+
161
+ Parameters
162
+ ----------
163
+ x : any
164
+ Value to be converted to a float if possible.
165
+
166
+ Returns
167
+ -------
168
+ any
169
+ Value converted to a float if possible, otherwise the original value.
170
+
171
+ """
172
+ try:
173
+ return np.float64(x)
174
+ except (ValueError, TypeError):
175
+ return x
176
+
177
+
178
+ def replace_empty_with_none_in_standard_columns(df: pd.DataFrame) -> pd.DataFrame:
179
+ """
180
+ Replace empty strings with None in the columns specified in src.convert.STANDARD_COLUMNS.
181
+
182
+ Parameters
183
+ ----------
184
+ data_frame : pandas.DataFrame
185
+ DataFrame to be modified
186
+
187
+ Returns
188
+ -------
189
+ pandas.DataFrame
190
+ Modified DataFrame
191
+ """
192
+ from pydpeet.io.convert import STANDARD_COLUMNS
193
+
194
+ try:
195
+ if df is None:
196
+ raise ValueError("dataFrame is None")
197
+ if df.empty:
198
+ raise ValueError("dataframe is empty")
199
+ df[STANDARD_COLUMNS] = df[STANDARD_COLUMNS].replace("", None)
200
+ except Exception as e:
201
+ logging.warning(f"Error replacing empty with None. Reason: {e}")
202
+
203
+ return df
204
+
205
+
206
+ def testtime_hours_to_seconds_direct(df: pd.DataFrame) -> pd.DataFrame:
207
+ """
208
+ Convert "Test_Time[s]" column from hours to seconds.
209
+
210
+ Parameters
211
+ ----------
212
+ data_frame : pandas.DataFrame
213
+ DataFrame to be modified
214
+
215
+ Returns
216
+ -------
217
+ pandas.DataFrame
218
+ Modified DataFrame
219
+ """
220
+ try:
221
+ if df is None:
222
+ raise ValueError("dataFrame is None")
223
+ if "Test_Time[s]" not in df.columns:
224
+ raise ValueError("Test_Time[s] is not in dataFrame.columns")
225
+ df["Test_Time[s]"] = df["Test_Time[s]"].apply(_convert_to_hours_to_seconds_direct_if_possible)
226
+ except Exception as e:
227
+ logging.warning(f"Error fixing Test_Time[s] (converting hours to seconds). Reason: {e}")
228
+
229
+ return df
230
+
231
+
232
+ def _convert_to_hours_to_seconds_direct_if_possible(x) -> float:
233
+ # TODO: Docstring
234
+ try:
235
+ return np.float64(x) * 3600
236
+ except (ValueError, TypeError):
237
+ return x
238
+
239
+
240
+ def round_testtime(df: pd.DataFrame) -> pd.DataFrame:
241
+ """
242
+ Round the values in the "Test_Time[s]" column of the DataFrame to 5 decimal places.
243
+
244
+ Parameters
245
+ ----------
246
+ data_frame : pandas.DataFrame
247
+ DataFrame containing the "Test_Time[s]" column to be rounded.
248
+
249
+ Returns
250
+ -------
251
+ pandas.DataFrame
252
+ Modified DataFrame with rounded "Test_Time[s]" values.
253
+ """
254
+ try:
255
+ if df is None:
256
+ raise ValueError("dataFrame is None")
257
+ if df.empty:
258
+ raise ValueError("dataframe is empty")
259
+ if "Test_Time[s]" not in df.columns:
260
+ raise ValueError("Test_Time[s] is not in dataFrame.columns")
261
+
262
+ df["Test_Time[s]"] = round(df["Test_Time[s]"].astype(float), 5)
263
+ except Exception as e:
264
+ logging.warning(f"Error fixing Test_Time[s] (rounding). Reason: {e}")
265
+
266
+ return df
267
+
268
+
269
+ def nan_to_none_in_column(
270
+ df: pd.DataFrame,
271
+ column_name: str,
272
+ ) -> pd.DataFrame:
273
+ """
274
+ Replace NaN values in a DataFrame column (column_name) with None.
275
+
276
+ Parameters
277
+ ----------
278
+ dataFrame : pandas.DataFrame
279
+ DataFrame containing the column to be modified.
280
+ column_name : str
281
+ Name of the column in the DataFrame to be modified.
282
+
283
+ Returns
284
+ -------
285
+ pandas.DataFrame
286
+ Modified DataFrame with NaN values replaced with None.
287
+ """
288
+ try:
289
+ if df is None:
290
+ raise ValueError("dataFrame is None")
291
+ if column_name is None:
292
+ raise ValueError("column_name is None")
293
+ if type(column_name) is not str:
294
+ raise ValueError("column_name is not a string")
295
+ if column_name not in df.columns:
296
+ raise ValueError(f"{column_name} is not in {df.columns}")
297
+ df[column_name] = df[column_name].replace({np.nan: None})
298
+ except Exception:
299
+ logging.warning(f"Error fixing {column_name} (replacing NaN with None)")
300
+
301
+ return df
302
+
303
+
304
+ def move_strings_from_column_to_metadata(
305
+ df: pd.DataFrame,
306
+ column_name: str,
307
+ ) -> pd.DataFrame:
308
+ """
309
+ Move strings from a column into the "Meta_Data" column and replace them with None.
310
+
311
+ This function takes a DataFrame and a column name as input, and moves all strings in that column to the
312
+ "Meta_Data" column. The strings are joined together with a newline character and added to the existing
313
+ "Meta_Data" content. The original column is then replaced with None values.
314
+
315
+ Parameters
316
+ ----------
317
+ data_frame : pandas.DataFrame
318
+ DataFrame containing the column to be modified.
319
+ column_name : str
320
+ Name of the column in the DataFrame to be modified.
321
+
322
+ Returns
323
+ -------
324
+ pandas.DataFrame
325
+ Modified DataFrame with strings moved to "Meta_Data" and replaced with None in the original column.
326
+ """
327
+ concatenated_string = "\n"
328
+ try:
329
+ if df is None:
330
+ raise ValueError("dataFrame is None")
331
+ if not isinstance(column_name, str):
332
+ raise ValueError(f"column_name is not a string. Type is {type(column_name)}")
333
+ if column_name not in df.columns:
334
+ raise ValueError(f"{column_name} is not in {df.columns}")
335
+ if "Meta_Data" not in df.columns:
336
+ raise ValueError("Meta_Data Column doesn't exsist")
337
+
338
+ strings = df[column_name].apply(lambda x: x if isinstance(x, str) else None)
339
+ concatenated_string = concatenated_string.join(filter(None, strings))
340
+
341
+ df.loc[0, "Meta_Data"] = str(df.loc[0, "Meta_Data"]) + "\n\n" + concatenated_string
342
+ df[column_name] = df[column_name].apply(lambda x: None if isinstance(x, str) else x).astype(object)
343
+ df[column_name] = df[column_name].replace({np.nan: None})
344
+ except Exception:
345
+ logging.warning("Error adding Messages to Meta_Data")
346
+
347
+ return df
348
+
349
+
350
+ def fix_time_format(
351
+ df: pd.DataFrame,
352
+ input_format: str = None,
353
+ ) -> pd.DataFrame:
354
+ """
355
+ Fix the format of the "Date_Time" column.
356
+
357
+ This function takes a DataFrame and an optional input format as input, and
358
+ attempts to convert the "Date_Time" column to a
359
+ datetime object using the given input format. If the input format is not
360
+ given, the function will try to infer the format from the data. The
361
+ resulting datetime object is then formatted as a string in the format
362
+ '%Y-%m-%d %H:%M:%S' and replaces the original column.
363
+ Fills empty parts with pandas.NaT.
364
+
365
+ Parameters
366
+ ----------
367
+ data_frame : pandas.DataFrame
368
+ DataFrame containing the column to be modified.
369
+ input_format : str, optional
370
+ Format string to use when converting the column to a datetime object.
371
+ If not given, the function will try to infer the format from the data.
372
+
373
+ Returns
374
+ -------
375
+ pandas.DataFrame
376
+ Modified DataFrame with the "Date_Time" column
377
+ converted to the correct format.
378
+ """
379
+ column_name = "Date_Time"
380
+
381
+ try:
382
+ if df is None:
383
+ raise ValueError("data_frame is None")
384
+ if not isinstance(input_format, str):
385
+ raise ValueError(f"input_format is not a string. Type is {type(column_name)}")
386
+ if column_name not in df.columns:
387
+ raise ValueError(f"{column_name} is not in {df.columns}")
388
+ if "Date_Time" not in df.columns:
389
+ raise ValueError("Date_Time Column doesn't exsist")
390
+ try:
391
+ df[column_name] = pd.to_datetime(df[column_name], format=input_format, errors="coerce")
392
+ except Exception:
393
+ raise ValueError("Error changing to datetime") from None
394
+ try:
395
+ df[column_name] = df[column_name].dt.strftime("%Y-%m-%d %H:%M:%S")
396
+ except Exception:
397
+ raise ValueError("Error changing to correct order in Timeformat") from None
398
+ except Exception:
399
+ logging.warning("Error fixing timeformat Date_Time")
400
+
401
+ return df
402
+
403
+
404
+ def absolute_time_timedate_typecast(df: pd.DataFrame) -> pd.DataFrame:
405
+ """
406
+ Convert the "Date_Time" column in the DataFrame to a datetime object.
407
+
408
+ This function attempts to typecast the specified column in the DataFrame to a pandas datetime object.
409
+
410
+ Parameters
411
+ ----------
412
+ data_frame : pandas.DataFrame
413
+ The DataFrame containing the column to be converted.
414
+
415
+ Returns
416
+ -------
417
+ pandas.DataFrame
418
+ The DataFrame with the "Date_Time" column converted to datetime objects.
419
+ """
420
+ try:
421
+ if df is None:
422
+ raise ValueError("data_frame is None")
423
+ if "Date_Time" not in df.columns:
424
+ raise ValueError("Date_Time Column doesn't exsist")
425
+ df["Date_Time"] = pd.to_datetime(df["Date_Time"], errors="coerce")
426
+ except Exception:
427
+ logging.warning("Error typecasting Date_Time")
428
+
429
+ return df
@@ -0,0 +1,33 @@
1
+ import os
2
+ from importlib.util import (
3
+ module_from_spec,
4
+ spec_from_file_location,
5
+ )
6
+ from types import ModuleType
7
+
8
+
9
+ def load_custom_module(
10
+ folder_path: str,
11
+ module_name: str,
12
+ ) -> ModuleType:
13
+ """
14
+ Loads a custom Python module from a specified folder.
15
+
16
+ Parameters:
17
+ folder_path (str): The path to the folder containing the module file.
18
+ module_name (str): The name of the module to load (without the .py extension).
19
+
20
+ Returns:
21
+ module: The loaded Python module object.
22
+
23
+ Raises:
24
+ FileNotFoundError: If the module file does not exist in the specified folder.
25
+ """
26
+ module_path = os.path.join(folder_path, f"{module_name}.py")
27
+ if not os.path.exists(module_path):
28
+ raise FileNotFoundError(f"Module {module_name}.py not found in the specified folder: {folder_path}")
29
+ spec = spec_from_file_location(module_name, module_path)
30
+ module = module_from_spec(spec)
31
+ spec.loader.exec_module(module)
32
+
33
+ return module
@@ -0,0 +1,36 @@
1
+ import logging
2
+ import time
3
+ from typing import Any
4
+
5
+
6
+ def measure_time(func) -> Any:
7
+ """
8
+ A decorator that measures the execution time of a function.
9
+
10
+ Args:
11
+ func (Callable): The function whose execution time is to be measured.
12
+
13
+ Returns:
14
+ Callable: A wrapped function that, when called, executes the original
15
+ function and prints the time taken to execute it.
16
+ """
17
+
18
+ def wrapper(*args, **kwargs) -> Any:
19
+ """
20
+ A wrapped function that, when called, executes the original function and prints the time taken to execute it.
21
+
22
+ Args:
23
+ *args (Any): The arguments to be passed to the original function.
24
+ **kwargs (Any): The keyword arguments to be passed to the original function.
25
+
26
+ Returns:
27
+ Any: The result of the original function.
28
+ """
29
+ start_time = time.perf_counter()
30
+ result = func(*args, **kwargs)
31
+ end_time = time.perf_counter()
32
+ logging.info(f"{func.__name__} executed in {end_time - start_time:.6f} seconds")
33
+
34
+ return result
35
+
36
+ return wrapper
pydpeet/io/write.py ADDED
@@ -0,0 +1,99 @@
1
+ import logging
2
+ import os
3
+
4
+ import pandas as pd
5
+
6
+ from pydpeet.io.configs.config import DataOutputFiletype
7
+
8
+
9
+ # TODO:
10
+ # Adjust to accept dataframes or lists of dataframes
11
+ # or write dedicated wrapper for list inputs
12
+ def write(
13
+ data_input: pd.DataFrame,
14
+ output_path: str,
15
+ output_file_name: str,
16
+ data_output_filetype: DataOutputFiletype = DataOutputFiletype.parquet,
17
+ ) -> None:
18
+ """
19
+ Export the given DataFrame to the given output path.
20
+
21
+ Parameters
22
+ ----------
23
+ data_frame : pandas.DataFrame
24
+ DataFrame to be exported.
25
+ output_path : str
26
+ Path to the directory where the file will be saved. Must exist.
27
+ output_file_name : str
28
+ Name of the file to be saved.
29
+ data_output_filetype : DataOutputFiletype, optional
30
+ The file type to use when exporting the DataFrame. Defaults to
31
+ DataOutputFiletype.parquet.
32
+
33
+ Raises
34
+ ------
35
+ ValueError
36
+ If data_frame is None, or if output_path or output_file_name are None.
37
+ TypeError
38
+ If data_frame is not a pandas.DataFrame, or if data_output_filetype is not
39
+ a DataOutputFiletype.
40
+ """
41
+ if data_input is None:
42
+ raise ValueError("data_frame is None")
43
+ if not isinstance(data_input, pd.DataFrame):
44
+ raise TypeError(f"data_frame is not a pandas.DataFrame, but {type(data_input)}")
45
+ if output_path is None:
46
+ raise ValueError("output_path is None")
47
+ if not isinstance(output_path, str):
48
+ raise ValueError(f"output_path is not a string, but {type(output_path)}")
49
+ if output_file_name is None:
50
+ raise ValueError("output_file_name is None")
51
+ if not isinstance(output_file_name, str):
52
+ raise ValueError(f"output_file_name is not a string, but {type(output_file_name)}")
53
+ if data_output_filetype is None:
54
+ raise ValueError("data_output_filetype is None")
55
+ if not isinstance(data_output_filetype, DataOutputFiletype):
56
+ raise ValueError(f"data_output_filetype is not a DataOutputFiletype, but {type(data_output_filetype)}")
57
+
58
+ abs_path = os.path.abspath(output_path)
59
+
60
+ if not os.path.exists(abs_path):
61
+ os.makedirs(abs_path)
62
+
63
+ _do_export(data_input, os.path.join(abs_path, output_file_name), data_output_filetype)
64
+
65
+
66
+ def _do_export(
67
+ df: pd.DataFrame,
68
+ output_path: str,
69
+ data_output_filetype: DataOutputFiletype,
70
+ ) -> None:
71
+ """
72
+ Export a DataFrame to a file in the specified format.
73
+
74
+ This function exports the given DataFrame to a file at the specified output
75
+ path in the format specified by data_output_filetype. The function supports
76
+ exporting to Parquet, CSV, and XLSX file formats.
77
+
78
+ Parameters
79
+ ----------
80
+ data_frame : pandas.DataFrame
81
+ The DataFrame to be exported.
82
+ output_path : str
83
+ The path (excluding file extension) where the file will be saved.
84
+ data_output_filetype : DataOutputFiletype
85
+ The format in which to export the DataFrame. Can be one of
86
+ DataOutputFiletype.parquet, DataOutputFiletype.csv, or
87
+ DataOutputFiletype.xlsx.
88
+ """
89
+ logging.info(f"exporting to {output_path}")
90
+ match data_output_filetype:
91
+ case DataOutputFiletype.parquet:
92
+ full_output_path = f"{output_path}_Data.parquet"
93
+ df.to_parquet(full_output_path, index=False, engine="pyarrow")
94
+ case DataOutputFiletype.csv:
95
+ full_output_path = f"{output_path}_Data.csv"
96
+ df.to_csv(full_output_path, index=False)
97
+ case DataOutputFiletype.xlsx:
98
+ full_output_path = f"{output_path}_Data.xlsx"
99
+ df.to_excel(full_output_path, index=False)
@@ -0,0 +1,7 @@
1
+ """
2
+ Auto-generated __init__ file.
3
+ Created: 2026-03-06 15:11:49
4
+ """
5
+
6
+ # Restrictive package init: start with no public API
7
+ __all__ = []
@@ -0,0 +1,7 @@
1
+ """
2
+ Auto-generated __init__ file.
3
+ Created: 2026-03-06 15:11:49
4
+ """
5
+
6
+ # Restrictive package init: start with no public API
7
+ __all__ = []