dycw-utilities 0.159.2__py3-none-any.whl → 0.159.4__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.159.2
3
+ Version: 0.159.4
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=xlALYg1JKmNAieowbIXvn_qRt1OWQ6pgWljwJTA5kvs,60
1
+ utilities/__init__.py,sha256=8PwC9PN_0tTaC-HGuAmY0sqVwC7bKnFBwSDdjEvRwnk,60
2
2
  utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
3
3
  utilities/asyncio.py,sha256=PUedzQ5deqlSECQ33sam9cRzI9TnygHz3FdOqWJWPTM,15288
4
4
  utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
@@ -32,7 +32,7 @@ utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
32
32
  utilities/libcst.py,sha256=TKgKN4bNmtBNEE-TUfhTyd1BrTncfsl_7tTuhpesGYY,5585
33
33
  utilities/lightweight_charts.py,sha256=YM3ojBvJxuCSUBu_KrhFBmaMCvRPvupKC3qkm-UVZq4,2751
34
34
  utilities/logging.py,sha256=ihbfQJgjc7t3Pds0oPvF_J1eigiqFKzxNOijzoee8U4,18064
35
- utilities/math.py,sha256=7ve4RxX3g-FGGVnWV0K9bBeGnKUEjnTbH13VxdvFtGE,26847
35
+ utilities/math.py,sha256=cevB-YyEYAzJTWtkAr7qeeu-hbxorDI3gMznXlmNQkw,26897
36
36
  utilities/memory_profiler.py,sha256=XzN56jDCa5aqXS_DxEjb_K4L6aIWh_5zyKi6OhcIxw0,853
37
37
  utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
38
38
  utilities/more_itertools.py,sha256=syfIPhQF_WS-YiicdGe2h5F1G-Ld12Q2XsVduL2hA40,10908
@@ -45,7 +45,7 @@ utilities/parse.py,sha256=JcJn5yXKhIWXBCwgBdPsyu7Hvcuw6kyEdqvaebCaI9k,17951
45
45
  utilities/pathlib.py,sha256=qGuU8XPmdgGpy8tOMUgelfXx3kxI8h9IaV3TI_06QGE,8428
46
46
  utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
47
47
  utilities/platform.py,sha256=pTn7gw6N4T6LdKrf0virwarof_mze9WtoQlrGMzhGVI,2798
48
- utilities/polars.py,sha256=SE-ZuB19vd-RxoAX7lZPImKcc199ArZh13jy65iV8gk,77059
48
+ utilities/polars.py,sha256=4Qc9lv0SZEpjXFKANUQBb7nA55sC4zaHtlZEJXTBALg,76896
49
49
  utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
50
50
  utilities/postgres.py,sha256=ynCTTaF-bVEOSW-KEAR-dlLh_hYjeVVjm__-4pEU8Zk,12269
51
51
  utilities/pottery.py,sha256=ggMN72Y7wx7Js8VN6eyNyodpm8TIYqZHGghkDPXIVWk,3949
@@ -74,7 +74,7 @@ utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
74
74
  utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
75
75
  utilities/traceback.py,sha256=1k5JgumSMaqAGLd0dZ36CtPS0EGaglxTr29r2Dz4D60,9457
76
76
  utilities/typed_settings.py,sha256=SFWqS3lAzV7IfNRwqFcTk0YynTcQ7BmrcW2mr_KUnos,4466
77
- utilities/types.py,sha256=L4cjFPyFZX58Urfw0S_i-XRywPIFyuSLOieewj0qqsM,18516
77
+ utilities/types.py,sha256=zVMapJd3i6xPrY4VagawVfBW_a-ygIKWffwXqxcH0bo,18604
78
78
  utilities/typing.py,sha256=7ZgCNZwA6oaiwpSJIS9Rj3i3MbRBYHMqbC3jMe5KiNg,13992
79
79
  utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
80
80
  utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
@@ -87,8 +87,8 @@ utilities/zoneinfo.py,sha256=FBMcUQ4662Aq8SsuCL1OAhDQiyANmVjtb-C30DRrWoE,1966
87
87
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
88
88
  utilities/pytest_plugins/pytest_randomly.py,sha256=B1qYVlExGOxTywq2r1SMi5o7btHLk2PNdY_b1p98dkE,409
89
89
  utilities/pytest_plugins/pytest_regressions.py,sha256=9v8kAXDM2ycIXJBimoiF4EgrwbUvxTycFWJiGR_GHhM,1466
90
- dycw_utilities-0.159.2.dist-info/METADATA,sha256=SCOwKj6kpRPNyf969J709Gfh_B23vJM7AJJAwC2OQ5c,1643
91
- dycw_utilities-0.159.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.159.2.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
93
- dycw_utilities-0.159.2.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.159.2.dist-info/RECORD,,
90
+ dycw_utilities-0.159.4.dist-info/METADATA,sha256=WLxPLavB-QLjzJhQr7T5-LbVJNRA_PlRiBtCJTT9FmQ,1643
91
+ dycw_utilities-0.159.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.159.4.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
93
+ dycw_utilities-0.159.4.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
+ dycw_utilities-0.159.4.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.159.2"
3
+ __version__ = "0.159.4"
utilities/math.py CHANGED
@@ -641,7 +641,10 @@ def _is_close(
641
641
  ##
642
642
 
643
643
 
644
- def number_of_decimals(x: float, /, *, max_decimals: int = 20) -> int:
644
+ MAX_DECIMALS = 10
645
+
646
+
647
+ def number_of_decimals(x: float, /, *, max_decimals: int = MAX_DECIMALS) -> int:
645
648
  """Get the number of decimals."""
646
649
  _, frac = divmod(x, 1)
647
650
  results = (
@@ -889,6 +892,7 @@ def significant_figures(x: float, /, *, n: int = 2) -> str:
889
892
 
890
893
 
891
894
  __all__ = [
895
+ "MAX_DECIMALS",
892
896
  "MAX_FLOAT32",
893
897
  "MAX_FLOAT64",
894
898
  "MAX_INT8",
utilities/polars.py CHANGED
@@ -36,6 +36,7 @@ from polars import (
36
36
  datetime_range,
37
37
  int_range,
38
38
  lit,
39
+ max_horizontal,
39
40
  struct,
40
41
  sum_horizontal,
41
42
  when,
@@ -52,11 +53,10 @@ from polars.schema import Schema
52
53
  from polars.testing import assert_frame_equal, assert_series_equal
53
54
  from whenever import DateDelta, DateTimeDelta, PlainDateTime, TimeDelta, ZonedDateTime
54
55
 
56
+ import utilities.math
55
57
  from utilities.dataclasses import yield_fields
56
58
  from utilities.errors import ImpossibleCaseError
57
59
  from utilities.functions import (
58
- EnsureIntError,
59
- ensure_int,
60
60
  is_dataclass_class,
61
61
  is_dataclass_instance,
62
62
  make_isinstance,
@@ -79,12 +79,12 @@ from utilities.iterables import (
79
79
  )
80
80
  from utilities.json import write_formatted_json
81
81
  from utilities.math import (
82
+ MAX_DECIMALS,
82
83
  CheckIntegerError,
83
84
  check_integer,
84
85
  ewm_parameters,
85
86
  is_less_than,
86
87
  is_non_negative,
87
- number_of_decimals,
88
88
  )
89
89
  from utilities.reprlib import get_repr
90
90
  from utilities.types import MaybeStr, Number, PathLike, WeekDay
@@ -1426,56 +1426,6 @@ def get_frequency_spectrum(series: Series, /, *, d: int = 1) -> DataFrame:
1426
1426
  ##
1427
1427
 
1428
1428
 
1429
- @overload
1430
- def get_series_number_of_decimals(
1431
- series: Series, /, *, nullable: Literal[True]
1432
- ) -> int | None: ...
1433
- @overload
1434
- def get_series_number_of_decimals(
1435
- series: Series, /, *, nullable: Literal[False] = False
1436
- ) -> int: ...
1437
- @overload
1438
- def get_series_number_of_decimals(
1439
- series: Series, /, *, nullable: bool = False
1440
- ) -> int | None: ...
1441
- def get_series_number_of_decimals(
1442
- series: Series, /, *, nullable: bool = False
1443
- ) -> int | None:
1444
- """Get the number of decimals of a series."""
1445
- if not isinstance(dtype := series.dtype, Float64):
1446
- raise _GetSeriesNumberOfDecimalsNotFloatError(dtype=dtype)
1447
- decimals = series.map_elements(number_of_decimals, return_dtype=Int64).max()
1448
- try:
1449
- return ensure_int(decimals, nullable=nullable)
1450
- except EnsureIntError:
1451
- raise _GetSeriesNumberOfDecimalsAllNullError(series=series) from None
1452
-
1453
-
1454
- @dataclass(kw_only=True, slots=True)
1455
- class GetSeriesNumberOfDecimalsError(Exception): ...
1456
-
1457
-
1458
- @dataclass(kw_only=True, slots=True)
1459
- class _GetSeriesNumberOfDecimalsNotFloatError(GetSeriesNumberOfDecimalsError):
1460
- dtype: DataType
1461
-
1462
- @override
1463
- def __str__(self) -> str:
1464
- return f"Data type must be Float64; got {self.dtype}"
1465
-
1466
-
1467
- @dataclass(kw_only=True, slots=True)
1468
- class _GetSeriesNumberOfDecimalsAllNullError(GetSeriesNumberOfDecimalsError):
1469
- series: Series
1470
-
1471
- @override
1472
- def __str__(self) -> str:
1473
- return f"Series must not be all-null; got {self.series}"
1474
-
1475
-
1476
- ##
1477
-
1478
-
1479
1429
  @overload
1480
1430
  def increasing_horizontal(*columns: ExprLike) -> Expr: ...
1481
1431
  @overload
@@ -1641,6 +1591,42 @@ def integers(
1641
1591
  ##
1642
1592
 
1643
1593
 
1594
+ @overload
1595
+ def is_close(
1596
+ x: ExprLike, y: ExprLike, /, *, rel_tol: float = 1e-9, abs_tol: float = 0
1597
+ ) -> Expr: ...
1598
+ @overload
1599
+ def is_close(
1600
+ x: Series, y: Series, /, *, rel_tol: float = 1e-9, abs_tol: float = 0
1601
+ ) -> Series: ...
1602
+ @overload
1603
+ def is_close(
1604
+ x: IntoExprColumn,
1605
+ y: IntoExprColumn,
1606
+ /,
1607
+ *,
1608
+ rel_tol: float = 1e-9,
1609
+ abs_tol: float = 0,
1610
+ ) -> ExprOrSeries: ...
1611
+ def is_close(
1612
+ x: IntoExprColumn,
1613
+ y: IntoExprColumn,
1614
+ /,
1615
+ *,
1616
+ rel_tol: float = 1e-9,
1617
+ abs_tol: float = 0,
1618
+ ) -> ExprOrSeries:
1619
+ """Check if two columns are close."""
1620
+ x, y = map(ensure_expr_or_series, [x, y])
1621
+ result = (x - y).abs() <= max_horizontal(
1622
+ rel_tol * max_horizontal(x.abs(), y.abs()), abs_tol
1623
+ )
1624
+ return try_reify_expr(result, x, y)
1625
+
1626
+
1627
+ ##
1628
+
1629
+
1644
1630
  @overload
1645
1631
  def is_near_event(
1646
1632
  *exprs: ExprLike, before: int = 0, after: int = 0, **named_exprs: ExprLike
@@ -1986,6 +1972,26 @@ def normal(
1986
1972
  ##
1987
1973
 
1988
1974
 
1975
+ def number_of_decimals(
1976
+ series: Series, /, *, max_decimals: int = MAX_DECIMALS
1977
+ ) -> Series:
1978
+ """Get the number of decimals."""
1979
+ frac = series - series.floor()
1980
+ results = [
1981
+ _number_of_decimals_check_scale(frac, s) for s in range(max_decimals + 1)
1982
+ ]
1983
+ df_results = concat_series(*results)
1984
+ return first_true_horizontal(df_results)
1985
+
1986
+
1987
+ def _number_of_decimals_check_scale(frac: Series, scale: int, /) -> Series:
1988
+ scaled = 10**scale * frac
1989
+ return is_close(scaled, scaled.round()).alias(str(scale))
1990
+
1991
+
1992
+ ##
1993
+
1994
+
1989
1995
  def offset_datetime(
1990
1996
  datetime: ZonedDateTime, offset: str, /, *, n: int = 1
1991
1997
  ) -> ZonedDateTime:
@@ -2352,7 +2358,7 @@ def round_to_float(
2352
2358
  """Round a column to the nearest multiple of another float."""
2353
2359
  x = ensure_expr_or_series(x)
2354
2360
  z = (x / y).round(mode=mode) * y
2355
- return z.round(decimals=number_of_decimals(y) + 1)
2361
+ return z.round(decimals=utilities.math.number_of_decimals(y) + 1)
2356
2362
 
2357
2363
 
2358
2364
  ##
@@ -2558,7 +2564,6 @@ __all__ = [
2558
2564
  "ExprOrSeries",
2559
2565
  "FiniteEWMMeanError",
2560
2566
  "GetDataTypeOrSeriesTimeZoneError",
2561
- "GetSeriesNumberOfDecimalsError",
2562
2567
  "InsertAfterError",
2563
2568
  "InsertBeforeError",
2564
2569
  "InsertBetweenError",
@@ -2597,12 +2602,12 @@ __all__ = [
2597
2602
  "get_data_type_or_series_time_zone",
2598
2603
  "get_expr_name",
2599
2604
  "get_frequency_spectrum",
2600
- "get_series_number_of_decimals",
2601
2605
  "increasing_horizontal",
2602
2606
  "insert_after",
2603
2607
  "insert_before",
2604
2608
  "insert_between",
2605
2609
  "integers",
2610
+ "is_close",
2606
2611
  "is_false",
2607
2612
  "is_near_event",
2608
2613
  "is_true",
@@ -2612,6 +2617,7 @@ __all__ = [
2612
2617
  "nan_sum_agg",
2613
2618
  "nan_sum_cols",
2614
2619
  "normal",
2620
+ "number_of_decimals",
2615
2621
  "offset_datetime",
2616
2622
  "one_column",
2617
2623
  "order_of_magnitude",
utilities/types.py CHANGED
@@ -79,6 +79,9 @@ type MaybeCoro[T] = T | Coro[T]
79
79
 
80
80
 
81
81
  # collections.abc
82
+ type SupportsFloatOrIndex = SupportsFloat | SupportsIndex
83
+
84
+
82
85
  @runtime_checkable
83
86
  class SupportsKeysAndGetItem(Protocol[_T, _T_co]):
84
87
  def keys(self) -> Iterable[_T]: ... # pragma: no cover
@@ -328,6 +331,7 @@ __all__ = [
328
331
  "SupportsBytes",
329
332
  "SupportsComplex",
330
333
  "SupportsFloat",
334
+ "SupportsFloatOrIndex",
331
335
  "SupportsGT",
332
336
  "SupportsInt",
333
337
  "SupportsInt",