dycw-utilities 0.159.0__py3-none-any.whl → 0.159.1__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.0
3
+ Version: 0.159.1
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=2MGx8NHJpAZDdhMAcNp1DSjsWA7ttyJ5FzMvBsFTXHg,60
1
+ utilities/__init__.py,sha256=Sn6930zXuSe3WGJ5qrzIW92IebaarjXcUx0osmO7Hsk,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
@@ -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=glQ89DyPV9hE1_SFjOyaqhErJ6BT_0NQutDJTZG6UJo,75966
48
+ utilities/polars.py,sha256=qgk9TXHrHodUsgevDbtglYtiZ8jZ-PT8y4EyfU4fk9U,76772
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
@@ -54,7 +54,7 @@ utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
54
54
  utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  utilities/pyinstrument.py,sha256=NZCZz2nBo0BLJ9DTf7H_Q_KGxvsf2S2M3h0qYoYh2kw,804
56
56
  utilities/pytest.py,sha256=M-Om6b3hpF9W_bEB7UFY2IzBCubSxzVQleGrgRXHtxY,7741
57
- utilities/pytest_regressions.py,sha256=8by5DWEL89Y469TI5AzX1pMy3NJWVtjEg2xQdOOdYuM,4169
57
+ utilities/pytest_regressions.py,sha256=ocjHTtfOeiGfQAKIei8pKNd61sxN9dawrJJ9gPt2wzA,4097
58
58
  utilities/random.py,sha256=hZlH4gnAtoaofWswuJYjcygejrY8db4CzP-z_adO2Mo,4165
59
59
  utilities/re.py,sha256=S4h-DLL6ScMPqjboZ_uQ1BVTJajrqV06r_81D--_HCE,4573
60
60
  utilities/redis.py,sha256=pqzl5A08vaRS4Gfjxob3LWWH9c-vwlsKbvVMTjWMSh8,28364
@@ -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.0.dist-info/METADATA,sha256=NZWORLt2CKIbYdJTUllVDUsYVPK9jdxW5yUH5qjN-vc,1643
91
- dycw_utilities-0.159.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
- dycw_utilities-0.159.0.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
93
- dycw_utilities-0.159.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
- dycw_utilities-0.159.0.dist-info/RECORD,,
90
+ dycw_utilities-0.159.1.dist-info/METADATA,sha256=CxCbx_EE_zO3iemn0g8u__516dCh1gbwoQCSZZ7ZEcE,1643
91
+ dycw_utilities-0.159.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
92
+ dycw_utilities-0.159.1.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
93
+ dycw_utilities-0.159.1.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
94
+ dycw_utilities-0.159.1.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.159.0"
3
+ __version__ = "0.159.1"
utilities/polars.py CHANGED
@@ -42,9 +42,9 @@ from polars import (
42
42
  from polars._typing import PolarsDataType
43
43
  from polars.datatypes import DataType, DataTypeClass
44
44
  from polars.exceptions import (
45
- ColumnNotFoundError, # pyright: ignore[reportAttributeAccessIssue]
45
+ ColumnNotFoundError,
46
46
  NoRowsReturnedError,
47
- OutOfBoundsError, # pyright: ignore[reportAttributeAccessIssue]
47
+ OutOfBoundsError,
48
48
  PolarsInefficientMapWarning,
49
49
  )
50
50
  from polars.schema import Schema
@@ -337,8 +337,8 @@ def are_frames_equal(
337
337
  check_column_order: bool = True,
338
338
  check_dtypes: bool = True,
339
339
  check_exact: bool = False,
340
- rtol: float = 1e-5,
341
- atol: float = 1e-8,
340
+ rel_tol: float = 1e-5,
341
+ abs_tol: float = 1e-8,
342
342
  categorical_as_str: bool = False,
343
343
  ) -> bool:
344
344
  """Check if two DataFrames are equal."""
@@ -350,8 +350,8 @@ def are_frames_equal(
350
350
  check_column_order=check_column_order,
351
351
  check_dtypes=check_dtypes,
352
352
  check_exact=check_exact,
353
- rtol=rtol,
354
- atol=atol,
353
+ rel_tol=rel_tol,
354
+ abs_tol=abs_tol,
355
355
  categorical_as_str=categorical_as_str,
356
356
  )
357
357
  except AssertionError:
@@ -783,15 +783,6 @@ def choice(
783
783
  ##
784
784
 
785
785
 
786
- def collect_series(expr: Expr, /) -> Series:
787
- """Collect a column expression into a Series."""
788
- data = DataFrame().with_columns(expr)
789
- return data[one(data.columns)]
790
-
791
-
792
- ##
793
-
794
-
795
786
  def columns_to_dict(df: DataFrame, key: str, value: str, /) -> dict[Any, Any]:
796
787
  """Map a pair of columns into a dictionary. Must be unique on `key`."""
797
788
  col_key = df[key]
@@ -1044,7 +1035,9 @@ def _dataclass_to_dataframe_cast(series: Series, /) -> Series:
1044
1035
  is_path = series.map_elements(make_isinstance(Path), return_dtype=Boolean).all()
1045
1036
  is_uuid = series.map_elements(make_isinstance(UUID), return_dtype=Boolean).all()
1046
1037
  if is_path or is_uuid:
1047
- with suppress_warnings(category=PolarsInefficientMapWarning):
1038
+ with suppress_warnings(
1039
+ category=cast("type[Warning]", PolarsInefficientMapWarning)
1040
+ ):
1048
1041
  return series.map_elements(str, return_dtype=String)
1049
1042
  if series.map_elements(make_isinstance(whenever.Time), return_dtype=Boolean).all():
1050
1043
  return series.map_elements(lambda x: x.py_time(), return_dtype=pl.Time)
@@ -1219,6 +1212,14 @@ def ensure_expr_or_series_many(
1219
1212
  ##
1220
1213
 
1221
1214
 
1215
+ def expr_to_series(expr: Expr, /) -> Series:
1216
+ """Collect a column expression into a Series."""
1217
+ return one_column(DataFrame().with_columns(expr))
1218
+
1219
+
1220
+ ##
1221
+
1222
+
1222
1223
  @overload
1223
1224
  def finite_ewm_mean(
1224
1225
  column: ExprLike,
@@ -1393,8 +1394,7 @@ def get_expr_name(obj: Series | DataFrame, expr: IntoExprColumn, /) -> str:
1393
1394
  case Series() as series:
1394
1395
  return get_expr_name(series.to_frame(), expr)
1395
1396
  case DataFrame() as df:
1396
- selected = df.select(expr)
1397
- return one(selected.columns)
1397
+ return one_column(df.select(expr)).name
1398
1398
  case never:
1399
1399
  assert_never(never)
1400
1400
 
@@ -1989,6 +1989,43 @@ def offset_datetime(
1989
1989
  ##
1990
1990
 
1991
1991
 
1992
+ def one_column(df: DataFrame, /) -> Series:
1993
+ """Return the unique column in a DataFrame."""
1994
+ try:
1995
+ return df[one(df.columns)]
1996
+ except OneEmptyError:
1997
+ raise OneColumnEmptyError(df=df) from None
1998
+ except OneNonUniqueError as error:
1999
+ raise OneColumnNonUniqueError(
2000
+ df=df, first=error.first, second=error.second
2001
+ ) from None
2002
+
2003
+
2004
+ @dataclass(kw_only=True, slots=True)
2005
+ class OneColumnError(Exception):
2006
+ df: DataFrame
2007
+
2008
+
2009
+ @dataclass(kw_only=True, slots=True)
2010
+ class OneColumnEmptyError(OneColumnError):
2011
+ @override
2012
+ def __str__(self) -> str:
2013
+ return "DataFrame must not be empty"
2014
+
2015
+
2016
+ @dataclass(kw_only=True, slots=True)
2017
+ class OneColumnNonUniqueError(OneColumnError):
2018
+ first: str
2019
+ second: str
2020
+
2021
+ @override
2022
+ def __str__(self) -> str:
2023
+ return f"DataFrame must contain exactly one column; got {self.first!r}, {self.second!r} and perhaps more"
2024
+
2025
+
2026
+ ##
2027
+
2028
+
1992
2029
  @overload
1993
2030
  def order_of_magnitude(column: ExprLike, /, *, round_: bool = False) -> Expr: ...
1994
2031
  @overload
@@ -2110,13 +2147,10 @@ def reify_exprs(
2110
2147
  .with_columns(*all_exprs)
2111
2148
  .drop("_index")
2112
2149
  )
2113
- match len(df.columns):
2114
- case 0:
2115
- raise ImpossibleCaseError(case=[f"{df.columns=}"]) # pragma: no cover
2116
- case 1:
2117
- return df[one(df.columns)]
2118
- case _:
2119
- return df
2150
+ try:
2151
+ return one_column(df)
2152
+ except OneColumnNonUniqueError:
2153
+ return df
2120
2154
 
2121
2155
 
2122
2156
  @dataclass(kw_only=True, slots=True)
@@ -2519,6 +2553,9 @@ __all__ = [
2519
2553
  "InsertBeforeError",
2520
2554
  "InsertBetweenError",
2521
2555
  "IsNearEventError",
2556
+ "OneColumnEmptyError",
2557
+ "OneColumnError",
2558
+ "OneColumnNonUniqueError",
2522
2559
  "SetFirstRowAsColumnsError",
2523
2560
  "TimePeriodDType",
2524
2561
  "acf",
@@ -2533,7 +2570,6 @@ __all__ = [
2533
2570
  "boolean_value_counts",
2534
2571
  "check_polars_dataframe",
2535
2572
  "choice",
2536
- "collect_series",
2537
2573
  "columns_to_dict",
2538
2574
  "concat_series",
2539
2575
  "convert_time_zone",
@@ -2545,6 +2581,7 @@ __all__ = [
2545
2581
  "ensure_data_type",
2546
2582
  "ensure_expr_or_series",
2547
2583
  "ensure_expr_or_series_many",
2584
+ "expr_to_series",
2548
2585
  "finite_ewm_mean",
2549
2586
  "get_data_type_or_series_time_zone",
2550
2587
  "get_expr_name",
@@ -2565,6 +2602,7 @@ __all__ = [
2565
2602
  "nan_sum_cols",
2566
2603
  "normal",
2567
2604
  "offset_datetime",
2605
+ "one_column",
2568
2606
  "order_of_magnitude",
2569
2607
  "period_range",
2570
2608
  "read_dataframe",
@@ -91,9 +91,7 @@ class PolarsRegressionFixture:
91
91
  def check(self, obj: Series | DataFrame, /, *, suffix: str | None = None) -> None:
92
92
  """Check the Series/DataFrame summary against the baseline."""
93
93
  from polars import DataFrame, Series, col
94
- from polars.exceptions import (
95
- InvalidOperationError, # pyright: ignore[reportAttributeAccessIssue]
96
- )
94
+ from polars.exceptions import InvalidOperationError
97
95
 
98
96
  data: StrMapping = {
99
97
  "describe": obj.describe(percentiles=[i / 10 for i in range(1, 10)]).rows(