dycw-utilities 0.109.15__py3-none-any.whl → 0.109.17__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.15
3
+ Version: 0.109.17
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=qZyUzwQ2zZu6ZkKszEeJGs10nbVWvqm_izEvqG8MgXM,61
1
+ utilities/__init__.py,sha256=9W0BorEWmH8eCgNBXgx9zWKa3BNPPP1rlZfrx6qGCio,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=rA1b0_GkBUSMjnv77tinRM70KRnkcmZxI9xbrsXFDRg,21819
39
+ utilities/numpy.py,sha256=XMOaauVbssv_yrS1FdSnPExht3G4P8nAALmQsqHwp7w,25143
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
@@ -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.15.dist-info/METADATA,sha256=sqk1b4cCCM8V737VBkLkTToWlAJ5Mb7Co4LmlynJ4Tg,13005
90
- dycw_utilities-0.109.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
91
- dycw_utilities-0.109.15.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
92
- dycw_utilities-0.109.15.dist-info/RECORD,,
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,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.15"
3
+ __version__ = "0.109.17"
utilities/numpy.py CHANGED
@@ -1,19 +1,23 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from functools import reduce
4
+ from functools import partial, reduce
5
5
  from itertools import repeat
6
6
  from typing import TYPE_CHECKING, Any, overload, override
7
7
 
8
8
  import numpy as np
9
9
  from numpy import (
10
+ argsort,
10
11
  array,
11
12
  bool_,
13
+ complex128,
12
14
  digitize,
13
15
  dtype,
14
16
  errstate,
17
+ exp,
15
18
  flatnonzero,
16
19
  float64,
20
+ floating,
17
21
  full_like,
18
22
  inf,
19
23
  int64,
@@ -30,6 +34,7 @@ from numpy import (
30
34
  roll,
31
35
  where,
32
36
  )
37
+ from numpy.fft import fft, fftfreq, ifft
33
38
  from numpy.linalg import det, eig
34
39
  from numpy.random import default_rng
35
40
  from numpy.typing import NDArray
@@ -37,7 +42,7 @@ from numpy.typing import NDArray
37
42
  from utilities.iterables import is_iterable_not_str
38
43
 
39
44
  if TYPE_CHECKING:
40
- from collections.abc import Iterable
45
+ from collections.abc import Callable, Iterable
41
46
 
42
47
 
43
48
  ##
@@ -129,6 +134,65 @@ class AsIntError(Exception): ...
129
134
  ##
130
135
 
131
136
 
137
+ def boxcar(
138
+ array: NDArray[floating[Any]],
139
+ /,
140
+ *,
141
+ loc_low: float = -1.0,
142
+ slope_low: float = 1.0,
143
+ loc_high: float = 1.0,
144
+ slope_high: float = 1.0,
145
+ rtol: float | None = None,
146
+ atol: float | None = None,
147
+ ) -> NDArray[floating[Any]]:
148
+ """Construct a boxcar function."""
149
+ if not is_at_most(loc_low, loc_high, rtol=rtol, atol=atol):
150
+ raise _BoxCarLocationsError(low=loc_low, high=loc_high)
151
+ if not is_positive(slope_low, rtol=rtol, atol=atol):
152
+ raise _BoxCarLowerBoundSlopeError(slope=slope_low)
153
+ if not is_positive(slope_high, rtol=rtol, atol=atol):
154
+ raise _BoxCarUpperBoundSlopeError(slope=slope_high)
155
+ return (
156
+ sigmoid(array, loc=loc_low, slope=slope_low)
157
+ + sigmoid(array, loc=loc_high, slope=-slope_high)
158
+ ) / 2
159
+
160
+
161
+ @dataclass(kw_only=True, slots=True)
162
+ class BoxCarError(Exception): ...
163
+
164
+
165
+ @dataclass(kw_only=True, slots=True)
166
+ class _BoxCarLocationsError(BoxCarError):
167
+ low: float
168
+ high: float
169
+
170
+ @override
171
+ def __str__(self) -> str:
172
+ return f"Location parameters must be consistent; got {self.low} and {self.high}"
173
+
174
+
175
+ @dataclass(kw_only=True, slots=True)
176
+ class _BoxCarLowerBoundSlopeError(BoxCarError):
177
+ slope: float
178
+
179
+ @override
180
+ def __str__(self) -> str:
181
+ return f"Lower-bound slope parameter must be positive; got {self.slope}"
182
+
183
+
184
+ @dataclass(kw_only=True, slots=True)
185
+ class _BoxCarUpperBoundSlopeError(BoxCarError):
186
+ slope: float
187
+
188
+ @override
189
+ def __str__(self) -> str:
190
+ return f"Upper-bound slope parameter must be positive; got {self.slope}"
191
+
192
+
193
+ ##
194
+
195
+
132
196
  def discretize(x: NDArrayF, bins: int | Iterable[float], /) -> NDArrayF:
133
197
  """Discretize an array of floats.
134
198
 
@@ -160,6 +224,33 @@ def fillna(array: NDArrayF, /, *, value: float = 0.0) -> NDArrayF:
160
224
  ##
161
225
 
162
226
 
227
+ def filter_frequencies(
228
+ array: NDArrayF,
229
+ /,
230
+ *filters: Callable[[NDArray[floating[Any]]], NDArrayB],
231
+ d: int = 1,
232
+ ) -> NDArrayF:
233
+ """Filter an array by the frequencies of its FFT."""
234
+ (n,) = array.shape
235
+ fft_vals = fft(array)
236
+ freqs = fftfreq(n, d=d)
237
+ reduced = reduce(partial(_filter_frequencies_one, freqs=freqs), filters, fft_vals)
238
+ return ifft(reduced).real
239
+
240
+
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)
249
+
250
+
251
+ ##
252
+
253
+
163
254
  def flatn0(array: NDArrayB, /) -> int:
164
255
  """Return the index of the unique True element."""
165
256
  if not array.any():
@@ -193,6 +284,19 @@ class FlatN0MultipleError(FlatN0Error):
193
284
  ##
194
285
 
195
286
 
287
+ def get_frequency_spectrum(array: NDArrayF, /, *, d: int = 1) -> NDArray[floating[Any]]:
288
+ """Get the frequency spectrum."""
289
+ (n,) = array.shape
290
+ fft_vals = fft(array)
291
+ freqs = fftfreq(n, d=d)
292
+ amplitudes = np.abs(fft_vals)
293
+ data = np.hstack([freqs.reshape(-1, 1), amplitudes.reshape(-1, 1)])
294
+ return data[argsort(data[:, 0])]
295
+
296
+
297
+ ##
298
+
299
+
196
300
  def has_dtype(x: Any, dtype: Any, /) -> bool:
197
301
  """Check if an object has the required dtype."""
198
302
  if is_iterable_not_str(dtype):
@@ -803,6 +907,31 @@ def shift_bool(
803
907
  ##
804
908
 
805
909
 
910
+ def sigmoid(
911
+ array: NDArray[floating[Any]],
912
+ /,
913
+ *,
914
+ loc: float = 0.0,
915
+ slope: float = 1.0,
916
+ rtol: float | None = None,
917
+ atol: float | None = None,
918
+ ) -> NDArray[floating[Any]]:
919
+ """Construct a sigmoid function."""
920
+ if is_zero(slope, rtol=rtol, atol=atol):
921
+ raise SigmoidError
922
+ return 1 / (1 + exp(-slope * (array - loc)))
923
+
924
+
925
+ @dataclass(kw_only=True, slots=True)
926
+ class SigmoidError(Exception):
927
+ @override
928
+ def __str__(self) -> str:
929
+ return "Slope must be non-zero"
930
+
931
+
932
+ ##
933
+
934
+
806
935
  def _is_close(
807
936
  x: Any,
808
937
  y: Any,
@@ -825,6 +954,7 @@ def _is_close(
825
954
  __all__ = [
826
955
  "DEFAULT_RNG",
827
956
  "AsIntError",
957
+ "BoxCarError",
828
958
  "FlatN0EmptyError",
829
959
  "FlatN0Error",
830
960
  "FlatN0MultipleError",
@@ -834,8 +964,10 @@ __all__ = [
834
964
  "NDArrayI",
835
965
  "NDArrayO",
836
966
  "ShiftError",
967
+ "SigmoidError",
837
968
  "array_indexer",
838
969
  "as_int",
970
+ "boxcar",
839
971
  "datetime64D",
840
972
  "datetime64M",
841
973
  "datetime64W",
@@ -851,7 +983,9 @@ __all__ = [
851
983
  "datetime64us",
852
984
  "discretize",
853
985
  "fillna",
986
+ "filter_frequencies",
854
987
  "flatn0",
988
+ "get_frequency_spectrum",
855
989
  "has_dtype",
856
990
  "is_at_least",
857
991
  "is_at_least_or_nan",
@@ -902,4 +1036,5 @@ __all__ = [
902
1036
  "maximum",
903
1037
  "minimum",
904
1038
  "shift_bool",
1039
+ "sigmoid",
905
1040
  ]