dycw-utilities 0.109.21__py3-none-any.whl → 0.109.23__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,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.109.21
3
+ Version: 0.109.23
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
7
  Requires-Dist: typing-extensions<4.14,>=4.13.1
8
8
  Provides-Extra: test
9
- Requires-Dist: hypothesis<6.132,>=6.131.7; extra == 'test'
9
+ Requires-Dist: hypothesis<6.132,>=6.131.9; extra == 'test'
10
10
  Requires-Dist: pytest-asyncio<0.27,>=0.26.0; extra == 'test'
11
11
  Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
12
12
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
@@ -80,7 +80,7 @@ Provides-Extra: zzz-test-hypothesis
80
80
  Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
81
81
  Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
82
82
  Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
83
- Requires-Dist: hypothesis<6.132,>=6.131.7; extra == 'zzz-test-hypothesis'
83
+ Requires-Dist: hypothesis<6.132,>=6.131.9; extra == 'zzz-test-hypothesis'
84
84
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
85
85
  Requires-Dist: numpy<2.3,>=2.2.5; extra == 'zzz-test-hypothesis'
86
86
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=Q2YV4YDdpMRcnIlWhuT_v301enhQDMFopz9BOGv-13g,61
1
+ utilities/__init__.py,sha256=4lEn-RM4k4cprCQpv11-dCWVShqTEywTV3oeqqUTdtc,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
@@ -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=27oT-CmOtlAEdsZ2XOoN1GLINnRM2Byj_oBtsHGmp3c,57444
49
+ utilities/polars.py,sha256=woTGhyzXNLN30SQgwXz54-I1aJp1oWATJ2rpmke7gKI,58419
50
50
  utilities/polars_ols.py,sha256=efhXf0gjrHUpQrvS6a7g8yJQJWf_ATKtJnqqF2inCOU,5680
51
51
  utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
52
52
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -68,7 +68,7 @@ utilities/slack_sdk.py,sha256=SeDNMh24IPiEBWoGMdgvrflUaFa9TGlTS03H9-NKaQw,4132
68
68
  utilities/socket.py,sha256=K77vfREvzoVTrpYKo6MZakol0EYu2q1sWJnnZqL0So0,118
69
69
  utilities/sqlalchemy.py,sha256=GWzp54TP3F2mGhxPTn0c56KxxDeN9VKLMagcRSELhf4,35453
70
70
  utilities/sqlalchemy_polars.py,sha256=oGyMX5gSxuLI3N8mtz_-ml3UdWKcZuj6aFRW6ifI0Kc,15617
71
- utilities/statsmodels.py,sha256=F0bNmPUCmi3GsfpSuTVBYWxGimKury-pmcpFQ-SJ65I,2629
71
+ utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
72
72
  utilities/streamlit.py,sha256=U9PJBaKP1IdSykKhPZhIzSPTZsmLsnwbEPZWzNhJPKk,2955
73
73
  utilities/sys.py,sha256=h0Xr7Vj86wNalvwJVP1wj5Y0kD_VWm1vzuXZ_jw94mE,2743
74
74
  utilities/tempfile.py,sha256=VqmZJAhTJ1OaVywFzk5eqROV8iJbW9XQ_QYAV0bpdRo,1384
@@ -87,7 +87,7 @@ utilities/warnings.py,sha256=yUgjnmkCRf6QhdyAXzl7u0qQFejhQG3PrjoSwxpbHrs,1819
87
87
  utilities/whenever.py,sha256=TjoTAJ1R27-rKXiXzdE4GzPidmYqm0W58XydDXp-QZM,17786
88
88
  utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
89
89
  utilities/zoneinfo.py,sha256=-DQz5a0Ikw9jfSZtL0BEQkXOMC9yGn_xiJYNCLMiqEc,1989
90
- dycw_utilities-0.109.21.dist-info/METADATA,sha256=oeS1xA25IzXCADvmuZu2P1ULQ3p1Kyv0_tkEcZmZzDo,13005
91
- dycw_utilities-0.109.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.109.21.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
- dycw_utilities-0.109.21.dist-info/RECORD,,
90
+ dycw_utilities-0.109.23.dist-info/METADATA,sha256=fMol_EYcAIARY4nGcAjYEe1AXs-5dAorn3vGy27XdBY,13005
91
+ dycw_utilities-0.109.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.109.23.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
93
+ dycw_utilities-0.109.23.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.109.21"
3
+ __version__ = "0.109.23"
utilities/polars.py CHANGED
@@ -140,6 +140,32 @@ DatetimeUSEastern = Datetime(time_zone="US/Eastern")
140
140
  DatetimeUTC = Datetime(time_zone="UTC")
141
141
  _FINITE_EWM_MIN_WEIGHT = 0.9999
142
142
 
143
+ ##
144
+
145
+
146
+ def ac_halflife(
147
+ series: Series,
148
+ /,
149
+ *,
150
+ adjusted: bool = False,
151
+ fft: bool = True,
152
+ bartlett_confint: bool = True,
153
+ missing: ACFMissing = "none",
154
+ step: float = 0.01,
155
+ ) -> float:
156
+ """Compute the autocorrelation halflife."""
157
+ import utilities.statsmodels
158
+
159
+ array = series.to_numpy()
160
+ return utilities.statsmodels.ac_halflife(
161
+ array,
162
+ adjusted=adjusted,
163
+ fft=fft,
164
+ bartlett_confint=bartlett_confint,
165
+ missing=missing,
166
+ step=step,
167
+ )
168
+
143
169
 
144
170
  ##
145
171
 
@@ -223,6 +249,11 @@ def _acf_process_qstats_pvalues(qstats: NDArrayF, pvalues: NDArrayF, /) -> DataF
223
249
  ##
224
250
 
225
251
 
252
+ # def acf_halflife(series: Series,/)
253
+
254
+ ##
255
+
256
+
226
257
  def adjust_frequencies(
227
258
  series: Series,
228
259
  /,
@@ -1197,6 +1228,21 @@ class _GetDataTypeOrSeriesTimeZoneNotZonedError(GetDataTypeOrSeriesTimeZoneError
1197
1228
  ##
1198
1229
 
1199
1230
 
1231
+ def get_expr_name(obj: Series | DataFrame, expr: Expr, /) -> str:
1232
+ """Get the name of an expression."""
1233
+ match obj:
1234
+ case Series() as series:
1235
+ return get_expr_name(series.to_frame(), expr)
1236
+ case DataFrame() as df:
1237
+ selected = df.select(expr)
1238
+ return one(selected.columns)
1239
+ case _ as never:
1240
+ assert_never(never)
1241
+
1242
+
1243
+ ##
1244
+
1245
+
1200
1246
  def get_frequency_spectrum(series: Series, /, *, d: int = 1) -> DataFrame:
1201
1247
  """Get the frequency spectrum."""
1202
1248
  import utilities.numpy
@@ -1939,6 +1985,7 @@ __all__ = [
1939
1985
  "finite_ewm_mean",
1940
1986
  "floor_datetime",
1941
1987
  "get_data_type_or_series_time_zone",
1988
+ "get_expr_name",
1942
1989
  "get_frequency_spectrum",
1943
1990
  "get_series_number_of_decimals",
1944
1991
  "insert_after",
utilities/statsmodels.py CHANGED
@@ -2,12 +2,45 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any, Literal, cast, overload
4
4
 
5
+ import numpy as np
5
6
  import statsmodels.tsa.stattools
7
+ from numpy import arange, argmax, interp, nan
8
+
9
+ from utilities.numpy import shift
6
10
 
7
11
  if TYPE_CHECKING:
8
12
  from utilities.numpy import NDArrayF
9
13
 
10
14
 
15
+ def ac_halflife(
16
+ array: NDArrayF,
17
+ /,
18
+ *,
19
+ adjusted: bool = False,
20
+ fft: bool = True,
21
+ bartlett_confint: bool = True,
22
+ missing: ACFMissing = "none",
23
+ step: float = 0.01,
24
+ ) -> float:
25
+ """Compute the autocorrelation halflife."""
26
+ (n,) = array.shape
27
+ acfs = acf(
28
+ array,
29
+ adjusted=adjusted,
30
+ nlags=n,
31
+ fft=fft,
32
+ bartlett_confint=bartlett_confint,
33
+ missing=missing,
34
+ )
35
+ lags = arange(0, n, step=step)
36
+ interp_acfs = interp(lags, arange(n), acfs)
37
+ is_half = (shift(interp_acfs) > 0.5) & (interp_acfs <= 0.5)
38
+ return lags[argmax(is_half)].item() if np.any(is_half) else nan
39
+
40
+
41
+ ##
42
+
43
+
11
44
  type ACFMissing = Literal["none", "raise", "conservative", "drop"]
12
45
 
13
46
 
@@ -114,4 +147,4 @@ def acf(
114
147
  )
115
148
 
116
149
 
117
- __all__ = ["ACFMissing", "acf"]
150
+ __all__ = ["ACFMissing", "ac_halflife", "acf"]