dycw-utilities 0.112.10__tar.gz → 0.112.11__tar.gz

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.
Files changed (228) hide show
  1. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/PKG-INFO +1 -1
  2. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/pyproject.toml +2 -2
  3. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_polars.py +368 -1
  4. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/__init__.py +1 -1
  5. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/polars.py +156 -8
  6. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/.gitignore +0 -0
  7. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/LICENSE +0 -0
  8. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/README.md +0 -0
  9. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/__init__.py +0 -0
  10. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/conftest.py +0 -0
  11. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/__init__.py +0 -0
  12. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_missing/__init__.py +0 -0
  13. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_missing/module.py +0 -0
  14. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/__init__.py +0 -0
  15. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/outer_1.py +0 -0
  16. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/outer_2.py +0 -0
  17. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  18. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  19. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  20. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  21. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_without/__init__.py +0 -0
  22. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_without/module_1.py +0 -0
  23. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/package_without/module_2.py +0 -0
  24. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/standalone.py +0 -0
  25. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/modules/with_imports.py +0 -0
  26. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  27. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  28. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  29. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  30. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  31. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  32. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  33. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  34. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/__init__.py +0 -0
  35. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_async_service/__init__.py +0 -0
  36. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_async_service/__main__.py +0 -0
  37. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_async_service/run.sh +0 -0
  38. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_queue_processor/__init__.py +0 -0
  39. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_queue_processor/__main__.py +0 -0
  40. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/scripts/test_queue_processor/run.sh +0 -0
  41. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_altair.py +0 -0
  42. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_astor.py +0 -0
  43. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_asyncio.py +0 -0
  44. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_atomicwrites.py +0 -0
  45. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_atools.py +0 -0
  46. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_cachetools.py +0 -0
  47. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_click.py +0 -0
  48. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_concurrent.py +0 -0
  49. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_contextlib.py +0 -0
  50. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_contextvars.py +0 -0
  51. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_cryptography.py +0 -0
  52. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_cvxpy.py +0 -0
  53. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_dataclasses.py +0 -0
  54. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_datetime.py +0 -0
  55. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_enum.py +0 -0
  56. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_errors.py +0 -0
  57. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_eventkit.py +0 -0
  58. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_fastapi.py +0 -0
  59. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_fpdf2.py +0 -0
  60. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_functions.py +0 -0
  61. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_functools.py +0 -0
  62. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_getpass.py +0 -0
  63. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_git.py +0 -0
  64. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_hashlib.py +0 -0
  65. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_http.py +0 -0
  66. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_hypothesis.py +0 -0
  67. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_ipython.py +0 -0
  68. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_iterables.py +0 -0
  69. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_jupyter.py +0 -0
  70. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_lightweight_charts.py +0 -0
  71. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_logging.py +0 -0
  72. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_loguru.py +0 -0
  73. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_luigi.py +0 -0
  74. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_math.py +0 -0
  75. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_memory_profiler.py +0 -0
  76. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_modules.py +0 -0
  77. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_more_itertools.py +0 -0
  78. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_numpy.py +0 -0
  79. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_operator.py +0 -0
  80. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_optuna.py +0 -0
  81. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_orjson.py +0 -0
  82. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_os.py +0 -0
  83. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_parse.py +0 -0
  84. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pathlib.py +0 -0
  85. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_period.py +0 -0
  86. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pickle.py +0 -0
  87. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_platform.py +0 -0
  88. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_polars_ols.py +0 -0
  89. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pqdm.py +0 -0
  90. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pydantic.py +0 -0
  91. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pyinstrument.py +0 -0
  92. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pyrsistent.py +0 -0
  93. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pytest.py +0 -0
  94. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_pytest_regressions.py +0 -0
  95. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_python_dotenv.py +0 -0
  96. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_random.py +0 -0
  97. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_re.py +0 -0
  98. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_redis.py +0 -0
  99. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_reprlib.py +0 -0
  100. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_rich.py +0 -0
  101. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_scipy.py +0 -0
  102. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_sentinel.py +0 -0
  103. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_shelve.py +0 -0
  104. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_slack_sdk.py +0 -0
  105. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_socket.py +0 -0
  106. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_sqlalchemy.py +0 -0
  107. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_sqlalchemy_polars.py +0 -0
  108. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_statsmodel.py +0 -0
  109. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_streamlit.py +0 -0
  110. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_sys.py +0 -0
  111. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_tempfile.py +0 -0
  112. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_tenacity.py +0 -0
  113. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_text.py +0 -0
  114. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_threading.py +0 -0
  115. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_timer.py +0 -0
  116. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback.py +0 -0
  117. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/__init__.py +0 -0
  118. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/chain.py +0 -0
  119. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  120. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  121. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  122. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/many.py +0 -0
  123. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/one.py +0 -0
  124. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/recursive.py +0 -0
  125. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  126. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  127. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/two.py +0 -0
  128. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_traceback_funcs/untraced.py +0 -0
  129. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_types.py +0 -0
  130. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_typing.py +0 -0
  131. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_typing_funcs/__init__.py +0 -0
  132. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_typing_funcs/no_future.py +0 -0
  133. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_typing_funcs/with_future.py +0 -0
  134. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_tzdata.py +0 -0
  135. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_tzlocal.py +0 -0
  136. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_uuid.py +0 -0
  137. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_version.py +0 -0
  138. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_warnings.py +0 -0
  139. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_whenever.py +0 -0
  140. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_zipfile.py +0 -0
  141. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/tests/test_zoneinfo.py +0 -0
  142. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/altair.py +0 -0
  143. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/astor.py +0 -0
  144. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/asyncio.py +0 -0
  145. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/atomicwrites.py +0 -0
  146. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/atools.py +0 -0
  147. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/cachetools.py +0 -0
  148. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/click.py +0 -0
  149. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/concurrent.py +0 -0
  150. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/contextlib.py +0 -0
  151. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/contextvars.py +0 -0
  152. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/cryptography.py +0 -0
  153. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/cvxpy.py +0 -0
  154. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/dataclasses.py +0 -0
  155. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/datetime.py +0 -0
  156. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/enum.py +0 -0
  157. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/errors.py +0 -0
  158. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/eventkit.py +0 -0
  159. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/fastapi.py +0 -0
  160. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/fpdf2.py +0 -0
  161. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/functions.py +0 -0
  162. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/functools.py +0 -0
  163. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/getpass.py +0 -0
  164. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/git.py +0 -0
  165. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/hashlib.py +0 -0
  166. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/http.py +0 -0
  167. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/hypothesis.py +0 -0
  168. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/ipython.py +0 -0
  169. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/iterables.py +0 -0
  170. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/jupyter.py +0 -0
  171. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/lightweight_charts.py +0 -0
  172. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/logging.py +0 -0
  173. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/loguru.py +0 -0
  174. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/luigi.py +0 -0
  175. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/math.py +0 -0
  176. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/memory_profiler.py +0 -0
  177. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/modules.py +0 -0
  178. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/more_itertools.py +0 -0
  179. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/numpy.py +0 -0
  180. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/operator.py +0 -0
  181. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/optuna.py +0 -0
  182. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/orjson.py +0 -0
  183. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/os.py +0 -0
  184. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/parse.py +0 -0
  185. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pathlib.py +0 -0
  186. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/period.py +0 -0
  187. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pickle.py +0 -0
  188. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/platform.py +0 -0
  189. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/polars_ols.py +0 -0
  190. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pqdm.py +0 -0
  191. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/py.typed +0 -0
  192. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pydantic.py +0 -0
  193. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pyinstrument.py +0 -0
  194. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pyrsistent.py +0 -0
  195. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pytest.py +0 -0
  196. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/pytest_regressions.py +0 -0
  197. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/python_dotenv.py +0 -0
  198. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/random.py +0 -0
  199. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/re.py +0 -0
  200. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/redis.py +0 -0
  201. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/reprlib.py +0 -0
  202. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/rich.py +0 -0
  203. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/scipy.py +0 -0
  204. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/sentinel.py +0 -0
  205. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/shelve.py +0 -0
  206. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/slack_sdk.py +0 -0
  207. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/socket.py +0 -0
  208. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/sqlalchemy.py +0 -0
  209. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/sqlalchemy_polars.py +0 -0
  210. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/statsmodels.py +0 -0
  211. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/streamlit.py +0 -0
  212. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/sys.py +0 -0
  213. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/tempfile.py +0 -0
  214. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/tenacity.py +0 -0
  215. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/text.py +0 -0
  216. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/threading.py +0 -0
  217. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/timer.py +0 -0
  218. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/traceback.py +0 -0
  219. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/types.py +0 -0
  220. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/tzdata.py +0 -0
  222. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/tzlocal.py +0 -0
  223. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/uuid.py +0 -0
  224. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/version.py +0 -0
  225. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/warnings.py +0 -0
  226. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/whenever.py +0 -0
  227. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/zipfile.py +0 -0
  228. {dycw_utilities-0.112.10 → dycw_utilities-0.112.11}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.112.10
3
+ Version: 0.112.11
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -93,7 +93,7 @@ dependencies = [
93
93
  name = "dycw-utilities"
94
94
  readme = "README.md"
95
95
  requires-python = ">= 3.12"
96
- version = "0.112.10"
96
+ version = "0.112.11"
97
97
 
98
98
  [project.optional-dependencies]
99
99
  test = [
@@ -336,7 +336,7 @@ zzz-test-zoneinfo = [
336
336
  # bump-my-version
337
337
  [tool.bumpversion]
338
338
  allow_dirty = true
339
- current_version = "0.112.10"
339
+ current_version = "0.112.11"
340
340
 
341
341
  [[tool.bumpversion.files]]
342
342
  filename = "src/utilities/__init__.py"
@@ -5,7 +5,7 @@ import enum
5
5
  import itertools
6
6
  from dataclasses import dataclass, field
7
7
  from enum import auto
8
- from itertools import chain
8
+ from itertools import chain, repeat
9
9
  from math import isfinite, nan
10
10
  from pathlib import Path
11
11
  from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
@@ -49,6 +49,7 @@ from polars import (
49
49
  datetime_range,
50
50
  int_range,
51
51
  lit,
52
+ struct,
52
53
  )
53
54
  from polars._typing import (
54
55
  IntoExprColumn, # pyright: ignore[reportPrivateImportUsage]
@@ -112,6 +113,10 @@ from utilities.polars import (
112
113
  _GetSeriesNumberOfDecimalsNotFloatError,
113
114
  _InsertBetweenMissingColumnsError,
114
115
  _InsertBetweenNonConsecutiveError,
116
+ _IsNearEventAfterError,
117
+ _IsNearEventBeforeError,
118
+ _ReifyExprsEmptyError,
119
+ _ReifyExprsSeriesNonUniqueError,
115
120
  _yield_struct_series_element_remove_nulls,
116
121
  ac_halflife,
117
122
  acf,
@@ -144,6 +149,7 @@ from utilities.polars import (
144
149
  insert_after,
145
150
  insert_before,
146
151
  insert_between,
152
+ is_near_event,
147
153
  is_not_null_struct_series,
148
154
  is_null_struct_series,
149
155
  join,
@@ -151,11 +157,13 @@ from utilities.polars import (
151
157
  nan_sum_agg,
152
158
  nan_sum_cols,
153
159
  normal,
160
+ reify_exprs,
154
161
  replace_time_zone,
155
162
  set_first_row_as_columns,
156
163
  struct_dtype,
157
164
  struct_from_dataclass,
158
165
  touch,
166
+ try_reify_expr,
159
167
  uniform,
160
168
  unique_element,
161
169
  week_num,
@@ -1639,6 +1647,102 @@ class TestIntegers:
1639
1647
  assert series.is_between(0, high, closed="left").all()
1640
1648
 
1641
1649
 
1650
+ class TestIsNearEvent:
1651
+ df: ClassVar[DataFrame] = DataFrame(
1652
+ data=[
1653
+ (False, False),
1654
+ (False, False),
1655
+ (True, False),
1656
+ (True, False),
1657
+ (False, False),
1658
+ (False, False),
1659
+ (False, False),
1660
+ (False, False),
1661
+ (False, False),
1662
+ (False, True),
1663
+ ],
1664
+ schema={"x": Boolean, "y": Boolean},
1665
+ orient="row",
1666
+ )
1667
+
1668
+ def test_no_exprs(self) -> None:
1669
+ result = self.df.with_columns(is_near_event().alias("z"))["z"]
1670
+ expected = Series(
1671
+ name="z", values=list(repeat(object=False, times=10)), dtype=Boolean
1672
+ )
1673
+ assert_series_equal(result, expected)
1674
+
1675
+ def test_x(self) -> None:
1676
+ result = self.df.with_columns(is_near_event("x").alias("z"))["z"]
1677
+ expected = Series(
1678
+ name="z",
1679
+ values=[False, False, True, True, False, False, False, False, False, False],
1680
+ dtype=Boolean,
1681
+ )
1682
+ assert_series_equal(result, expected)
1683
+
1684
+ def test_y(self) -> None:
1685
+ result = self.df.with_columns(is_near_event("y").alias("z"))["z"]
1686
+ expected = Series(
1687
+ name="z",
1688
+ values=[
1689
+ False,
1690
+ False,
1691
+ False,
1692
+ False,
1693
+ False,
1694
+ False,
1695
+ False,
1696
+ False,
1697
+ False,
1698
+ True,
1699
+ ],
1700
+ dtype=Boolean,
1701
+ )
1702
+ assert_series_equal(result, expected)
1703
+
1704
+ def test_x_before(self) -> None:
1705
+ result = self.df.with_columns(is_near_event("x", before=1).alias("z"))["z"]
1706
+ expected = Series(
1707
+ name="z",
1708
+ values=[False, True, True, True, False, False, False, False, False, False],
1709
+ dtype=Boolean,
1710
+ )
1711
+ assert_series_equal(result, expected)
1712
+
1713
+ def test_x_after(self) -> None:
1714
+ result = self.df.with_columns(is_near_event("x", after=1).alias("z"))["z"]
1715
+ expected = Series(
1716
+ name="z",
1717
+ values=[False, False, True, True, True, False, False, False, False, False],
1718
+ dtype=Boolean,
1719
+ )
1720
+ assert_series_equal(result, expected)
1721
+
1722
+ def test_x_or_y(self) -> None:
1723
+ result = self.df.with_columns(is_near_event("x", "y").alias("z"))["z"]
1724
+ expected = Series(
1725
+ name="z",
1726
+ values=[False, False, True, True, False, False, False, False, False, True],
1727
+ dtype=Boolean,
1728
+ )
1729
+ assert_series_equal(result, expected)
1730
+
1731
+ @given(before=hypothesis.strategies.integers(max_value=-1))
1732
+ def test_error_before(self, *, before: int) -> None:
1733
+ with raises(
1734
+ _IsNearEventBeforeError, match=r"'Before' must be non-negative; got \-\d+"
1735
+ ):
1736
+ _ = is_near_event(before=before)
1737
+
1738
+ @given(after=hypothesis.strategies.integers(max_value=-1))
1739
+ def test_error_after(self, *, after: int) -> None:
1740
+ with raises(
1741
+ _IsNearEventAfterError, match=r"'After' must be non-negative; got \-\d+"
1742
+ ):
1743
+ _ = is_near_event(after=after)
1744
+
1745
+
1642
1746
  class TestIsNullAndIsNotNullStructSeries:
1643
1747
  @given(
1644
1748
  case=sampled_from([
@@ -1877,6 +1981,98 @@ class TestNormal:
1877
1981
  assert series.is_finite().all()
1878
1982
 
1879
1983
 
1984
+ class TestReifyExprs:
1985
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
1986
+ def test_one_expr(self, *, length: int, name: str) -> None:
1987
+ expr = int_range(end=length).alias(name)
1988
+ result = reify_exprs(expr)
1989
+ assert isinstance(result, Expr)
1990
+ result2 = (
1991
+ int_range(end=length, eager=True)
1992
+ .alias(f"_{name}")
1993
+ .to_frame()
1994
+ .with_columns(result)[name]
1995
+ )
1996
+ expected = int_range(end=length, eager=True).alias(name)
1997
+ assert_series_equal(result2, expected)
1998
+
1999
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
2000
+ def test_one_series(self, *, length: int, name: str) -> None:
2001
+ series = int_range(end=length, eager=True).alias(name)
2002
+ result = reify_exprs(series)
2003
+ assert isinstance(result, Series)
2004
+ assert_series_equal(result, series)
2005
+
2006
+ @given(
2007
+ length=hypothesis.strategies.integers(0, 10),
2008
+ names=pairs(text_ascii(), unique=True),
2009
+ )
2010
+ def test_two_exprs(self, *, length: int, names: tuple[str, str]) -> None:
2011
+ name1, name2 = names
2012
+ expr1 = int_range(end=length).alias(name1)
2013
+ expr2 = int_range(end=length).alias(name2)
2014
+ result = reify_exprs(expr1, expr2)
2015
+ assert isinstance(result, Expr)
2016
+ result2 = (
2017
+ int_range(end=length, eager=True)
2018
+ .alias(f"_{names}")
2019
+ .to_frame()
2020
+ .with_columns(result)[name1]
2021
+ )
2022
+ assert result2.name == name1
2023
+ assert result2.dtype == Struct(dict.fromkeys(names, Int64))
2024
+
2025
+ @given(
2026
+ length=hypothesis.strategies.integers(0, 10),
2027
+ names=pairs(text_ascii(), unique=True),
2028
+ )
2029
+ def test_one_expr_and_one_series(
2030
+ self, *, length: int, names: tuple[str, str]
2031
+ ) -> None:
2032
+ name1, name2 = names
2033
+ expr = int_range(end=length).alias(name1)
2034
+ series = int_range(end=length, eager=True).alias(name2)
2035
+ result = reify_exprs(expr, series)
2036
+ assert isinstance(result, DataFrame)
2037
+ assert result.schema == dict.fromkeys(names, Int64)
2038
+
2039
+ @given(
2040
+ length=hypothesis.strategies.integers(0, 10),
2041
+ names=pairs(text_ascii(), unique=True),
2042
+ )
2043
+ def test_two_series(self, *, length: int, names: tuple[str, str]) -> None:
2044
+ name1, name2 = names
2045
+ series1 = int_range(end=length, eager=True).alias(name1)
2046
+ series2 = int_range(end=length, eager=True).alias(name2)
2047
+ result = reify_exprs(series1, series2)
2048
+ assert isinstance(result, DataFrame)
2049
+ expected = concat_series(series1, series2)
2050
+ assert_frame_equal(result, expected)
2051
+
2052
+ def test_error_empty(self) -> None:
2053
+ with raises(
2054
+ _ReifyExprsEmptyError, match="At least 1 Expression or Series must be given"
2055
+ ):
2056
+ _ = reify_exprs()
2057
+
2058
+ @given(
2059
+ lengths=pairs(hypothesis.strategies.integers(0, 10), unique=True),
2060
+ names=pairs(text_ascii(), unique=True),
2061
+ )
2062
+ def test_error_non_unique(
2063
+ self, *, lengths: tuple[int, int], names: tuple[str, str]
2064
+ ) -> None:
2065
+ series1, series2 = [
2066
+ int_range(end=length, eager=True).alias(name)
2067
+ for length, name in zip(lengths, names, strict=True)
2068
+ ]
2069
+ with raises(
2070
+ _ReifyExprsSeriesNonUniqueError,
2071
+ match=r"Series must contain exactly one length; got \d+, \d+ and perhaps more",
2072
+ ):
2073
+ _ = reify_exprs(series1, series2)
2074
+
2075
+
1880
2076
  class TestReplaceTimeZone:
1881
2077
  def test_datetime(self) -> None:
1882
2078
  now_utc = get_now()
@@ -2062,6 +2258,177 @@ class TestStructFromDataClass:
2062
2258
  _ = struct_from_dataclass(Example)
2063
2259
 
2064
2260
 
2261
+ class TestTryReifyExpr:
2262
+ # expr
2263
+
2264
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
2265
+ def test_flat_expr(self, *, length: int, name: str) -> None:
2266
+ expr = int_range(end=length).alias(name)
2267
+ result = try_reify_expr(expr)
2268
+ assert isinstance(result, Expr)
2269
+ result2 = (
2270
+ int_range(end=length, eager=True)
2271
+ .alias(f"_{name}")
2272
+ .to_frame()
2273
+ .with_columns(result)[name]
2274
+ )
2275
+ expected = int_range(end=length, eager=True).alias(name)
2276
+ assert_series_equal(result2, expected)
2277
+
2278
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2279
+ def test_flat_expr_and_expr(self, *, length: int, names: tuple[str, str]) -> None:
2280
+ name1, name2 = names
2281
+ expr1 = int_range(end=length).alias(name1)
2282
+ expr2 = int_range(end=length).alias(name2)
2283
+ result = try_reify_expr(expr1, expr2)
2284
+ assert isinstance(result, Expr)
2285
+ result2 = (
2286
+ int_range(end=length, eager=True)
2287
+ .alias(f"_{name1}")
2288
+ .to_frame()
2289
+ .with_columns(result)[name1]
2290
+ )
2291
+ expected = int_range(end=length, eager=True).alias(name1)
2292
+ assert_series_equal(result2, expected)
2293
+
2294
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2295
+ def test_flat_expr_and_series(self, *, length: int, names: tuple[str, str]) -> None:
2296
+ name1, name2 = names
2297
+ expr = int_range(end=length).alias(name1)
2298
+ series = int_range(end=length, eager=True).alias(name2)
2299
+ result = try_reify_expr(expr, series)
2300
+ assert isinstance(result, Series)
2301
+ assert_series_equal(result, series.alias(name1))
2302
+
2303
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
2304
+ def test_struct_expr(self, *, length: int, name: str) -> None:
2305
+ expr = struct(int_range(end=length).alias(name)).alias(name)
2306
+ result = try_reify_expr(expr)
2307
+ assert isinstance(result, Expr)
2308
+ result2 = (
2309
+ int_range(end=length, eager=True)
2310
+ .alias(f"_{name}")
2311
+ .to_frame()
2312
+ .with_columns(result)[name]
2313
+ )
2314
+ expected = (
2315
+ int_range(end=length, eager=True)
2316
+ .alias(name)
2317
+ .to_frame()
2318
+ .select(struct(name))[name]
2319
+ )
2320
+ assert_series_equal(result2, expected)
2321
+
2322
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2323
+ def test_struct_expr_and_expr(self, *, length: int, names: tuple[str, str]) -> None:
2324
+ name1, name2 = names
2325
+ expr1 = struct(int_range(end=length).alias(name1)).alias(name1)
2326
+ expr2 = int_range(end=length).alias(name2)
2327
+ result = try_reify_expr(expr1, expr2)
2328
+ assert isinstance(result, Expr)
2329
+ result2 = (
2330
+ int_range(end=length, eager=True)
2331
+ .alias(f"_{name1}")
2332
+ .to_frame()
2333
+ .with_columns(result)[name1]
2334
+ )
2335
+ expected = (
2336
+ int_range(end=length, eager=True)
2337
+ .alias(name1)
2338
+ .to_frame()
2339
+ .select(struct(name1))[name1]
2340
+ )
2341
+ assert_series_equal(result2, expected)
2342
+
2343
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2344
+ def test_struct_expr_and_series(
2345
+ self, *, length: int, names: tuple[str, str]
2346
+ ) -> None:
2347
+ name1, name2 = names
2348
+ expr = struct(int_range(end=length).alias(name1)).alias(name1)
2349
+ series = int_range(end=length, eager=True).alias(name2)
2350
+ result = try_reify_expr(expr, series)
2351
+ assert isinstance(result, Series)
2352
+ expected = series.alias(name1).to_frame().select(struct(name1))[name1]
2353
+ assert_series_equal(result, expected)
2354
+
2355
+ # series
2356
+
2357
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
2358
+ def test_flat_series(self, *, length: int, name: str) -> None:
2359
+ series = int_range(end=length, eager=True).alias(name)
2360
+ result = try_reify_expr(series)
2361
+ assert isinstance(result, Series)
2362
+ assert_series_equal(result, series)
2363
+
2364
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2365
+ def test_flat_series_and_expr(self, *, length: int, names: tuple[str, str]) -> None:
2366
+ name1, name2 = names
2367
+ series = int_range(end=length, eager=True).alias(name1)
2368
+ expr = int_range(end=length).alias(name2)
2369
+ result = try_reify_expr(series, expr)
2370
+ assert isinstance(result, Series)
2371
+ assert_series_equal(result, series)
2372
+
2373
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2374
+ def test_flat_series_and_series(
2375
+ self, *, length: int, names: tuple[str, str]
2376
+ ) -> None:
2377
+ name1, name2 = names
2378
+ series1 = int_range(end=length, eager=True).alias(name1)
2379
+ series2 = int_range(end=length, eager=True).alias(name2)
2380
+ result = try_reify_expr(series1, series2)
2381
+ assert isinstance(result, Series)
2382
+ assert_series_equal(result, series1)
2383
+
2384
+ @given(length=hypothesis.strategies.integers(0, 10), name=text_ascii())
2385
+ def test_struct_series(self, *, length: int, name: str) -> None:
2386
+ series = (
2387
+ int_range(end=length, eager=True)
2388
+ .alias(name)
2389
+ .to_frame()
2390
+ .select(struct(name))[name]
2391
+ )
2392
+ assert isinstance(series.dtype, Struct)
2393
+ result = try_reify_expr(series)
2394
+ assert isinstance(result, Series)
2395
+ assert_series_equal(result, series)
2396
+
2397
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2398
+ def test_struct_series_and_expr(
2399
+ self, *, length: int, names: tuple[str, str]
2400
+ ) -> None:
2401
+ name1, name2 = names
2402
+ series = (
2403
+ int_range(end=length, eager=True)
2404
+ .alias(name1)
2405
+ .to_frame()
2406
+ .select(struct(name1))[name1]
2407
+ )
2408
+ assert isinstance(series.dtype, Struct)
2409
+ expr = int_range(end=length).alias(name2)
2410
+ result = try_reify_expr(series, expr)
2411
+ assert isinstance(result, Series)
2412
+ assert_series_equal(result, series)
2413
+
2414
+ @given(length=hypothesis.strategies.integers(0, 10), names=pairs(text_ascii()))
2415
+ def test_struct_series_and_series(
2416
+ self, *, length: int, names: tuple[str, str]
2417
+ ) -> None:
2418
+ name1, name2 = names
2419
+ series1 = (
2420
+ int_range(end=length, eager=True)
2421
+ .alias(name1)
2422
+ .to_frame()
2423
+ .select(struct(name1))[name1]
2424
+ )
2425
+ assert isinstance(series1.dtype, Struct)
2426
+ series2 = int_range(end=length).alias(name2)
2427
+ result = try_reify_expr(series1, series2)
2428
+ assert isinstance(result, Series)
2429
+ assert_series_equal(result, series1)
2430
+
2431
+
2065
2432
  class TestUniform:
2066
2433
  @given(
2067
2434
  length=hypothesis.strategies.integers(0, 10),
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.112.10"
3
+ __version__ = "0.112.11"
@@ -7,7 +7,7 @@ from collections.abc import Set as AbstractSet
7
7
  from contextlib import suppress
8
8
  from dataclasses import asdict, dataclass
9
9
  from functools import partial, reduce
10
- from itertools import chain
10
+ from itertools import chain, product
11
11
  from math import ceil, log
12
12
  from pathlib import Path
13
13
  from typing import (
@@ -41,6 +41,7 @@ from polars import (
41
41
  Struct,
42
42
  UInt32,
43
43
  all_horizontal,
44
+ any_horizontal,
44
45
  col,
45
46
  concat,
46
47
  int_range,
@@ -1265,13 +1266,7 @@ def finite_ewm_mean(
1265
1266
  column = ensure_expr_or_series(column)
1266
1267
  mean = column.fill_null(value=0.0).rolling_mean(len(weights), weights=list(weights))
1267
1268
  expr = when(column.is_not_null()).then(mean)
1268
- match column:
1269
- case Expr():
1270
- return expr
1271
- case Series() as series:
1272
- return series.to_frame().with_columns(expr.alias(series.name))[series.name]
1273
- case _ as never:
1274
- assert_never(never)
1269
+ return try_reify_expr(expr, column)
1275
1270
 
1276
1271
 
1277
1272
  @dataclass(kw_only=True)
@@ -1599,6 +1594,69 @@ def integers(
1599
1594
  ##
1600
1595
 
1601
1596
 
1597
+ @overload
1598
+ def is_near_event(
1599
+ *exprs: ExprLike, before: int = 0, after: int = 0, **named_exprs: ExprLike
1600
+ ) -> Expr: ...
1601
+ @overload
1602
+ def is_near_event(
1603
+ *exprs: Series, before: int = 0, after: int = 0, **named_exprs: Series
1604
+ ) -> Series: ...
1605
+ @overload
1606
+ def is_near_event(
1607
+ *exprs: IntoExprColumn,
1608
+ before: int = 0,
1609
+ after: int = 0,
1610
+ **named_exprs: IntoExprColumn,
1611
+ ) -> Expr | Series: ...
1612
+ def is_near_event(
1613
+ *exprs: IntoExprColumn,
1614
+ before: int = 0,
1615
+ after: int = 0,
1616
+ **named_exprs: IntoExprColumn,
1617
+ ) -> Expr | Series:
1618
+ """Compute the rows near any event."""
1619
+ if before <= -1:
1620
+ raise _IsNearEventBeforeError(before=before)
1621
+ if after <= -1:
1622
+ raise _IsNearEventAfterError(after=after)
1623
+ all_exprs = ensure_expr_or_series_many(*exprs, **named_exprs)
1624
+ shifts = range(-before, after + 1)
1625
+ if len(all_exprs) == 0:
1626
+ near = lit(value=False, dtype=Boolean)
1627
+ else:
1628
+ near_exprs = (
1629
+ e.shift(s).fill_null(value=False) for e, s in product(all_exprs, shifts)
1630
+ )
1631
+ near = any_horizontal(*near_exprs)
1632
+ return try_reify_expr(near, *exprs, **named_exprs)
1633
+
1634
+
1635
+ @dataclass(kw_only=True, slots=True)
1636
+ class IsNearEventError(Exception): ...
1637
+
1638
+
1639
+ @dataclass(kw_only=True, slots=True)
1640
+ class _IsNearEventBeforeError(IsNearEventError):
1641
+ before: int
1642
+
1643
+ @override
1644
+ def __str__(self) -> str:
1645
+ return f"'Before' must be non-negative; got {self.before}"
1646
+
1647
+
1648
+ @dataclass(kw_only=True, slots=True)
1649
+ class _IsNearEventAfterError(IsNearEventError):
1650
+ after: int
1651
+
1652
+ @override
1653
+ def __str__(self) -> str:
1654
+ return f"'After' must be non-negative; got {self.after}"
1655
+
1656
+
1657
+ ##
1658
+
1659
+
1602
1660
  def is_not_null_struct_series(series: Series, /) -> Series:
1603
1661
  """Check if a struct-dtype Series is not null as per the <= 1.1 definition."""
1604
1662
  try:
@@ -1791,6 +1849,71 @@ def normal(
1791
1849
  ##
1792
1850
 
1793
1851
 
1852
+ def reify_exprs(
1853
+ *exprs: IntoExprColumn, **named_exprs: IntoExprColumn
1854
+ ) -> Expr | Series | DataFrame:
1855
+ """Reify a set of expressions."""
1856
+ all_exprs = ensure_expr_or_series_many(*exprs, **named_exprs)
1857
+ if len(all_exprs) == 0:
1858
+ raise _ReifyExprsEmptyError from None
1859
+ series = [s for s in all_exprs if isinstance(s, Series)]
1860
+ lengths = {s.len() for s in series}
1861
+ try:
1862
+ length = one(lengths)
1863
+ except OneEmptyError:
1864
+ match len(all_exprs):
1865
+ case 0:
1866
+ raise ImpossibleCaseError(
1867
+ case=[f"{all_exprs=}"]
1868
+ ) from None # pragma: no cover
1869
+ case 1:
1870
+ return one(all_exprs)
1871
+ case _:
1872
+ return struct(*all_exprs)
1873
+ except OneNonUniqueError as error:
1874
+ raise _ReifyExprsSeriesNonUniqueError(
1875
+ first=error.first, second=error.second
1876
+ ) from None
1877
+ df = (
1878
+ int_range(end=length, eager=True)
1879
+ .alias("_index")
1880
+ .to_frame()
1881
+ .with_columns(*all_exprs)
1882
+ .drop("_index")
1883
+ )
1884
+ match len(df.columns):
1885
+ case 0:
1886
+ raise ImpossibleCaseError(case=[f"{df.columns=}"]) # pragma: no cover
1887
+ case 1:
1888
+ return df[one(df.columns)]
1889
+ case _:
1890
+ return df
1891
+
1892
+
1893
+ @dataclass(kw_only=True, slots=True)
1894
+ class ReifyExprsError(Exception): ...
1895
+
1896
+
1897
+ @dataclass(kw_only=True, slots=True)
1898
+ class _ReifyExprsEmptyError(ReifyExprsError):
1899
+ @override
1900
+ def __str__(self) -> str:
1901
+ return "At least 1 Expression or Series must be given"
1902
+
1903
+
1904
+ @dataclass
1905
+ class _ReifyExprsSeriesNonUniqueError(ReifyExprsError):
1906
+ first: int
1907
+ second: int
1908
+
1909
+ @override
1910
+ def __str__(self) -> str:
1911
+ return f"Series must contain exactly one length; got {self.first}, {self.second} and perhaps more"
1912
+
1913
+
1914
+ ##
1915
+
1916
+
1794
1917
  @overload
1795
1918
  def replace_time_zone(
1796
1919
  obj: Series, /, *, time_zone: TimeZoneLike | None = UTC
@@ -1930,6 +2053,28 @@ class _StructFromDataClassTypeError(StructFromDataClassError):
1930
2053
  ##
1931
2054
 
1932
2055
 
2056
+ def try_reify_expr(
2057
+ expr: IntoExprColumn, /, *exprs: IntoExprColumn, **named_exprs: IntoExprColumn
2058
+ ) -> Expr | Series:
2059
+ """Try reify an expression."""
2060
+ expr = ensure_expr_or_series(expr)
2061
+ all_exprs = ensure_expr_or_series_many(*exprs, **named_exprs)
2062
+ all_exprs = [e.alias(f"_{i}") for i, e in enumerate(all_exprs)]
2063
+ result = reify_exprs(expr, *all_exprs)
2064
+ match result:
2065
+ case Expr():
2066
+ return expr
2067
+ case Series() as series:
2068
+ return series
2069
+ case DataFrame() as df:
2070
+ return df[get_expr_name(df, expr)]
2071
+ case _ as never:
2072
+ assert_never(never)
2073
+
2074
+
2075
+ ##
2076
+
2077
+
1933
2078
  def uniform(
1934
2079
  obj: int | Series | DataFrame,
1935
2080
  /,
@@ -2123,6 +2268,7 @@ __all__ = [
2123
2268
  "InsertAfterError",
2124
2269
  "InsertBeforeError",
2125
2270
  "InsertBetweenError",
2271
+ "IsNearEventError",
2126
2272
  "IsNullStructSeriesError",
2127
2273
  "SetFirstRowAsColumnsError",
2128
2274
  "StructFromDataClassError",
@@ -2157,6 +2303,7 @@ __all__ = [
2157
2303
  "insert_before",
2158
2304
  "insert_between",
2159
2305
  "integers",
2306
+ "is_near_event",
2160
2307
  "is_not_null_struct_series",
2161
2308
  "is_null_struct_series",
2162
2309
  "join",
@@ -2169,6 +2316,7 @@ __all__ = [
2169
2316
  "struct_dtype",
2170
2317
  "struct_from_dataclass",
2171
2318
  "touch",
2319
+ "try_reify_expr",
2172
2320
  "uniform",
2173
2321
  "unique_element",
2174
2322
  "yield_struct_series_dataclasses",