Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add dt.replace #19708

Merged
merged 17 commits into from
Dec 20, 2024
Merged

feat: Add dt.replace #19708

merged 17 commits into from
Dec 20, 2024

Conversation

mcrumiller
Copy link
Contributor

@mcrumiller mcrumiller commented Nov 9, 2024

Closes #8879.

@MarcoGorelli since this involves constructing new Date/Datetime chunked arrays, I moved some of the fn datetime() construction code that needed to be reused into DateChunked::new_from_parts and DatetimeChunked::new_from_parts.

Examples

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

# -- Series
s = pl.Series([date(2024, 1, 1), date(2024, 1, 2), date(2024, 1, 3)])
s.dt.replace(month=2, day=15)
# shape: (3,)
# Series: '' [date]
# [
#         2024-02-15
#         2024-02-15
#         2024-02-15
# ]

# -- Expr
df = pl.DataFrame({
    "a": [datetime(1, 1, 1, 1, 1, 1, 1)],
})
df.with_columns(
    col("a").dt.replace(year=2024).alias("year"),
    col("a").dt.replace(year=2015, day=23, second=45, microsecond=5000).alias("many"),
)
# shape: (1, 3)
# ┌────────────────────────────┬────────────────────────────┬─────────────────────────┐
# │ a                          ┆ year                       ┆ many                    │
# │ ---                        ┆ ---                        ┆ ---                     │
# │ datetime[μs]               ┆ datetime[μs]               ┆ datetime[μs]            │
# ╞════════════════════════════╪════════════════════════════╪═════════════════════════╡
# │ 0001-01-01 01:01:01.000001 ┆ 2024-01-01 01:01:01.000001 ┆ 2015-01-23 01:01:45.005 │
# └────────────────────────────┴────────────────────────────┴─────────────────────────┘

# -- Expr using other columns
df = pl.DataFrame({
    "date": [date(2024, 1, 1), date(2024, 1, 2)],
    "day": [6, 13],
})
df.with_columns(
    col("date").dt.replace(day="day")
)
# shape: (2, 2)
# ┌────────────┬─────┐
# │ date       ┆ day │
# │ ---        ┆ --- │
# │ date       ┆ i64 │
# ╞════════════╪═════╡
# │ 2024-01-06 ┆ 6   │
# │ 2024-01-13 ┆ 13  │
# └────────────┴─────┘

@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 9, 2024
@mcrumiller
Copy link
Contributor Author

@MarcoGorelli I could also perhaps use some help creating tests to cover the non_existent parameter and the ambiguous parameter, neither of which my tests cover.

@MarcoGorelli
Copy link
Collaborator

@MarcoGorelli I could also perhaps use some help creating tests to cover the non_existent parameter and the ambiguous parameter, neither of which my tests cover.

sure - would it work to start with

datetime(2020, 10, 25)

and then do .dt.replace(hour=1)?

@mcrumiller
Copy link
Contributor Author

sure - would it work to start with

datetime(2020, 10, 25)

and then do .dt.replace(hour=1)?

Thanks--looks like I need to use Europe/London for this to work (found in another unit test).

Copy link

codecov bot commented Nov 13, 2024

Codecov Report

Attention: Patch coverage is 98.56115% with 4 lines in your changes missing coverage. Please review.

Project coverage is 79.63%. Comparing base (91ad299) to head (33019a1).
Report is 36 commits behind head on main.

Files with missing lines Patch % Lines
...ates/polars-plan/src/dsl/function_expr/datetime.rs 94.59% 2 Missing ⚠️
.../polars-python/src/lazyframe/visitor/expr_nodes.rs 0.00% 1 Missing ⚠️
crates/polars-time/src/chunkedarray/datetime.rs 97.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #19708      +/-   ##
==========================================
+ Coverage   79.60%   79.63%   +0.02%     
==========================================
  Files        1567     1568       +1     
  Lines      218398   218628     +230     
  Branches     2465     2465              
==========================================
+ Hits       173866   174095     +229     
- Misses      43963    43964       +1     
  Partials      569      569              

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

Copy link
Collaborator

@MarcoGorelli MarcoGorelli left a comment

Choose a reason for hiding this comment

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

thanks for working on this, it'll probably be useful to people

@mcrumiller mcrumiller marked this pull request as draft November 17, 2024 14:30
@mcrumiller mcrumiller marked this pull request as ready for review November 17, 2024 15:06
@mcrumiller mcrumiller marked this pull request as draft November 17, 2024 17:23
@mcrumiller mcrumiller marked this pull request as ready for review November 17, 2024 18:11
@mcrumiller mcrumiller marked this pull request as ready for review December 7, 2024 22:00
Copy link
Collaborator

@MarcoGorelli MarcoGorelli left a comment

Choose a reason for hiding this comment

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

thanks - the microsecond / nanosecond part looks a bit off?

In [12]: df = pl.DataFrame({'a': ['2020-01-01T00:00:00.123456789']*2}).select(pl.col('a').str.to_dat
    ...: etime(time_unit='ns'))

In [13]: df
Out[13]: 
shape: (2, 1)
┌───────────────────────────────┐
│ a                             │
│ ---                           │
│ datetime[ns]                  │
╞═══════════════════════════════╡
│ 2020-01-01 00:00:00.123456789 │
│ 2020-01-01 00:00:00.123456789 │
└───────────────────────────────┘

In [14]: df['a'].dt.replace(year=2021)
Out[14]: 
shape: (2,)
Series: 'a' [datetime[ns]]
[
        2021-01-01 00:00:00.123456
        2021-01-01 00:00:00.123456
]

crates/polars-time/src/replace.rs Outdated Show resolved Hide resolved
@mcrumiller mcrumiller marked this pull request as draft December 8, 2024 16:34
@mcrumiller mcrumiller marked this pull request as ready for review December 9, 2024 15:01
Copy link
Collaborator

@MarcoGorelli MarcoGorelli left a comment

Choose a reason for hiding this comment

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

print statement

crates/polars-time/src/replace.rs Outdated Show resolved Hide resolved
@MarcoGorelli
Copy link
Collaborator

😄 no worries, thanks for updating

looks like there's another merge conflict, sorry

@mcrumiller
Copy link
Contributor Author

looks like there's another merge conflict, sorry

Figured that would happen that with that PR. Updated.

Copy link
Collaborator

@MarcoGorelli MarcoGorelli left a comment

Choose a reason for hiding this comment

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

Nice one, thanks @mcrumiller !

Leaving open a bit in case there's objections

separately to this PR, as a follow-up, py-polars/tests/unit/operations/namespaces/temporal/test_datetime.py has become very big, I think it'd be worth splitting these dt.replace tests into a separate test file

@MarcoGorelli
Copy link
Collaborator

There's been no objections, and I think this feature would be really ergonomic (and higher-perf than some current alternatives involving strftime / strptime string hacking), so I'd say let's ship it - thanks @mcrumiller !

@MarcoGorelli MarcoGorelli merged commit 8757ad0 into pola-rs:main Dec 20, 2024
26 checks passed
@mcrumiller mcrumiller deleted the dt-replace branch December 20, 2024 16:14
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.

Add .dt.replace
5 participants