diff --git a/.gitignore b/.gitignore index d88e5dd..2e2316f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /pyo3-polars/target -Cargo.lock .idea/ venv/ target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f211f38 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1938 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "argminmax" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52424b59d69d69d5056d508b260553afd91c57e21849579cd1f50ee8b8b88eaa" +dependencies = [ + "num-traits", +] + +[[package]] +name = "array-init-cursor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7d0a018de4f6aa429b9d33d69edf69072b1c5b1cb8d3e4a5f7ef898fc3eb76" + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atoi_simd" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "pure-rust-locales", + "windows-targets", +] + +[[package]] +name = "chrono-tz" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" +dependencies = [ + "chrono", + "chrono-tz-build 0.2.1", + "phf", +] + +[[package]] +name = "chrono-tz" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +dependencies = [ + "chrono", + "chrono-tz-build 0.3.0", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "chrono-tz-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "comfy-table" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" +dependencies = [ + "crossterm", + "strum", + "strum_macros", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "parking_lot", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast-float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" + +[[package]] +name = "foreign_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1b05cbd864bcaecbd3455d6d967862d446e4ebfc3c2e5e5b9841e53cba6673" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "rayon", + "serde", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "iter-read" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c397ca3ea05ad509c4ec451fea28b4771236a376ca1c69fd5143aae0cf8f93c4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "itoap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lz4" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eab492fe7f8651add23237ea56dbf11b3c4ff762ab83d40a47f11433421f91" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9764018d143cc854c9f17f0b907de70f14393b1f502da6375dce70f00514eb3" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "multiversion" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4851161a11d3ad0bf9402d90ffc3967bf231768bfd7aeb61755ad06dbf1a142" +dependencies = [ + "multiversion-macros", + "target-features", +] + +[[package]] +name = "multiversion-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a74ddee9e0c27d2578323c13905793e91622148f138ba29738f9dddb835e90" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "target-features", +] + +[[package]] +name = "now" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0" +dependencies = [ + "chrono", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "parquet-format-safe" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1131c54b167dd4e4799ce762e1ab01549ebb94d5bdd13e6ec1b467491c378e1f" + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "planus" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1691dd09e82f428ce8d6310bd6d5da2557c82ff17694d2a32cad7242aea89f" +dependencies = [ + "array-init-cursor", +] + +[[package]] +name = "polars" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce49e10a756f68eb99c102c6b2a0cbc0c583a0fa7263536ad0913d94be878d2d" +dependencies = [ + "getrandom", + "polars-arrow", + "polars-core", + "polars-error", + "polars-io", + "polars-lazy", + "polars-ops", + "polars-parquet", + "polars-sql", + "polars-time", + "polars-utils", + "version_check", +] + +[[package]] +name = "polars-arrow" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b436f83f62e864f0d91871e26528f2c5552c7cf07c8d77547f1b8e3fde22bd27" +dependencies = [ + "ahash", + "atoi", + "atoi_simd", + "bytemuck", + "chrono", + "chrono-tz 0.8.6", + "dyn-clone", + "either", + "ethnum", + "fast-float", + "foreign_vec", + "getrandom", + "hashbrown", + "itoa", + "itoap", + "lz4", + "multiversion", + "num-traits", + "polars-arrow-format", + "polars-error", + "polars-utils", + "ryu", + "simdutf8", + "streaming-iterator", + "strength_reduce", + "version_check", + "zstd", +] + +[[package]] +name = "polars-arrow-format" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b0ef2474af9396b19025b189d96e992311e6a47f90c53cd998b36c4c64b84c" +dependencies = [ + "planus", + "serde", +] + +[[package]] +name = "polars-compute" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6758f834f07e622a2f859bebb542b2b7f8879b8704dbb2b2bbab460ddcdca4b" +dependencies = [ + "bytemuck", + "either", + "num-traits", + "polars-arrow", + "polars-error", + "polars-utils", + "strength_reduce", + "version_check", +] + +[[package]] +name = "polars-core" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ed262e9bdda15a12a9bfcfc9200bec5253335633dbd86cf5b94fda0194244b3" +dependencies = [ + "ahash", + "bitflags", + "bytemuck", + "chrono", + "chrono-tz 0.8.6", + "comfy-table", + "either", + "hashbrown", + "indexmap", + "num-traits", + "once_cell", + "polars-arrow", + "polars-compute", + "polars-error", + "polars-row", + "polars-utils", + "rand", + "rand_distr", + "rayon", + "regex", + "smartstring", + "thiserror", + "version_check", + "xxhash-rust", +] + +[[package]] +name = "polars-error" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e1707a17475ba5e74c349154b415e3148a1a275e395965427971b5e53ad621" +dependencies = [ + "polars-arrow-format", + "regex", + "simdutf8", + "thiserror", +] + +[[package]] +name = "polars-expr" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a9688d5842e7a7fbad88e67a174778794a91d97d3bba1b3c09dd1656fee3b2" +dependencies = [ + "ahash", + "bitflags", + "once_cell", + "polars-arrow", + "polars-core", + "polars-io", + "polars-ops", + "polars-plan", + "polars-time", + "polars-utils", + "rayon", + "smartstring", +] + +[[package]] +name = "polars-ffi" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf2cb86f7c01523d95998760200836d98b49653172ee35fb871c4f301d85569" +dependencies = [ + "polars-arrow", + "polars-core", +] + +[[package]] +name = "polars-io" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18798dacd94fb9263f65f63f0feab0908675422646d6f7fc37043b85ff6dca35" +dependencies = [ + "ahash", + "atoi_simd", + "bytes", + "chrono", + "chrono-tz 0.8.6", + "fast-float", + "home", + "itoa", + "memchr", + "memmap2", + "num-traits", + "once_cell", + "percent-encoding", + "polars-arrow", + "polars-core", + "polars-error", + "polars-time", + "polars-utils", + "rayon", + "regex", + "ryu", + "simdutf8", + "smartstring", +] + +[[package]] +name = "polars-lazy" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a11994c2211f2e99d9ac31776fd7c2c0607d5fe62d5b5db9e396f7d663f3d5" +dependencies = [ + "ahash", + "bitflags", + "glob", + "memchr", + "once_cell", + "polars-arrow", + "polars-core", + "polars-expr", + "polars-io", + "polars-mem-engine", + "polars-ops", + "polars-pipe", + "polars-plan", + "polars-time", + "polars-utils", + "rayon", + "smartstring", + "version_check", +] + +[[package]] +name = "polars-mem-engine" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acd5fde6fadaddfcae3227ec5b64121007928f8e68870c80653438e20c1c587" +dependencies = [ + "polars-arrow", + "polars-core", + "polars-error", + "polars-expr", + "polars-io", + "polars-ops", + "polars-plan", + "polars-time", + "polars-utils", + "rayon", +] + +[[package]] +name = "polars-ops" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4170c59e974727941edfb722f6d430ed623be9e7f30581ee00832c907f1b9fd" +dependencies = [ + "ahash", + "argminmax", + "base64", + "bytemuck", + "chrono", + "chrono-tz 0.8.6", + "either", + "hashbrown", + "hex", + "indexmap", + "memchr", + "num-traits", + "polars-arrow", + "polars-compute", + "polars-core", + "polars-error", + "polars-utils", + "rayon", + "regex", + "smartstring", + "unicode-reverse", + "version_check", +] + +[[package]] +name = "polars-parquet" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c684638c36c60c691d707d414249fe8af4a19a35a39d418464b140fe23732e5d" +dependencies = [ + "ahash", + "base64", + "ethnum", + "num-traits", + "parquet-format-safe", + "polars-arrow", + "polars-compute", + "polars-error", + "polars-utils", + "seq-macro", + "simdutf8", + "streaming-decompression", +] + +[[package]] +name = "polars-pipe" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832af9fbebc4c074d95fb19e1ef9e1bf37c343641238c2476febff296a7028ea" +dependencies = [ + "crossbeam-channel", + "crossbeam-queue", + "enum_dispatch", + "hashbrown", + "num-traits", + "polars-arrow", + "polars-compute", + "polars-core", + "polars-expr", + "polars-io", + "polars-ops", + "polars-plan", + "polars-row", + "polars-utils", + "rayon", + "smartstring", + "uuid", + "version_check", +] + +[[package]] +name = "polars-plan" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801390ea815c05c9cf8337f3148090c9c10c9595a839fa0706b77cc2405b4466" +dependencies = [ + "ahash", + "bytemuck", + "chrono-tz 0.8.6", + "either", + "hashbrown", + "once_cell", + "percent-encoding", + "polars-arrow", + "polars-core", + "polars-io", + "polars-ops", + "polars-time", + "polars-utils", + "rayon", + "recursive", + "regex", + "smartstring", + "strum_macros", + "version_check", +] + +[[package]] +name = "polars-row" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee955e91b605fc91db4d0a8ea02609d3a09ff79256d905214a2a6f758cd6f7b" +dependencies = [ + "bytemuck", + "polars-arrow", + "polars-error", + "polars-utils", +] + +[[package]] +name = "polars-sql" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89c00a4b399501d5bd478e8e8022b9391047fe8570324ecba20c4e4833c0e87" +dependencies = [ + "hex", + "once_cell", + "polars-arrow", + "polars-core", + "polars-error", + "polars-lazy", + "polars-ops", + "polars-plan", + "polars-time", + "rand", + "serde", + "serde_json", + "sqlparser", +] + +[[package]] +name = "polars-time" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9689b3aff99d64befe300495528bdc44c36d2656c3a8b242a790d4f43df027fc" +dependencies = [ + "atoi", + "bytemuck", + "chrono", + "chrono-tz 0.8.6", + "now", + "once_cell", + "polars-arrow", + "polars-core", + "polars-error", + "polars-ops", + "polars-utils", + "regex", + "smartstring", +] + +[[package]] +name = "polars-utils" +version = "0.41.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12081e346983a91e26f395597e1d53dea1b4ecd694653aee1cc402d2fae01f04" +dependencies = [ + "ahash", + "bytemuck", + "hashbrown", + "indexmap", + "num-traits", + "once_cell", + "polars-error", + "raw-cpuid", + "rayon", + "smartstring", + "stacker", + "sysinfo", + "version_check", +] + +[[package]] +name = "polars_xdt" +version = "0.15.0" +dependencies = [ + "chrono", + "chrono-tz 0.9.0", + "jemallocator", + "polars", + "polars-arrow", + "polars-ops", + "pyo3", + "pyo3-polars", + "serde", +] + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "pure-rust-locales" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" + +[[package]] +name = "pyo3" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "pyo3-polars" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "910d4952410bded2d87e13d48ca8e59b7fe028b07d29deaffd4539b3d906e75f" +dependencies = [ + "polars", + "polars-core", + "polars-ffi", + "polars-plan", + "pyo3", + "pyo3-polars-derive", + "serde", + "serde-pickle", + "thiserror", +] + +[[package]] +name = "pyo3-polars-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b6ee1fa8425af0d3ec588cba614fc2f3fd8e4838fb74f38987e697196884fe" +dependencies = [ + "polars-core", + "polars-ffi", + "polars-plan", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "raw-cpuid" +version = "11.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn 2.0.68", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seq-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-pickle" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762ad136a26407c6a80825813600ceeab5e613660d93d79a41f0ec877171e71" +dependencies = [ + "byteorder", + "iter-read", + "num-bigint", + "num-traits", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "sqlparser" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295e9930cd7a97e58ca2a070541a3ca502b17f5d1fa7157376d0fabd85324f25" +dependencies = [ + "log", +] + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "streaming-decompression" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3" +dependencies = [ + "fallible-streaming-iterator", +] + +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.68", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysinfo" +version = "0.30.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "windows", +] + +[[package]] +name = "target-features" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1bbb9f3c5c463a01705937a24fdabc5047929ac764b2d5b9cf681c1f5041ed5" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-reverse" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6f4888ebc23094adfb574fdca9fdc891826287a6397d2cd28802ffd6f20c76" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.68", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.11+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index bdef00d..59a73b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "polars_xdt" -version = "0.14.14" +version = "0.15.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,14 +10,13 @@ crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.21.2", features = ["extension-module", "abi3-py38"] } -pyo3-polars = { version = "0.14.0", features = ["derive"] } +pyo3-polars = { version = "0.15.0", features = ["derive"] } serde = { version = "1", features = ["derive"] } chrono = { version = "0.4.38", default-features = false, features = ["std", "unstable-locales"] } chrono-tz = "0.9.0" -polars = { version = "0.40.0", features = ["strings", "dtype-date"], default-features = false } -polars-time = { version = "0.40.0", features = ["timezones"], default-features = false } -polars-ops = { version = "0.40.0", default-features = false } -polars-arrow = { version = "0.40.0", default-features = false } +polars = { version = "0.41.2", features = ["strings", "timezones"]} +polars-ops = { version = "0.41.2", default-features = false } +polars-arrow = { version = "0.41.2", default-features = false } [target.'cfg(target_os = "linux")'.dependencies] jemallocator = { version = "0.5", features = ["disable_initial_exec_tls"] } diff --git a/README.md b/README.md index 88a6a91..d40e370 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ eXtra stuff for DateTimes in [Polars](https://www.pola.rs/). - ✅ blazingly fast, written in Rust -- ✅ custom business-day arithmetic - ✅ convert to and from multiple time zones - ✅ format datetime in different locales - ✅ convert to Julian Dates -- ✅ time-based EWMA +- ✅ ~time-based EWMA~ (upstreamed to Polars itself) +- ✅ ~custom business-day arithmetic~ (upstreamed to Polars itself) Installation ------------ @@ -31,50 +31,56 @@ Then, you'll need to install `polars-xdt`: pip install polars-xdt ``` -Read the [documentation](https://marcogorelli.github.io/polars-xdt-docs/) for a little tutorial and API reference. +Read the [documentation](https://marcogorelli.github.io/polars-xdt-docs/) for a more examples and functionality. Basic Example ------------- Say we start with ```python -from datetime import date +from datetime import datetime import polars as pl import polars_xdt as xdt - df = pl.DataFrame( - {"date": [date(2023, 4, 3), date(2023, 9, 1), date(2024, 1, 4)]} + { + "local_dt": [ + datetime(2020, 10, 10, 1), + datetime(2020, 10, 10, 2), + datetime(2020, 10, 9, 20), + ], + "timezone": [ + "Europe/London", + "Africa/Kigali", + "America/New_York", + ], + } ) ``` -Let's shift `Date` forwards by 5 days, excluding Saturday and Sunday: +Let's localize each datetime to the given timezone and convert to +UTC, all in one step: ```python result = df.with_columns( - date_shifted=xdt.offset_by( - 'date', - '5bd', - weekend=('Sat', 'Sun'), - ) + xdt.from_local_datetime( + "local_dt", pl.col("timezone"), "UTC" + ).alias("date") ) print(result) ``` ``` -shape: (3, 2) -┌────────────┬──────────────┐ -│ date ┆ date_shifted │ -│ --- ┆ --- │ -│ date ┆ date │ -╞════════════╪══════════════╡ -│ 2023-04-03 ┆ 2023-04-10 │ -│ 2023-09-01 ┆ 2023-09-08 │ -│ 2024-01-04 ┆ 2024-01-11 │ -└────────────┴──────────────┘ +shape: (3, 3) +┌─────────────────────┬──────────────────┬─────────────────────────┐ +│ local_dt ┆ timezone ┆ date │ +│ --- ┆ --- ┆ --- │ +│ datetime[μs] ┆ str ┆ datetime[μs, UTC] │ +╞═════════════════════╪══════════════════╪═════════════════════════╡ +│ 2020-10-10 01:00:00 ┆ Europe/London ┆ 2020-10-10 00:00:00 UTC │ +│ 2020-10-10 02:00:00 ┆ Africa/Kigali ┆ 2020-10-10 00:00:00 UTC │ +│ 2020-10-09 20:00:00 ┆ America/New_York ┆ 2020-10-10 00:00:00 UTC │ +└─────────────────────┴──────────────────┴─────────────────────────┘ ``` -Note that `polars-xdt` also registers a `xdt` namespace in the `Expression` class, so you -could equivalently write the above using `pl.col('date').xdt.offset_by('5bd')` (but note -that then type-checking would not recognise the `xdt` attribute). Read the [documentation](https://marcogorelli.github.io/polars-xdt-docs/) for more examples! diff --git a/docs/API.rst b/docs/API.rst index f1da729..56c9d41 100644 --- a/docs/API.rst +++ b/docs/API.rst @@ -5,8 +5,6 @@ API :toctree: api/ polars_xdt.date_range - polars_xdt.ewma_by_time - polars_xdt.workday_count polars_xdt.ceil polars_xdt.day_name polars_xdt.format_localized @@ -14,6 +12,5 @@ API polars_xdt.is_workday polars_xdt.month_name polars_xdt.month_delta - polars_xdt.offset_by polars_xdt.to_local_datetime polars_xdt.to_julian_date diff --git a/docs/api/polars_xdt.workday_count.rst b/docs/api/polars_xdt.workday_count.rst deleted file mode 100644 index b468d40..0000000 --- a/docs/api/polars_xdt.workday_count.rst +++ /dev/null @@ -1,6 +0,0 @@ -polars\_xdt.workday\_count -========================== - -.. currentmodule:: polars_xdt - -.. autofunction:: workday_count \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 7d61e11..2acba2d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,5 +13,4 @@ Polars, a blazingly fast dataframe library. :caption: Contents: installation - tutorial API diff --git a/docs/installation.rst b/docs/installation.rst index f715833..6eaba16 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -14,9 +14,6 @@ Then, if you can run .. code-block:: import polars as pl - import polars_xdt # noqa: F401 + import polars_xdt - print(pl.col('a').xdt) - -and see something like ``, -it means installation all worked correctly! +without errors it means installation all worked correctly! diff --git a/docs/tutorial.rst b/docs/tutorial.rst deleted file mode 100644 index 3f8bc63..0000000 --- a/docs/tutorial.rst +++ /dev/null @@ -1,99 +0,0 @@ -Tutorial -======== - -Say we start with - -.. code-block:: python - - from datetime import date - - import polars as pl - import polars_xdt as xdt - - - df = pl.DataFrame( - {"date": [date(2023, 4, 3), date(2023, 9, 1), date(2024, 1, 4)]} - ) - - -Let's shift `Date` forwards by 5 days, excluding Saturday and Sunday: - -.. code-block:: python - - result = df.with_columns( - date_shifted=xdt.offset_by('date', '5bd', weekend=('Sat', 'Sun')) - ) - print(result) - -.. code-block:: - - shape: (3, 2) - ┌────────────┬──────────────┐ - │ date ┆ date_shifted │ - │ --- ┆ --- │ - │ date ┆ date │ - ╞════════════╪══════════════╡ - │ 2023-04-03 ┆ 2023-04-10 │ - │ 2023-09-01 ┆ 2023-09-08 │ - │ 2024-01-04 ┆ 2024-01-11 │ - └────────────┴──────────────┘ - -Let's shift `Date` forwards by 5 days, excluding Friday, Saturday, and England holidays -for 2023 and 2024 (note: you'll need to install the -`holidays `_ package for this example to work): - -.. code-block:: python - - import holidays - - england_holidays = holidays.country_holidays("UK", subdiv='ENG', years=[2023, 2024]) - - result = df.with_columns( - date_shifted=xdt.offset_by( - 'date', - by='5bd', - weekend=('Sat', 'Sun'), - holidays=england_holidays, - ) - ) - print(result) - -.. code-block:: - - shape: (3, 2) - ┌────────────┬──────────────┐ - │ date ┆ date_shifted │ - │ --- ┆ --- │ - │ date ┆ date │ - ╞════════════╪══════════════╡ - │ 2023-04-03 ┆ 2023-04-12 │ - │ 2023-09-01 ┆ 2023-09-08 │ - │ 2024-01-04 ┆ 2024-01-11 │ - └────────────┴──────────────┘ - -Count the number of business dates between two columns: - -.. code-block:: python - - df = pl.DataFrame( - { - "start": [date(2023, 1, 4), date(2023, 5, 1), date(2023, 9, 9)], - "end": [date(2023, 2, 8), date(2023, 5, 2), date(2023, 12, 30)], - } - ) - result = df.with_columns(n_business_days=xdt.workday_count('start', 'end')) - print(result) - - -.. code-block:: - - shape: (3, 3) - ┌────────────┬────────────┬─────────────────┐ - │ start ┆ end ┆ n_business_days │ - │ --- ┆ --- ┆ --- │ - │ date ┆ date ┆ i32 │ - ╞════════════╪════════════╪═════════════════╡ - │ 2023-01-04 ┆ 2023-02-08 ┆ 25 │ - │ 2023-05-01 ┆ 2023-05-02 ┆ 1 │ - │ 2023-09-09 ┆ 2023-12-30 ┆ 80 │ - └────────────┴────────────┴─────────────────┘ diff --git a/polars_xdt/__init__.py b/polars_xdt/__init__.py index 00036fa..81c8745 100644 --- a/polars_xdt/__init__.py +++ b/polars_xdt/__init__.py @@ -5,16 +5,13 @@ arg_previous_greater, ceil, day_name, - ewma_by_time, format_localized, from_local_datetime, is_workday, month_delta, month_name, - offset_by, to_julian_date, to_local_datetime, - workday_count, ) from polars_xdt.ranges import date_range @@ -28,12 +25,9 @@ "from_local_datetime", "is_workday", "month_name", - "offset_by", "to_julian_date", "to_local_datetime", - "workday_count", "month_delta", "arg_previous_greater", - "ewma_by_time", "__version__", ] diff --git a/polars_xdt/functions.py b/polars_xdt/functions.py index 6caee50..ecf0a31 100644 --- a/polars_xdt/functions.py +++ b/polars_xdt/functions.py @@ -1,9 +1,6 @@ from __future__ import annotations -import re import sys -import warnings -from datetime import date, timedelta from pathlib import Path from typing import TYPE_CHECKING, Literal, Sequence @@ -17,6 +14,8 @@ from typing_extensions import TypeAlias if TYPE_CHECKING: + from datetime import date + from polars.type_aliases import IntoExpr RollStrategy: TypeAlias = Literal["raise", "forward", "backward"] @@ -50,163 +49,6 @@ def get_weekmask(weekend: Sequence[str]) -> list[bool]: return weekmask -def offset_by( - expr: IntoExpr, - by: IntoExpr, - *, - weekend: Sequence[str] = ("Sat", "Sun"), - holidays: Sequence[date] | None = None, - roll: RollStrategy = "raise", -) -> pl.Expr: - """ - Offset this date by a relative time offset. - - .. deprecated:: 0.14.13 - - This is deprecated, please use `polars.add_business_days` instead. - - Parameters - ---------- - expr - Expression to offset by relative time offset. - by - The offset to apply. This can be a string of the form "nbd" (where n - is an integer), or a polars expression that evaluates to such a string. - Additional units are passed to `polars.dt.offset_by`. - weekend - The days of the week that are considered weekends. Defaults to ("Sat", "Sun"). - holidays - The holidays to exclude from the calculation. Defaults to None. - roll - How to handle dates that fall on a non-workday. - - - "raise" raise an error (default). - - "forward" roll forward to the next business day. - - "backward" roll backward to the previous business day. - - Returns - ------- - polars.Expr - - Examples - -------- - >>> import polars as pl - >>> import polars_xdt as xdt - >>> df = pl.DataFrame( - ... {"date": [date(2023, 4, 3), date(2023, 9, 1), date(2024, 1, 4)]} - ... ) - >>> df.with_columns( - ... date_shifted=xdt.offset_by("date", "1bd"), - ... ) - shape: (3, 2) - ┌────────────┬──────────────┐ - │ date ┆ date_shifted │ - │ --- ┆ --- │ - │ date ┆ date │ - ╞════════════╪══════════════╡ - │ 2023-04-03 ┆ 2023-04-04 │ - │ 2023-09-01 ┆ 2023-09-04 │ - │ 2024-01-04 ┆ 2024-01-05 │ - └────────────┴──────────────┘ - - You can also specify custom weekends and holidays: - - >>> import holidays - >>> holidays_england = holidays.country_holidays( - ... "UK", subdiv="ENG", years=[2023, 2024] - ... ) - >>> df.with_columns( - ... date_shifted=xdt.offset_by( - ... "date", - ... "5bd", - ... holidays=holidays_england, - ... weekend=["Fri", "Sat"], - ... roll="backward", - ... ), - ... ) - shape: (3, 2) - ┌────────────┬──────────────┐ - │ date ┆ date_shifted │ - │ --- ┆ --- │ - │ date ┆ date │ - ╞════════════╪══════════════╡ - │ 2023-04-03 ┆ 2023-04-11 │ - │ 2023-09-01 ┆ 2023-09-07 │ - │ 2024-01-04 ┆ 2024-01-11 │ - └────────────┴──────────────┘ - - You can also pass expressions to `by`: - - >>> df = pl.DataFrame( - ... { - ... "date": [ - ... date(2023, 4, 3), - ... date(2023, 9, 1), - ... date(2024, 1, 4), - ... ], - ... "by": ["1bd", "2bd", "-3bd"], - ... } - ... ) - >>> df.with_columns(date_shifted=xdt.offset_by("date", pl.col("by"))) - shape: (3, 3) - ┌────────────┬──────┬──────────────┐ - │ date ┆ by ┆ date_shifted │ - │ --- ┆ --- ┆ --- │ - │ date ┆ str ┆ date │ - ╞════════════╪══════╪══════════════╡ - │ 2023-04-03 ┆ 1bd ┆ 2023-04-04 │ - │ 2023-09-01 ┆ 2bd ┆ 2023-09-05 │ - │ 2024-01-04 ┆ -3bd ┆ 2024-01-01 │ - └────────────┴──────┴──────────────┘ - - """ - warnings.warn( - "`offset_by` is deprecated, as it has been upstreamed. Please use `polars.add_business_days` instead.", - DeprecationWarning, - stacklevel=2, - ) - expr = parse_into_expr(expr) - if ( - isinstance(by, str) - and (match := re.search(r"(\d+bd)", by)) is not None - and (len(match.group(1)) == len(by)) - ): - # Fast path - do we have a business day offset, and nothing else? - n: int | pl.Expr = pl.lit(int(by[:-2]), dtype=pl.Int32) - fastpath = True - else: - if not isinstance(by, pl.Expr): - by = pl.lit(by) - n = (by.str.extract(r"^(-?)") + by.str.extract(r"(\d+)bd")).cast( - pl.Int32, - ) - by = by.str.replace(r"(\d+bd)", "") - fastpath = False - - if not holidays: - holidays_int = [] - else: - holidays_int = sorted( - {(holiday - date(1970, 1, 1)).days for holiday in holidays}, - ) - weekmask = get_weekmask(weekend) - - result = register_plugin( - args=[expr, n], - lib=lib, - symbol="advance_n_days", - is_elementwise=True, - kwargs={ - "holidays": holidays_int, - "weekmask": weekmask, - "roll": roll, - }, - ) - if fastpath: - return result - return result.dt.offset_by(by) - - def is_workday( expr: IntoExpr, *, @@ -258,23 +100,13 @@ def is_workday( """ expr = parse_into_expr(expr) - weekmask = get_weekmask(weekend) - if not holidays: - holidays_int = [] - else: - holidays_int = sorted( - {(holiday - date(1970, 1, 1)).days for holiday in holidays}, + weekend_int = [mapping[x] for x in weekend] + if holidays is not None: + return ~( + expr.dt.date().is_in(holidays) + | expr.dt.weekday().is_in(weekend_int) ) - return register_plugin( - lib=lib, - symbol="is_workday", - is_elementwise=True, - args=[expr], - kwargs={ - "weekmask": weekmask, - "holidays": holidays_int, - }, - ) + return ~expr.dt.weekday().is_in(weekend_int) def from_local_datetime( @@ -680,85 +512,6 @@ def month_name(expr: str | pl.Expr, locale: str | None = None) -> pl.Expr: return result -def workday_count( - start_dates: IntoExpr, - end_dates: IntoExpr, - weekend: Sequence[str] = ("Sat", "Sun"), - holidays: Sequence[date] | None = None, -) -> pl.Expr: - """ - Count the number of workdays between two columns of dates. - - .. deprecated:: 0.14.13 - - This is deprecated, please use `polars.business_day_count` instead. - - Parameters - ---------- - start_dates - Start date(s). This can be a string column, a date column, or a single date. - end_dates - End date(s). This can be a string column, a date column, or a single date. - weekend - The days of the week that are considered weekends. Defaults to ("Sat", "Sun"). - holidays - The holidays to exclude from the calculation. Defaults to None. This should - be a list of ``datetime.date`` s. - - Returns - ------- - polars.Expr - - Examples - -------- - >>> from datetime import date - >>> import polars as pl - >>> import polars_xdt as xdt - >>> df = pl.DataFrame( - ... { - ... "start": [date(2023, 1, 4), date(2023, 5, 1), date(2023, 9, 9)], - ... "end": [date(2023, 2, 8), date(2023, 5, 2), date(2023, 12, 30)], - ... } - ... ) - >>> df.with_columns(n_business_days=xdt.workday_count("start", "end")) - shape: (3, 3) - ┌────────────┬────────────┬─────────────────┐ - │ start ┆ end ┆ n_business_days │ - │ --- ┆ --- ┆ --- │ - │ date ┆ date ┆ i32 │ - ╞════════════╪════════════╪═════════════════╡ - │ 2023-01-04 ┆ 2023-02-08 ┆ 25 │ - │ 2023-05-01 ┆ 2023-05-02 ┆ 1 │ - │ 2023-09-09 ┆ 2023-12-30 ┆ 80 │ - └────────────┴────────────┴─────────────────┘ - - """ - warnings.warn( - "`workday_count` is deprecated, as it has been upstreamed. Please use `polars.business_day_count` instead.", - DeprecationWarning, - stacklevel=2, - ) - start_dates = parse_into_expr(start_dates) - end_dates = parse_into_expr(end_dates) - weekmask = get_weekmask(weekend) - if not holidays: - holidays_int = [] - else: - holidays_int = sorted( - {(holiday - date(1970, 1, 1)).days for holiday in holidays}, - ) - return register_plugin( - lib=lib, - symbol="workday_count", - is_elementwise=True, - args=[start_dates, end_dates], - kwargs={ - "weekmask": weekmask, - "holidays": holidays_int, - }, - ) - - def month_delta( start_dates: IntoExpr, end_dates: IntoExpr, @@ -923,97 +676,3 @@ def arg_previous_greater(expr: IntoExpr) -> pl.Expr: is_elementwise=False, args=[expr], ) - - -def ewma_by_time( - values: IntoExpr, - *, - times: IntoExpr, - half_life: timedelta, -) -> pl.Expr: - r""" - Calculate time-based exponentially weighted moving average. - - Given observations :math:`x_1, x_2, \ldots, x_n` at times - :math:`t_1, t_2, \ldots, t_n`, the EWMA is calculated as - - .. math:: - - y_0 &= x_0 - - \alpha_i &= \exp(-\lambda(t_i - t_{i-1})) - - y_i &= \alpha_i x_i + (1 - \alpha_i) y_{i-1}; \quad i > 0 - - where :math:`\lambda` equals :math:`\ln(2) / \text{half_life}`. - - .. deprecated:: 0.14.13 - - This is deprecated, please use `polars.ewm_mean_by` instead. - - Parameters - ---------- - values - Values to calculate EWMA for. Should be signed numeric. - times - Times corresponding to `values`. Should be ``DateTime`` or ``Date``. - half_life - Unit over which observation decays to half its value. - - Returns - ------- - pl.Expr - Float64 - - Examples - -------- - >>> import polars as pl - >>> import polars_xdt as xdt - >>> from datetime import date, timedelta - >>> df = pl.DataFrame( - ... { - ... "values": [0, 1, 2, None, 4], - ... "times": [ - ... date(2020, 1, 1), - ... date(2020, 1, 3), - ... date(2020, 1, 10), - ... date(2020, 1, 15), - ... date(2020, 1, 17), - ... ], - ... } - ... ) - >>> df.with_columns( - ... ewma=xdt.ewma_by_time( - ... "values", times="times", half_life=timedelta(days=4) - ... ), - ... ) - shape: (5, 3) - ┌────────┬────────────┬──────────┐ - │ values ┆ times ┆ ewma │ - │ --- ┆ --- ┆ --- │ - │ i64 ┆ date ┆ f64 │ - ╞════════╪════════════╪══════════╡ - │ 0 ┆ 2020-01-01 ┆ 0.0 │ - │ 1 ┆ 2020-01-03 ┆ 0.292893 │ - │ 2 ┆ 2020-01-10 ┆ 1.492474 │ - │ null ┆ 2020-01-15 ┆ null │ - │ 4 ┆ 2020-01-17 ┆ 3.254508 │ - └────────┴────────────┴──────────┘ - - """ - warnings.warn( - "`ewma_by_time` is deprecated, as it has been upstreamed. Please use `polars.ewm_mean_by` instead.", - DeprecationWarning, - stacklevel=2, - ) - values = parse_into_expr(values) - half_life_us = ( - int(half_life.total_seconds()) * 1_000_000 + half_life.microseconds - ) - return register_plugin( - lib=lib, - symbol="ewma_by_time", - is_elementwise=False, - args=[values, times], - kwargs={"half_life": half_life_us}, - ) diff --git a/polars_xdt/ranges.py b/polars_xdt/ranges.py index 52168ad..c284fa4 100644 --- a/polars_xdt/ranges.py +++ b/polars_xdt/ranges.py @@ -28,8 +28,8 @@ def date_range( @overload def date_range( - start: date | datetime | IntoExprColumn, - end: date | datetime | IntoExprColumn, + start: date | IntoExprColumn, + end: date | IntoExprColumn, interval: str | timedelta = "1d", *, closed: ClosedInterval = ..., @@ -41,8 +41,8 @@ def date_range( @overload def date_range( - start: date | datetime | IntoExprColumn, - end: date | datetime | IntoExprColumn, + start: date | IntoExprColumn, + end: date | IntoExprColumn, interval: str | timedelta = "1d", *, closed: ClosedInterval = ..., diff --git a/src/business_days.rs b/src/business_days.rs deleted file mode 100644 index 0a5bed7..0000000 --- a/src/business_days.rs +++ /dev/null @@ -1,231 +0,0 @@ -use chrono::DateTime; -use polars::prelude::arity::try_binary_elementwise; -use polars::prelude::*; - -pub(crate) fn weekday(x: i32) -> i32 { - // the first modulo might return a negative number, so we add 7 and take - // the modulo again so we're sure we have something between 0 and 6 - ((x - 4) % 7 + 7) % 7 + 1 -} - -pub(crate) fn calculate_advance( - mut date: i32, - mut offset: i32, - mut day_of_week: i32, - weekmask: &[bool; 7], - n_weekdays: i32, - holidays: &[i32], - roll: &str, -) -> PolarsResult { - match roll { - "raise" => { - if holidays.contains(&date) - | unsafe { !*weekmask.get_unchecked(day_of_week as usize - 1) } - { - let date = DateTime::from_timestamp(date as i64 * 24 * 60 * 60, 0) - .unwrap() - .format("%Y-%m-%d"); - polars_bail!(ComputeError: - format!("date {} is not a business date, cannot advance; set a valid `roll` strategy.", date) - ) - }; - } - "forward" => { - while holidays.contains(&date) - | unsafe { !*weekmask.get_unchecked(day_of_week as usize - 1) } - { - date += 1; - day_of_week += 1; - if day_of_week > 7 { - day_of_week = 1; - } - } - } - "backward" => { - while holidays.contains(&date) - | unsafe { !*weekmask.get_unchecked(day_of_week as usize - 1) } - { - date -= 1; - day_of_week -= 1; - if day_of_week == 0 { - day_of_week = 7; - } - } - } - _ => { - polars_bail!(InvalidOperation: - "`roll` must be one of 'raise', 'forward' or 'backward'; found '{}'", roll - ) - } - } - - if offset > 0 { - let holidays_begin = match holidays.binary_search(&date) { - Ok(x) => x, - Err(x) => x, - }; - - date += (offset / n_weekdays) * 7; - offset %= n_weekdays; - - let holidays_temp = match holidays[holidays_begin..].binary_search(&date) { - Ok(x) => x + 1, - Err(x) => x, - } + holidays_begin; - - offset += (holidays_temp - holidays_begin) as i32; - let holidays_begin = holidays_temp; - - while offset > 0 { - date += 1; - day_of_week += 1; - if day_of_week > 7 { - day_of_week = 1; - } - if unsafe { - (*weekmask.get_unchecked(day_of_week as usize - 1)) - && (!holidays[holidays_begin..].contains(&date)) - } { - offset -= 1; - } - } - Ok(date) - } else { - let holidays_end = match holidays.binary_search(&date) { - Ok(x) => x + 1, - Err(x) => x, - }; - - date += (offset / n_weekdays) * 7; - offset %= n_weekdays; - - let holidays_temp = match holidays[..holidays_end].binary_search(&date) { - Ok(x) => x, - Err(x) => x, - }; - - offset -= (holidays_end - holidays_temp) as i32; - let holidays_end = holidays_temp; - - while offset < 0 { - date -= 1; - day_of_week -= 1; - if day_of_week == 0 { - day_of_week = 7; - } - if unsafe { - (*weekmask.get_unchecked(day_of_week as usize - 1)) - && (!holidays[..holidays_end].contains(&date)) - } { - offset += 1; - } - } - Ok(date) - } -} - -pub(crate) fn impl_advance_n_days( - s: &Series, - n: &Series, - holidays: Vec, - weekmask: &[bool; 7], - roll: &str, -) -> PolarsResult { - let original_dtype = s.dtype(); - - // Only keep holidays which aren't on weekends. - let holidays: Vec = { - holidays - .into_iter() - .filter(|x| unsafe { *weekmask.get_unchecked(weekday(*x) as usize - 1) }) - .collect() - }; - - let n_weekdays = weekmask.iter().filter(|&x| *x).count() as i32; - - let n = n.i32()?; - - match s.dtype() { - DataType::Date => { - let ca = s.date()?; - let out = match n.len() { - 1 => { - if let Some(n) = n.get(0) { - ca.try_apply_nonnull_values_generic(|x_date| { - let x_weekday = weekday(x_date); - calculate_advance( - x_date, n, x_weekday, weekmask, n_weekdays, &holidays, roll, - ) - }) - } else { - Ok(Int32Chunked::full_null(ca.name(), ca.len())) - } - } - _ => try_binary_elementwise(ca, n, |opt_s, opt_n| match (opt_s, opt_n) { - (Some(x_date), Some(n)) => { - let x_weekday = weekday(x_date); - Ok(Some(calculate_advance( - x_date, n, x_weekday, weekmask, n_weekdays, &holidays, roll, - )?)) - } - _ => Ok(None), - }), - }; - out?.cast(original_dtype) - } - DataType::Datetime(time_unit, time_zone) => { - let multiplier = match time_unit { - TimeUnit::Milliseconds => 60 * 60 * 24 * 1_000, - TimeUnit::Microseconds => 60 * 60 * 24 * 1_000_000, - TimeUnit::Nanoseconds => 60 * 60 * 24 * 1_000_000_000, - }; - let ca = &polars_ops::prelude::replace_time_zone( - s.datetime()?, - None, - &StringChunked::from_iter(std::iter::once("raise")), - NonExistent::Raise, - )?; - let out = match n.len() { - 1 => { - if let Some(n) = n.get(0) { - ca.try_apply_nonnull_values_generic(|x| { - let x_date = (x / multiplier) as i32; - let x_weekday = weekday(x_date); - Ok::( - x + ((calculate_advance( - x_date, n, x_weekday, weekmask, n_weekdays, &holidays, roll, - )? - x_date) as i64 - * multiplier), - ) - }) - } else { - Ok(Int64Chunked::full_null(ca.name(), ca.len())) - } - } - _ => try_binary_elementwise(ca, n, |opt_s, opt_n| match (opt_s, opt_n) { - (Some(x), Some(n)) => { - let x_date = (x / multiplier) as i32; - let x_weekday = weekday(x_date); - Ok(Some( - x + ((calculate_advance( - x_date, n, x_weekday, weekmask, n_weekdays, &holidays, roll, - )? - x_date) as i64 - * multiplier), - )) - } - _ => Ok(None), - }), - }; - let out = polars_ops::prelude::replace_time_zone( - &out?.into_datetime(*time_unit, None), - time_zone.as_deref(), - &StringChunked::from_iter(std::iter::once("raise")), - NonExistent::Raise, - )?; - out.cast(original_dtype) - } - _ => { - polars_bail!(ComputeError: format!("expected Datetime or Date dtype, got: {}", original_dtype)) - } - } -} diff --git a/src/ewma_by_time.rs b/src/ewma_by_time.rs deleted file mode 100644 index 3268935..0000000 --- a/src/ewma_by_time.rs +++ /dev/null @@ -1,85 +0,0 @@ -use polars::prelude::*; -use polars_arrow::array::PrimitiveArray; - -pub(crate) fn impl_ewma_by_time_float( - times: &Int64Chunked, - values: &Float64Chunked, - half_life: i64, - time_unit: TimeUnit, -) -> Float64Chunked { - let mut out = Vec::with_capacity(times.len()); - if values.is_empty() { - return Float64Chunked::full_null("", times.len()); - } - - let half_life = match time_unit { - TimeUnit::Milliseconds => half_life / 1_000, - TimeUnit::Microseconds => half_life, - TimeUnit::Nanoseconds => half_life * 1_000, - }; - - let mut skip_rows: usize = 0; - let mut prev_time: i64 = 0; - let mut prev_result: f64 = 0.; - for (idx, (value, time)) in values.iter().zip(times.iter()).enumerate() { - match (time, value) { - (Some(time), Some(value)) => { - prev_time = time; - prev_result = value; - out.push(Some(prev_result)); - skip_rows = idx + 1; - break; - } - _ => { - out.push(None); - } - }; - } - values - .iter() - .zip(times.iter()) - .skip(skip_rows) - .for_each(|(value, time)| { - match (time, value) { - (Some(time), Some(value)) => { - let delta_time = time - prev_time; - // equivalent to: - // alpha = exp(-delta_time*ln(2) / half_life) - let alpha = (0.5_f64).powf(delta_time as f64 / half_life as f64); - let result = (1. - alpha) * value + alpha * prev_result; - prev_time = time; - prev_result = result; - out.push(Some(result)); - } - _ => out.push(None), - } - }); - let arr = PrimitiveArray::::from(out); - Float64Chunked::from(arr) -} - -pub(crate) fn impl_ewma_by_time( - times: &Int64Chunked, - values: &Series, - half_life: i64, - time_unit: TimeUnit, -) -> Series { - match values.dtype() { - DataType::Float64 => { - let values = values.f64().unwrap(); - impl_ewma_by_time_float(times, values, half_life, time_unit).into_series() - } - DataType::Int64 | DataType::Int32 => { - let values = values.cast(&DataType::Float64).unwrap(); - let values = values.f64().unwrap(); - impl_ewma_by_time_float(times, values, half_life, time_unit).into_series() - } - DataType::Float32 => { - // todo: preserve Float32 in this case - let values = values.cast(&DataType::Float64).unwrap(); - let values = values.f64().unwrap(); - impl_ewma_by_time_float(times, values, half_life, time_unit).into_series() - } - dt => panic!("Expected values to be signed numeric, got {:?}", dt), - } -} diff --git a/src/expressions.rs b/src/expressions.rs index ab71e33..e9e8b8e 100644 --- a/src/expressions.rs +++ b/src/expressions.rs @@ -1,11 +1,7 @@ #![allow(clippy::unit_arg, clippy::unused_unit)] use crate::arg_previous_greater::*; -use crate::business_days::*; -use crate::ewma_by_time::*; use crate::format_localized::*; -use crate::is_workday::*; use crate::month_delta::*; -use crate::sub::*; use crate::timezone::*; use crate::to_julian::*; use crate::utc_offsets::*; @@ -14,12 +10,6 @@ use polars::prelude::*; use pyo3_polars::derive::polars_expr; use serde::Deserialize; use std::str::FromStr; -#[derive(Deserialize)] -pub struct BusinessDayKwargs { - holidays: Vec, - weekmask: [bool; 7], - roll: Option, -} #[derive(Deserialize)] pub struct FromLocalDatetimeKwargs { @@ -32,10 +22,6 @@ pub struct FormatLocalizedKwargs { locale: String, } -fn same_output(input_fields: &[Field]) -> PolarsResult { - let field = input_fields[0].clone(); - Ok(field) -} fn duration_ms(input_fields: &[Field]) -> PolarsResult { Ok(Field::new( input_fields[0].name(), @@ -68,26 +54,6 @@ pub fn from_local_datetime_output( Ok(Field::new(&field.name, dtype)) } -#[polars_expr(output_type_func=same_output)] -fn advance_n_days(inputs: &[Series], kwargs: BusinessDayKwargs) -> PolarsResult { - let s = &inputs[0]; - let n = &inputs[1].cast(&DataType::Int32)?; - let weekmask = kwargs.weekmask; - let holidays = kwargs.holidays; - let roll = kwargs.roll.unwrap(); - - impl_advance_n_days(s, n, holidays, &weekmask, &roll) -} - -#[polars_expr(output_type=Int32)] -fn workday_count(inputs: &[Series], kwargs: BusinessDayKwargs) -> PolarsResult { - let begin_dates = &inputs[0]; - let end_dates = &inputs[1]; - let weekmask = kwargs.weekmask; - let holidays = kwargs.holidays; - impl_workday_count(begin_dates, end_dates, &weekmask, holidays) -} - #[polars_expr(output_type=Int32)] fn month_delta(inputs: &[Series]) -> PolarsResult { let start_dates = &inputs[0]; @@ -95,14 +61,6 @@ fn month_delta(inputs: &[Series]) -> PolarsResult { impl_month_delta(start_dates, end_dates) } -#[polars_expr(output_type=Boolean)] -fn is_workday(inputs: &[Series], kwargs: BusinessDayKwargs) -> PolarsResult { - let dates = &inputs[0]; - let weekmask = kwargs.weekmask; - let holidays = kwargs.holidays; - impl_is_workday(dates, &weekmask, &holidays) -} - #[polars_expr(output_type_func=to_local_datetime_output)] fn to_local_datetime(inputs: &[Series]) -> PolarsResult { let s1 = &inputs[0]; @@ -175,31 +133,3 @@ fn arg_previous_greater(inputs: &[Series]) -> PolarsResult { dt => polars_bail!(ComputeError:"Expected numeric data type, got: {}", dt), } } - -#[derive(Deserialize)] -struct EwmTimeKwargs { - half_life: i64, -} - -#[polars_expr(output_type=Float64)] -fn ewma_by_time(inputs: &[Series], kwargs: EwmTimeKwargs) -> PolarsResult { - let values = &inputs[0]; - match &inputs[1].dtype() { - DataType::Datetime(_, _) => { - let time = &inputs[1].datetime().unwrap(); - Ok( - impl_ewma_by_time(&time.0, values, kwargs.half_life, time.time_unit()) - .into_series(), - ) - } - DataType::Date => { - let binding = &inputs[1].cast(&DataType::Datetime(TimeUnit::Milliseconds, None))?; - let time = binding.datetime().unwrap(); - Ok( - impl_ewma_by_time(&time.0, values, kwargs.half_life, time.time_unit()) - .into_series(), - ) - } - _ => polars_bail!(InvalidOperation: "First argument should be a date or datetime type."), - } -} diff --git a/src/is_workday.rs b/src/is_workday.rs deleted file mode 100644 index 9175821..0000000 --- a/src/is_workday.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::business_days::weekday; -use polars::prelude::*; - -fn is_workday_date(date: i32, weekmask: &[bool; 7], holidays: &[i32]) -> bool { - return unsafe { *weekmask.get_unchecked(weekday(date) as usize - 1) } - && (!holidays.contains(&date)); -} - -pub(crate) fn impl_is_workday( - dates: &Series, - weekmask: &[bool; 7], - holidays: &[i32], -) -> PolarsResult { - match dates.dtype() { - DataType::Date => { - let ca = dates.date()?; - let out: BooleanChunked = - ca.apply_values_generic(|date| is_workday_date(date, weekmask, holidays)); - Ok(out.into_series()) - } - DataType::Datetime(time_unit, _time_zone) => { - let multiplier = match time_unit { - TimeUnit::Milliseconds => 60 * 60 * 24 * 1_000, - TimeUnit::Microseconds => 60 * 60 * 24 * 1_000_000, - TimeUnit::Nanoseconds => 60 * 60 * 24 * 1_000_000_000, - }; - let ca = &polars_ops::prelude::replace_time_zone( - dates.datetime()?, - None, - &StringChunked::from_iter(std::iter::once("raise")), - NonExistent::Raise, - )?; - let out: BooleanChunked = ca.apply_values_generic(|date| { - is_workday_date((date / multiplier) as i32, weekmask, holidays) - }); - Ok(out.into_series()) - } - _ => { - polars_bail!(InvalidOperation: "polars_xdt is_workday only works on Date/Datetime type.") - } - } -} diff --git a/src/lib.rs b/src/lib.rs index bf6c212..f7995d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,7 @@ mod arg_previous_greater; -mod business_days; -mod ewma_by_time; mod expressions; mod format_localized; -mod is_workday; mod month_delta; -mod sub; mod timezone; mod to_julian; mod utc_offsets; diff --git a/src/sub.rs b/src/sub.rs deleted file mode 100644 index c9ced02..0000000 --- a/src/sub.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::business_days::weekday; -use polars::prelude::arity::binary_elementwise; -use polars::prelude::*; - -fn date_diff( - mut start_date: i32, - mut end_date: i32, - weekmask: &[bool; 7], - n_weekdays: i32, - holidays: &[i32], -) -> i32 { - let swapped = start_date > end_date; - if swapped { - (start_date, end_date) = (end_date, start_date); - start_date += 1; - end_date += 1; - } - - let holidays_begin = match holidays.binary_search(&start_date) { - Ok(x) => x, - Err(x) => x, - } as i32; - let holidays_end = match holidays.binary_search(&end_date) { - Ok(x) => x, - Err(x) => x, - } as i32; - - let mut start_weekday = weekday(start_date) as usize; - let diff = end_date - start_date; - let whole_weeks = diff / 7; - let mut count = -(holidays_end - holidays_begin); - count += whole_weeks * n_weekdays; - start_date += whole_weeks * 7; - while start_date < end_date { - if unsafe { *weekmask.get_unchecked(start_weekday - 1) } { - count += 1; - } - start_date += 1; - start_weekday += 1; - if start_weekday > 7 { - start_weekday = 1; - } - } - if swapped { - -count - } else { - count - } -} - -pub(crate) fn impl_workday_count( - start_dates: &Series, - end_dates: &Series, - weekmask: &[bool; 7], - holidays: Vec, -) -> PolarsResult { - if (start_dates.dtype() != &DataType::Date) || (end_dates.dtype() != &DataType::Date) { - polars_bail!(InvalidOperation: "polars_xdt.workday_count only works on Date type. Please cast to Date first."); - } - // Only keep holidays which aren't on weekends. - let holidays: Vec = { - holidays - .into_iter() - .filter(|x| unsafe { *weekmask.get_unchecked(weekday(*x) as usize - 1) }) - .collect() - }; - let start_dates = start_dates.date()?; - let end_dates = end_dates.date()?; - let n_weekdays = weekmask.iter().filter(|&x| *x).count() as i32; - let out = match (start_dates.len(), end_dates.len()) { - (_, 1) => { - if let Some(end_date) = end_dates.get(0) { - start_dates.apply(|x_date| { - x_date.map(|start_date| { - date_diff(start_date, end_date, weekmask, n_weekdays, &holidays) - }) - }) - } else { - Int32Chunked::full_null(start_dates.name(), start_dates.len()) - } - } - (1, _) => { - if let Some(start_date) = start_dates.get(0) { - end_dates.apply(|x_date| { - x_date.map(|end_date| { - date_diff(start_date, end_date, weekmask, n_weekdays, &holidays) - }) - }) - } else { - Int32Chunked::full_null(start_dates.name(), start_dates.len()) - } - } - _ => binary_elementwise(start_dates, end_dates, |opt_s, opt_n| { - match (opt_s, opt_n) { - (Some(start_date), Some(end_date)) => Some(date_diff( - start_date, end_date, weekmask, n_weekdays, &holidays, - )), - _ => None, - } - }), - }; - Ok(out.into_series()) -} diff --git a/tests/test_business_offsets.py b/tests/test_business_offsets.py deleted file mode 100644 index 596b74b..0000000 --- a/tests/test_business_offsets.py +++ /dev/null @@ -1,266 +0,0 @@ -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING, Any, Callable, Literal - -import hypothesis.strategies as st -import numpy as np -import polars as pl -import pytest -from hypothesis import assume, given, reject -from polars.testing import assert_frame_equal - -import polars_xdt as xdt - -if TYPE_CHECKING: - from polars.type_aliases import PolarsDataType - -mapping = {"Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, "Sun": 7} -reverse_mapping = {value: key for key, value in mapping.items()} - - -def get_result( - date: dt.date, - dtype: PolarsDataType, - by: str | pl.Series, - **kwargs: Any, -) -> dt.date: - if dtype == pl.Date: - result = ( - pl.DataFrame({"ts": [date]}) - .select(xdt.offset_by("ts", by=by, **kwargs))["ts"] # type: ignore[arg-type] - .item() - ) - else: - try: - result = ( - pl.DataFrame( - {"ts": [dt.datetime(date.year, date.month, date.day)]} - ) - .select( - xdt.offset_by( - pl.col("ts") - .dt.cast_time_unit(dtype.time_unit) # type: ignore[union-attr] - .dt.replace_time_zone(dtype.time_zone), # type: ignore[union-attr] - by=by, - **kwargs, # type: ignore[arg-type] - ).dt.date() - )["ts"] - .item() - ) - except Exception as exp: # noqa: BLE001 - assert "non-existent" in str(exp) or "ambiguous" in str(exp) # noqa: PT017 - reject() - return result # type: ignore[no-any-return] - - -@given( - date=st.dates( - min_value=dt.date(1969, 1, 1), max_value=dt.date(1971, 12, 31) - ), - n=st.integers(min_value=-30, max_value=30), - weekend=st.lists( - st.sampled_from(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]), - min_size=0, - max_size=6, - unique=True, - ), - holidays=st.lists( - st.dates( - min_value=dt.date(1969, 1, 1), max_value=dt.date(1971, 12, 31) - ), - min_size=1, - max_size=300, - ), - dtype=st.sampled_from( - [ - pl.Date, - pl.Datetime("ms"), - pl.Datetime("ms", "Asia/Kathmandu"), - pl.Datetime("us", "Europe/London"), - ] - ), - function=st.sampled_from([lambda x: x, lambda x: pl.Series([x])]), -) -def test_against_np_busday_offset( # noqa: PLR0913 - date: dt.date, - n: int, - weekend: list[str], - holidays: list[dt.date], - dtype: PolarsDataType, - function: Callable[[str], str | pl.Series], -) -> None: - assume(date.strftime("%a") not in weekend) - assume(date not in holidays) - roll = "raise" - with pytest.deprecated_call(): - result = get_result( - date, - dtype, - by=function(f"{n}bd"), - weekend=weekend, - holidays=holidays, - roll=roll, - ) - weekmask = [0 if reverse_mapping[i] in weekend else 1 for i in range(1, 8)] - expected = np.busday_offset(date, n, weekmask=weekmask, holidays=holidays) - assert np.datetime64(result) == expected - - -@given( - date=st.dates( - min_value=dt.date(1969, 1, 1), max_value=dt.date(1971, 12, 31) - ), - n=st.integers(min_value=-30, max_value=30), - weekend=st.lists( - st.sampled_from(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]), - min_size=0, - max_size=6, - unique=True, - ), - holidays=st.lists( - st.dates( - min_value=dt.date(1969, 1, 1), max_value=dt.date(1971, 12, 31) - ), - min_size=1, - max_size=300, - ), - dtype=st.sampled_from( - [ - pl.Date, - pl.Datetime("ms"), - pl.Datetime("ms", "Asia/Kathmandu"), - pl.Datetime("us", "Europe/London"), - ] - ), - function=st.sampled_from([lambda x: x, lambda x: pl.Series([x])]), - roll=st.sampled_from(["forward", "backward"]), -) -def test_against_np_busday_offset_with_roll( # noqa:PLR0913 - date: dt.date, - n: int, - weekend: list[str], - holidays: list[dt.date], - dtype: PolarsDataType, - function: Callable[[str], str | pl.Series], - roll: Literal["forward", "backward"], -) -> None: - with pytest.deprecated_call(): - result = get_result( - date, - dtype, - by=function(f"{n}bd"), - weekend=weekend, - holidays=holidays, - roll=roll, # type: ignore[arg-type] - ) - weekmask = [0 if reverse_mapping[i] in weekend else 1 for i in range(1, 8)] - expected = np.busday_offset( - date, n, weekmask=weekmask, holidays=holidays, roll=roll - ) - assert np.datetime64(result) == expected - - -@pytest.mark.parametrize( - ("by", "expected"), - [ - ("1bd", dt.datetime(2000, 1, 4)), - ("2bd", dt.datetime(2000, 1, 5)), - ("1bd2h", dt.datetime(2000, 1, 4, 2)), - ("2h1bd", dt.datetime(2000, 1, 4, 2)), - ("2bd1h", dt.datetime(2000, 1, 5, 1)), - ("-1bd", dt.datetime(1999, 12, 31)), - ("-2bd", dt.datetime(1999, 12, 30)), - ("-1bd2h", dt.datetime(1999, 12, 30, 22)), - ("-2h1bd", dt.datetime(1999, 12, 30, 22)), - ("-2bd1h", dt.datetime(1999, 12, 29, 23)), - ], -) -def test_extra_args(by: str, expected: dt.datetime) -> None: - start = dt.datetime(2000, 1, 3) - df = pl.DataFrame({"dates": [start]}) - with pytest.deprecated_call(): - result = ( - df.with_columns( - dates_shifted=xdt.offset_by("dates", by=by) - ).with_columns(end_wday=pl.col("dates_shifted").dt.strftime("%a")) - )["dates_shifted"].item() - assert result == expected - - -def test_extra_args_w_series() -> None: - start = dt.datetime(2000, 1, 3) - df = pl.DataFrame({"dates": [start] * 2, "by": ["1bd2h", "-1bd1h"]}) - with pytest.deprecated_call(): - result = ( - df.with_columns( - dates_shifted=xdt.offset_by("dates", by=pl.col("by")) - ).with_columns(end_wday=pl.col("dates_shifted").dt.strftime("%a")) - )["dates_shifted"] - assert result[0] == dt.datetime(2000, 1, 4, 2) - assert result[1] == dt.datetime(1999, 12, 30, 23) - - -def test_starting_on_non_business() -> None: - start = dt.date(2000, 1, 1) - n = -7 - weekend = ["Sat", "Sun"] - df = pl.DataFrame({"dates": [start]}) - with pytest.raises(pl.ComputeError), pytest.deprecated_call(): - df.with_columns( - dates_shifted=xdt.offset_by( - "dates", - by=f"{n}bd", - weekend=weekend, - ) - ) - - df = pl.DataFrame({"dates": [start]}) - weekend = [] - holidays = [start] - with pytest.raises(pl.ComputeError), pytest.deprecated_call(): - df.with_columns( - dates_shifted=xdt.offset_by( - "dates", - by=f"{n}bd", - holidays=holidays, - weekend=weekend, - ) - ) - - -def test_within_group_by() -> None: - data = { - "a": [1, 2], - "date": [dt.datetime(2022, 2, 1), dt.datetime(2023, 2, 1)], - } - df = pl.DataFrame(data) - - with pytest.deprecated_call(): - result = ( - df.group_by(["a"]).agg( - minDate=xdt.offset_by(pl.col.date.min(), "-3bd"), # type: ignore[attr-defined] - maxDate=xdt.offset_by(pl.col.date.max(), "3bd"), # type: ignore[attr-defined] - ) - ).sort("a", descending=True) - expected = pl.DataFrame( - { - "a": [2, 1], - "minDate": [dt.datetime(2023, 1, 27), dt.datetime(2022, 1, 27)], - "maxDate": [dt.datetime(2023, 2, 6), dt.datetime(2022, 2, 4)], - } - ) - assert_frame_equal(result, expected) - - -def test_invalid_roll_strategy() -> None: - df = pl.DataFrame( - { - "date": pl.date_range( - dt.date(2023, 12, 1), dt.date(2023, 12, 5), eager=True - ) - } - ) - with pytest.raises(pl.ComputeError): # noqa: SIM117 - with pytest.deprecated_call(): - df.with_columns(xdt.offset_by("date", "1bd", roll="cabbage")) # type: ignore[arg-type] diff --git a/tests/test_ewma_by_time.py b/tests/test_ewma_by_time.py deleted file mode 100644 index 60079fc..0000000 --- a/tests/test_ewma_by_time.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -from datetime import datetime, timedelta - -import polars as pl -import pytest -from polars.testing import assert_frame_equal - -import polars_xdt as xdt - -os.environ["POLARS_VERBOSE"] = "1" - - -@pytest.mark.parametrize("start_null", [True, False]) -def test_ewma_by_time(*, start_null: bool) -> None: - if start_null: - values = [None] - times = [datetime(2020, 1, 1)] - else: - values = [] - times = [] - - df = pl.DataFrame( - { - "values": [*values, 0.0, 1.0, 2.0, None, 4.0], - "times": [ - *times, - datetime(2020, 1, 2), - datetime(2020, 1, 4), - datetime(2020, 1, 11), - datetime(2020, 1, 16), - datetime(2020, 1, 18), - ], - } - ) - with pytest.deprecated_call(): - result = df.select( - xdt.ewma_by_time( - "values", times="times", half_life=timedelta(days=2) - ), - ) - - expected = pl.DataFrame( - { - "values": [ - *values, - 0.0, - 0.5, - 1.8674174785275222, - None, - 3.811504554703363, - ] - } - ) - - assert_frame_equal(result, expected) diff --git a/tests/test_sub.py b/tests/test_sub.py deleted file mode 100644 index 6cc4ad7..0000000 --- a/tests/test_sub.py +++ /dev/null @@ -1,181 +0,0 @@ -from __future__ import annotations - -import datetime as dt -from typing import Callable - -import hypothesis.strategies as st -import numpy as np -import polars as pl -import pytest -from hypothesis import assume, given, reject - -import polars_xdt as xdt - -mapping = {"Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, "Sun": 7} -reverse_mapping = {value: key for key, value in mapping.items()} - - -def get_result( - start_date: dt.date | pl.Series, - end_date: dt.date, - weekend: list[str], - holidays: list[dt.date], -) -> int: - with pytest.deprecated_call(): - return ( # type: ignore[no-any-return] - pl.DataFrame({"end_date": [end_date]}) - .select( - n=xdt.workday_count( - start_date, "end_date", weekend=weekend, holidays=holidays - ) - )["n"] # type: ignore[arg-type] - .item() - ) - - -@given( - start_date=st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - end_date=st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - function=st.sampled_from([lambda x: x, lambda x: pl.Series([x])]), - weekend=st.lists( - st.sampled_from(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]), - min_size=0, - max_size=6, - unique=True, - ), - holidays=st.lists( - st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - min_size=1, - max_size=300, - ), -) -def test_against_np_busday_count( - start_date: dt.date, - end_date: dt.date, - weekend: list[str], - holidays: list[dt.date], - function: Callable[[dt.date], dt.date | pl.Series], -) -> None: - result = get_result( - function(start_date), end_date, weekend=weekend, holidays=holidays - ) - weekmask = [0 if reverse_mapping[i] in weekend else 1 for i in range(1, 8)] - expected = np.busday_count( - start_date, end_date, weekmask=weekmask, holidays=holidays - ) - if start_date > end_date and tuple( - int(v) for v in np.__version__.split(".")[:2] - ) < (1, 25): - # Bug in old versions of numpy - reject() - assert result == expected - - -@given( - start_date=st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - end_date=st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - weekend=st.lists( - st.sampled_from(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]), - min_size=0, - max_size=6, - unique=True, - ), - holidays=st.lists( - st.dates( - min_value=dt.date(2000, 1, 1), max_value=dt.date(2000, 12, 31) - ), - min_size=1, - max_size=300, - ), -) -def test_against_naive_python( - start_date: dt.date, - end_date: dt.date, - weekend: list[str], - holidays: list[dt.date], -) -> None: - assume(end_date > start_date) - result = get_result( - start_date, end_date, weekend=weekend, holidays=holidays - ) - expected = 0 - start_date_copy = start_date - while start_date_copy < end_date: - if start_date_copy.strftime("%a") in weekend: - start_date_copy += dt.timedelta(days=1) - continue - if start_date_copy in holidays: - start_date_copy += dt.timedelta(days=1) - continue - start_date_copy += dt.timedelta(days=1) - expected += 1 - assert result == expected - - -def test_empty_weekmask() -> None: - df = pl.DataFrame( - { - "start": [dt.date(2020, 1, 1)], - "end": [dt.date(2020, 1, 5)], - } - ) - with pytest.raises(ValueError): # noqa: SIM117 - with pytest.deprecated_call(): - df.select( - xdt.workday_count( - "start", - "end", - weekend=["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - ) - ) - - -def test_sub_lit() -> None: - df = pl.DataFrame( - { - "end": [dt.date(2020, 1, 3), dt.date(2020, 1, 5)], - } - ) - with pytest.deprecated_call(): - result = df.select( - xdt.workday_count(pl.lit(dt.date(2020, 1, 1)), "end") - ) - assert result["literal"][0] == 2 - assert result["literal"][1] == 3 - - -def test_workday_count() -> None: - df = pl.DataFrame( - { - "start": [dt.date(2020, 1, 3), dt.date(2020, 1, 5)], - "end": [dt.date(2020, 1, 8), dt.date(2020, 1, 20)], - } - ) - with pytest.deprecated_call(): - result = df.with_columns( - workday_count=xdt.workday_count("start", "end") - ) - assert result["workday_count"][0] == 3 - assert result["workday_count"][1] == 10 - with pytest.deprecated_call(): - result = df.with_columns( - workday_count=xdt.workday_count("start", dt.date(2020, 1, 8)) - ) - assert result["workday_count"][0] == 3 - assert result["workday_count"][1] == 2 - with pytest.deprecated_call(): - result = df.with_columns( - workday_count=xdt.workday_count(dt.date(2020, 1, 5), pl.col("end")) - ) - assert result["workday_count"][0] == 2 - assert result["workday_count"][1] == 10 diff --git a/tests/test_timezone.py b/tests/test_timezone.py index 91f6ed6..d1c4e78 100644 --- a/tests/test_timezone.py +++ b/tests/test_timezone.py @@ -118,6 +118,7 @@ def test_convert_tz_to_local_datetime_literal() -> None: assert_frame_equal(result, expected) +@pytest.mark.filterwarnings("ignore:Resolving the schema of a LazyFrame") def test_convert_tz_to_local_datetime_schema() -> None: df = pl.LazyFrame({"date": [datetime(2020, 10, 15, tzinfo=timezone.utc)]}) result = df.with_columns(