Skip to content

Conversation

@mcrumiller
Copy link
Contributor

@mcrumiller mcrumiller commented Nov 13, 2025

Closes #7525
Closes #23424

This update adds the num_samples parameter to date_range and datetime_range. The input parameters that define the range are now: start, end, interval, and num_samples. A range can be generated using any three of these parameters. In order to keep backwards compatibility, and for the most common usage, the two-parameter combination of start and end works, and uses a default duration of 1 day.

This update also allows for ranges generated using negative intervals, in much the same way that linear_space and int_range can generate backwards ranges. Note that the closed parameter applies these as directional intervals, meaning that a left-closed series always has the form [s1, s2, ..., s_n) even if the values are descending.

This had the minor side effect of enabling negative intervals in time_range as well, with no additional effort, so I threw in a quick test for that. We can manually disable that by adding in a check if we don't want that in this PR.

Examples

import polars as pl
from polars import col
from datetime import date

df = pl.DataFrame({
    "start": [date(2025, 1, 1), date(2025, 1, 2)],
    "end": [date(2025, 1, 10), date(2025, 1, 11)],
    "num_samples": [5, 6],
})

df.select(
    start_end_samples=pl.datetime_range(start=col("start").min(), end=col("end").max(), num_samples=6),
    start_interval_samples=pl.datetime_range(start=col("start").min(), interval="-2d", num_samples=col("num_samples").min() + 1),
    end_interval_samples=pl.datetime_range(end=col("end").max(), interval="1mo", num_samples=col("num_samples").max()),
    start_end_interval=pl.datetime_range(start=col("start").min(), end=col("end").max(), interval="2d"),
)
# shape: (6, 4)
# ┌─────────────────────┬────────────────────────┬──────────────────────┬─────────────────────┐
# │ start_end_samples   ┆ start_interval_samples ┆ end_interval_samples ┆ start_end_interval  │
# │ ---                 ┆ ---                    ┆ ---                  ┆ ---                 │
# │ datetime[μs]        ┆ datetime[μs]           ┆ datetime[μs]         ┆ datetime[μs]        │
# ╞═════════════════════╪════════════════════════╪══════════════════════╪═════════════════════╡
# │ 2025-01-01 00:00:00 ┆ 2025-01-01 00:00:00    ┆ 2024-08-11 00:00:00  ┆ 2025-01-01 00:00:00 │
# │ 2025-01-03 00:00:00 ┆ 2024-12-30 00:00:00    ┆ 2024-09-11 00:00:00  ┆ 2025-01-03 00:00:00 │
# │ 2025-01-05 00:00:00 ┆ 2024-12-28 00:00:00    ┆ 2024-10-11 00:00:00  ┆ 2025-01-05 00:00:00 │
# │ 2025-01-07 00:00:00 ┆ 2024-12-26 00:00:00    ┆ 2024-11-11 00:00:00  ┆ 2025-01-07 00:00:00 │
# │ 2025-01-09 00:00:00 ┆ 2024-12-24 00:00:00    ┆ 2024-12-11 00:00:00  ┆ 2025-01-09 00:00:00 │
# │ 2025-01-11 00:00:00 ┆ 2024-12-22 00:00:00    ┆ 2025-01-11 00:00:00  ┆ 2025-01-11 00:00:00 │
# └─────────────────────┴────────────────────────┴──────────────────────┴─────────────────────┘

pl.Config.set_fmt_table_cell_list_len(999)
pl.Config.set_fmt_str_lengths(999)
pl.Config.set_tbl_width_chars(9999)

df.select(
    start_interval_samples=pl.datetime_ranges(start="start", interval="1d12h", num_samples=3, closed="right"),
    start_end_samples=pl.datetime_ranges(start="start", end="end", num_samples=3, closed="none"),
)
# shape: (2, 2)
# ┌─────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────┐
# │ start_interval_samples                                          ┆ start_end_samples                                               │
# │ ---                                                             ┆ ---                                                             │
# │ list[datetime[μs]]                                              ┆ list[datetime[μs]]                                              │
# ╞═════════════════════════════════════════════════════════════════╪═════════════════════════════════════════════════════════════════╡
# │ [2025-01-02 12:00:00, 2025-01-04 00:00:00, 2025-01-05 12:00:00] ┆ [2025-01-03 06:00:00, 2025-01-05 12:00:00, 2025-01-07 18:00:00] │
# │ [2025-01-03 12:00:00, 2025-01-05 00:00:00, 2025-01-06 12:00:00] ┆ [2025-01-04 06:00:00, 2025-01-06 12:00:00, 2025-01-08 18:00:00] │
# └─────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────┘

@github-actions github-actions bot added enhancement New feature or an improvement of an existing feature python Related to Python Polars rust Related to Rust Polars labels Nov 13, 2025
&s[1],
swap_closed_lr(closed),
)
.map(|c| c.reverse()),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a shortcut here; if the user wants date_range(end=e, interval=i, num_samples=n), this is equivalent to using date_range(start=e, interval=-i, num_samples=n).reverse() with the closed parameter left-right swapped. This obviously adds a bit more computation for this one specific case, with the benefit of code re-use.

@mcrumiller mcrumiller force-pushed the dt-range-num-samples branch 2 times, most recently from 20ab24c to 294d3b9 Compare November 14, 2025 03:29
@mcrumiller mcrumiller marked this pull request as ready for review November 14, 2025 03:55
@codecov
Copy link

codecov bot commented Nov 14, 2025

Codecov Report

❌ Patch coverage is 91.85520% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.12%. Comparing base (c11ffe2) to head (c874fef).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
crates/polars-time/src/date_range.rs 83.58% 33 Missing ⚠️
...s/polars-expr/src/dispatch/range/datetime_range.rs 95.41% 10 Missing ⚠️
crates/polars-expr/src/dispatch/range/utils.rs 93.87% 9 Missing ⚠️
crates/polars-python/src/functions/range.rs 96.29% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25329      +/-   ##
==========================================
+ Coverage   82.05%   82.12%   +0.06%     
==========================================
  Files        1711     1711              
  Lines      237142   237635     +493     
  Branches     3011     3017       +6     
==========================================
+ Hits       194594   195149     +555     
+ Misses      41775    41713      -62     
  Partials      773      773              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or an improvement of an existing feature python Related to Python Polars rust Related to Rust Polars

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow negative intervals in date_range and datetime_range adding periods argument to the date_range

1 participant