dycw-utilities 0.109.17__py3-none-any.whl → 0.109.18__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: dycw-utilities
3
- Version: 0.109.17
3
+ Version: 0.109.18
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=9W0BorEWmH8eCgNBXgx9zWKa3BNPPP1rlZfrx6qGCio,61
1
+ utilities/__init__.py,sha256=oi6A9do1IFvsNef8qyo7k6dU87Y8HcM7SGeQWrtR5w0,61
2
2
  utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
3
3
  utilities/astor.py,sha256=xuDUkjq0-b6fhtwjhbnebzbqQZAjMSHR1IIS5uOodVg,777
4
4
  utilities/asyncio.py,sha256=41oQUurWMvadFK5gFnaG21hMM0Vmfn2WS6OpC0R9mas,14757
@@ -36,7 +36,7 @@ utilities/math.py,sha256=TexfvLCI12d9Sw5_W4pKVBZ3nRr3zk2iPkcEU7xdEWU,26771
36
36
  utilities/memory_profiler.py,sha256=tf2C51P2lCujPGvRt2Rfc7VEw5LDXmVPCG3z_AvBmbU,962
37
37
  utilities/modules.py,sha256=SnhsRHRUS1po_acejrINauihGQpPvVsp8RDNCei1OLQ,3173
38
38
  utilities/more_itertools.py,sha256=CPUxrMAcTwRxbzbhiqPKi3Xx9hxqI0t6gkWjutaibGk,5534
39
- utilities/numpy.py,sha256=XMOaauVbssv_yrS1FdSnPExht3G4P8nAALmQsqHwp7w,25143
39
+ utilities/numpy.py,sha256=cBgCBet8YfZP_rb4nkCJHZx9_03qPEinVENMk1dGVYQ,25683
40
40
  utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
41
41
  utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
42
42
  utilities/orjson.py,sha256=Wj5pzG_VdgoAy14a7Luhem-BgYrRtRFvvl_POiszRd0,36930
@@ -46,7 +46,7 @@ utilities/pathlib.py,sha256=31WPMXdLIyXgYOMMl_HOI2wlo66MGSE-cgeelk-Lias,1410
46
46
  utilities/period.py,sha256=ikHXsWtDLr553cfH6p9mMaiCnIAP69B7q84ckWV3HaA,10884
47
47
  utilities/pickle.py,sha256=Bhvd7cZl-zQKQDFjUerqGuSKlHvnW1K2QXeU5UZibtg,657
48
48
  utilities/platform.py,sha256=NU7ycTvAXAG-fdYmDXaM1m4EOml2cGiaYwaUzfzSqyU,1767
49
- utilities/polars.py,sha256=aOQNVyV04qYZjg7Exi6zYERhSQoCMzBP74oufxqANFY,52167
49
+ utilities/polars.py,sha256=tagkPZ-LlyyC9Mx-iCmLmCl3FlblKUVxWSXAaTh-G_M,53164
50
50
  utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
51
51
  utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
52
52
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -86,7 +86,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
86
86
  utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
87
87
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
88
88
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
89
- dycw_utilities-0.109.17.dist-info/METADATA,sha256=9BOdlf6JtDA2hK4ElsW-HsjxWLvub9HUqc1ds9r2CkY,13005
90
- dycw_utilities-0.109.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
91
- dycw_utilities-0.109.17.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
92
- dycw_utilities-0.109.17.dist-info/RECORD,,
89
+ dycw_utilities-0.109.18.dist-info/METADATA,sha256=QCV1ccFqiVe1zuFNo1eiBrkw5_X9YtyA409swR54OPM,13005
90
+ dycw_utilities-0.109.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
91
+ dycw_utilities-0.109.18.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
92
+ dycw_utilities-0.109.18.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.17"
3
+ __version__ = "0.109.18"
utilities/numpy.py CHANGED
@@ -16,11 +16,10 @@ from numpy import (
16
16
  errstate,
17
17
  exp,
18
18
  flatnonzero,
19
- float64,
20
19
  floating,
21
20
  full_like,
22
21
  inf,
23
- int64,
22
+ integer,
24
23
  isclose,
25
24
  isfinite,
26
25
  isinf,
@@ -39,11 +38,13 @@ from numpy.linalg import det, eig
39
38
  from numpy.random import default_rng
40
39
  from numpy.typing import NDArray
41
40
 
42
- from utilities.iterables import is_iterable_not_str
41
+ from utilities.iterables import always_iterable, is_iterable_not_str
43
42
 
44
43
  if TYPE_CHECKING:
45
44
  from collections.abc import Callable, Iterable
46
45
 
46
+ from utilities.types import MaybeIterable
47
+
47
48
 
48
49
  ##
49
50
 
@@ -89,8 +90,9 @@ timedelta64as = dtype("timedelta64[as]")
89
90
 
90
91
  NDArrayA = NDArray[Any]
91
92
  NDArrayB = NDArray[bool_]
92
- NDArrayF = NDArray[float64]
93
- NDArrayI = NDArray[int64]
93
+ NDArrayC128 = NDArray[complex128]
94
+ NDArrayF = NDArray[floating[Any]]
95
+ NDArrayI = NDArray[integer[Any]]
94
96
  NDArrayO = NDArray[object_]
95
97
 
96
98
 
@@ -135,7 +137,7 @@ class AsIntError(Exception): ...
135
137
 
136
138
 
137
139
  def boxcar(
138
- array: NDArray[floating[Any]],
140
+ array: NDArrayF,
139
141
  /,
140
142
  *,
141
143
  loc_low: float = -1.0,
@@ -144,7 +146,7 @@ def boxcar(
144
146
  slope_high: float = 1.0,
145
147
  rtol: float | None = None,
146
148
  atol: float | None = None,
147
- ) -> NDArray[floating[Any]]:
149
+ ) -> NDArrayF:
148
150
  """Construct a boxcar function."""
149
151
  if not is_at_most(loc_low, loc_high, rtol=rtol, atol=atol):
150
152
  raise _BoxCarLocationsError(low=loc_low, high=loc_high)
@@ -224,28 +226,43 @@ def fillna(array: NDArrayF, /, *, value: float = 0.0) -> NDArrayF:
224
226
  ##
225
227
 
226
228
 
227
- def filter_frequencies(
229
+ def adjust_frequencies(
228
230
  array: NDArrayF,
229
231
  /,
230
- *filters: Callable[[NDArray[floating[Any]]], NDArrayB],
232
+ *,
233
+ filters: MaybeIterable[Callable[[NDArrayF], NDArrayB]] | None = None,
234
+ weights: MaybeIterable[Callable[[NDArrayF], NDArrayF]] | None = None,
231
235
  d: int = 1,
232
236
  ) -> NDArrayF:
233
- """Filter an array by the frequencies of its FFT."""
237
+ """Adjust an array via its FFT frequencies."""
234
238
  (n,) = array.shape
235
- fft_vals = fft(array)
239
+ amplitudes = fft(array)
236
240
  freqs = fftfreq(n, d=d)
237
- reduced = reduce(partial(_filter_frequencies_one, freqs=freqs), filters, fft_vals)
238
- return ifft(reduced).real
241
+ if filters is not None:
242
+ amplitudes = reduce(
243
+ partial(_adjust_frequencies_filter_one, freqs=freqs),
244
+ always_iterable(filters),
245
+ amplitudes,
246
+ )
247
+ if weights is not None:
248
+ amplitudes = reduce(
249
+ partial(_adjust_frequencies_weight_one, freqs=freqs),
250
+ always_iterable(weights),
251
+ amplitudes,
252
+ )
253
+ return ifft(amplitudes).real
254
+
255
+
256
+ def _adjust_frequencies_filter_one(
257
+ acc: NDArrayC128, el: Callable[[NDArrayF], NDArrayB], /, *, freqs: NDArrayF
258
+ ) -> NDArrayC128:
259
+ return where(el(freqs), acc, 0.0)
239
260
 
240
261
 
241
- def _filter_frequencies_one(
242
- acc: NDArray[complex128],
243
- el: Callable[[NDArray[floating[Any]]], NDArrayB],
244
- /,
245
- *,
246
- freqs: NDArray[floating[Any]],
247
- ) -> NDArray[complex128]:
248
- return where(el(freqs), acc, 0.0)
262
+ def _adjust_frequencies_weight_one(
263
+ acc: NDArrayC128, el: Callable[[NDArrayF], NDArrayF], /, *, freqs: NDArrayF
264
+ ) -> NDArrayC128:
265
+ return acc * el(freqs)
249
266
 
250
267
 
251
268
  ##
@@ -284,12 +301,12 @@ class FlatN0MultipleError(FlatN0Error):
284
301
  ##
285
302
 
286
303
 
287
- def get_frequency_spectrum(array: NDArrayF, /, *, d: int = 1) -> NDArray[floating[Any]]:
304
+ def get_frequency_spectrum(array: NDArrayF, /, *, d: int = 1) -> NDArrayF:
288
305
  """Get the frequency spectrum."""
289
306
  (n,) = array.shape
290
- fft_vals = fft(array)
307
+ amplitudes = fft(array)
291
308
  freqs = fftfreq(n, d=d)
292
- amplitudes = np.abs(fft_vals)
309
+ amplitudes = np.abs(amplitudes)
293
310
  data = np.hstack([freqs.reshape(-1, 1), amplitudes.reshape(-1, 1)])
294
311
  return data[argsort(data[:, 0])]
295
312
 
@@ -908,14 +925,14 @@ def shift_bool(
908
925
 
909
926
 
910
927
  def sigmoid(
911
- array: NDArray[floating[Any]],
928
+ array: NDArrayF,
912
929
  /,
913
930
  *,
914
931
  loc: float = 0.0,
915
932
  slope: float = 1.0,
916
933
  rtol: float | None = None,
917
934
  atol: float | None = None,
918
- ) -> NDArray[floating[Any]]:
935
+ ) -> NDArrayF:
919
936
  """Construct a sigmoid function."""
920
937
  if is_zero(slope, rtol=rtol, atol=atol):
921
938
  raise SigmoidError
@@ -965,6 +982,7 @@ __all__ = [
965
982
  "NDArrayO",
966
983
  "ShiftError",
967
984
  "SigmoidError",
985
+ "adjust_frequencies",
968
986
  "array_indexer",
969
987
  "as_int",
970
988
  "boxcar",
@@ -983,7 +1001,6 @@ __all__ = [
983
1001
  "datetime64us",
984
1002
  "discretize",
985
1003
  "fillna",
986
- "filter_frequencies",
987
1004
  "flatn0",
988
1005
  "get_frequency_spectrum",
989
1006
  "has_dtype",
utilities/polars.py CHANGED
@@ -119,6 +119,7 @@ if TYPE_CHECKING:
119
119
  TimeUnit, # pyright: ignore[reportPrivateImportUsage]
120
120
  )
121
121
 
122
+ from utilities.numpy import NDArrayB, NDArrayF
122
123
  from utilities.types import (
123
124
  Dataclass,
124
125
  MaybeIterable,
@@ -141,6 +142,27 @@ _FINITE_EWM_MIN_WEIGHT = 0.9999
141
142
  ##
142
143
 
143
144
 
145
+ def adjust_frequencies(
146
+ series: Series,
147
+ /,
148
+ *,
149
+ filters: MaybeIterable[Callable[[NDArrayF], NDArrayB]] | None = None,
150
+ weights: MaybeIterable[Callable[[NDArrayF], NDArrayF]] | None = None,
151
+ d: int = 1,
152
+ ) -> Series:
153
+ """Adjust a Series via its FFT frequencies."""
154
+ import utilities.numpy
155
+
156
+ array = series.to_numpy()
157
+ adjusted = utilities.numpy.adjust_frequencies(
158
+ array, filters=filters, weights=weights, d=d
159
+ )
160
+ return Series(name=series.name, values=adjusted, dtype=Float64)
161
+
162
+
163
+ ##
164
+
165
+
144
166
  def append_dataclass(df: DataFrame, obj: Dataclass, /) -> DataFrame:
145
167
  """Append a dataclass object to a DataFrame."""
146
168
  non_null_fields = {k: v for k, v in asdict(obj).items() if v is not None}
@@ -1027,6 +1049,20 @@ class _GetDataTypeOrSeriesTimeZoneNotZonedError(GetDataTypeOrSeriesTimeZoneError
1027
1049
  ##
1028
1050
 
1029
1051
 
1052
+ def get_frequency_spectrum(series: Series, /, *, d: int = 1) -> DataFrame:
1053
+ """Get the frequency spectrum."""
1054
+ import utilities.numpy
1055
+
1056
+ array = series.to_numpy()
1057
+ spectrum = utilities.numpy.get_frequency_spectrum(array, d=d)
1058
+ return DataFrame(
1059
+ data=spectrum, schema={"frequency": Float64, "amplitude": Float64}, orient="row"
1060
+ )
1061
+
1062
+
1063
+ ##
1064
+
1065
+
1030
1066
  @overload
1031
1067
  def get_series_number_of_decimals(
1032
1068
  series: Series, /, *, nullable: Literal[True]
@@ -1736,6 +1772,7 @@ __all__ = [
1736
1772
  "SetFirstRowAsColumnsError",
1737
1773
  "StructFromDataClassError",
1738
1774
  "YieldStructSeriesElementsError",
1775
+ "adjust_frequencies",
1739
1776
  "append_dataclass",
1740
1777
  "are_frames_equal",
1741
1778
  "ceil_datetime",
@@ -1753,6 +1790,7 @@ __all__ = [
1753
1790
  "finite_ewm_mean",
1754
1791
  "floor_datetime",
1755
1792
  "get_data_type_or_series_time_zone",
1793
+ "get_frequency_spectrum",
1756
1794
  "get_series_number_of_decimals",
1757
1795
  "insert_after",
1758
1796
  "insert_before",