Repository navigation
Add fill_null(exprs, strategy='inverse') #12925
Copy link
Copy link
Closed as not planned
Labels
enhancementNew feature or an improvement of an existing featureNew feature or an improvement of an existing feature
Description
Description
I have always want functions to support skip nan, because in the financial field, null values appear very frequently.
Recently I finally find a way, which is to use fill_null to fill back.
How to implement?
before call function,first drop_nulls, then call function, finally create array and fill it with results from tail to head, if meet null, skip it.
Here's how to use it:
import polars as pl
df = pl.DataFrame(
{
"A": [5, None, 3, 2, 1],
"B": [5, 3, None, 2, 1],
"C": [None, None, None, None, None],
}
)
print(df)
"""
┌──────┬──────┬──────┐
│ A ┆ B ┆ C │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ f32 │
╞══════╪══════╪══════╡
│ 5 ┆ 5 ┆ null │
│ null ┆ 3 ┆ null │
│ 3 ┆ null ┆ null │
│ 2 ┆ 2 ┆ null │
│ 1 ┆ 1 ┆ null │
└──────┴──────┴──────┘
"""
df1 = df.select([
pl.col("A", "B", "C").rolling_mean(2),
])
print(df1)
"""
┌──────┬──────┬──────┐
│ A ┆ B ┆ C │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f32 │
╞══════╪══════╪══════╡
│ null ┆ null ┆ null │
│ null ┆ 4.0 ┆ null │
│ null ┆ null ┆ null │
│ 2.5 ┆ null ┆ null │
│ 1.5 ┆ 1.5 ┆ null │
└──────┴──────┴──────┘
"""
# Implemented the function of ignoring nan, but the position and length need to be adjusted
df2 = df.select([
pl.col("A", "B").drop_nulls().rolling_mean(2),
])
print(df2)
df2 = df.select([
pl.col("C").drop_nulls().rolling_mean(2),
])
print(df2)
"""
┌──────┬──────┐
│ A ┆ B │
│ --- ┆ --- │
│ f64 ┆ f64 │
╞══════╪══════╡
│ null ┆ null │
│ 4.0 ┆ 4.0 │
│ 2.5 ┆ 2.5 │
│ 1.5 ┆ 1.5 │
└──────┴──────┘
shape: (0, 1)
┌─────┐
│ C │
│ --- │
│ f32 │
╞═════╡
└─────┘
"""
# This should be exactly the same as the original data
df3 = df.with_columns([
pl.col("A", "B", "C").drop_nulls().fill_null(pl.col("A", "B", "C"), strategy='inverse'),
])
print(df3)
"""
┌──────┬──────┬──────┐
│ A ┆ B ┆ C │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ f32 │
╞══════╪══════╪══════╡
│ 5 ┆ 5 ┆ null │
│ null ┆ 3 ┆ null │
│ 3 ┆ null ┆ null │
│ 2 ┆ 2 ┆ null │
│ 1 ┆ 1 ┆ null │
└──────┴──────┴──────┘
"""
# Implement skip nan
df4 = df.with_columns([
pl.col("A", "B", "C").drop_nulls().rolling_mean(2).fill_null(pl.col("A", "B", "C"), strategy='inverse'),
])
print(df4)
"""
┌──────┬──────┬──────┐
│ A ┆ B ┆ C │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ f32 │
╞══════╪══════╪══════╡
│ null ┆ null ┆ null │
│ null ┆ 4.0 ┆ null │
│ 4.0 ┆ null ┆ null │
│ 2.5 ┆ 2.5 ┆ null │
│ 1.5 ┆ 1.5 ┆ null │
└──────┴──────┴──────┘
"""Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or an improvement of an existing featureNew feature or an improvement of an existing feature
Activity
wukan1986 commentedon Dec 8, 2023
Maybe the follow code is better
stinodego commentedon Dec 15, 2023
I don't understand what you're trying to do here.
From what I understand, you're trying to use
fill_nullto add null values. We will not support that. Though we may implement a different method to do what you're trying to do.Please try to describe more clearly what you're trying to do and give a simple example.
wukan1986 commentedon Dec 16, 2023
Hi @stinodego
My purpose is to simplify handling of nan/null when calculating time series indicators.
The possible situations where null values may appear are:
indicators such as
rolling_*will have problems, andtalibwill also have problems. So I want to be able to skip null values. see#9131
It is unrealistic to implement the skip function for every time series function, it will be simplified if drop nan first and then backfill.
rolling_mean_S&P 500is allnan/nulland does not meet the requirements. Because the results I actually need to calculate are as follows.I wrote a demo code for reference
MarcoGorelli commentedon Dec 16, 2023
Hi @wukan1986
thanks for the request
As far as I can tell (please correct me if I'm wrong), you'd like a way to skip nulls in some rolling functions. I think we're confusing nan and null here, so let's stick to just skipping nulls (you can always fill nans with nulls if you want to skip both).
If so, would the following work for you?
If so, then for now I think this is probably OK, and doesn't warrant extra arguments?
wukan1986 commentedon Dec 17, 2023
Hi, @MarcoGorelli
Thank you for reply, I get a PanicException, it is caused by
pl.col("C")wukan1986 commentedon Dec 17, 2023
Hi, @MarcoGorelli
There are two requirements
but my expectation is
rolling(period="2i")is not enoughMarcoGorelli commentedon Dec 17, 2023
Thanks @wukan1986 for explaining, that's a clear example
So I think you also have a third requirement:
null, right?
How about
, I think this would give you your expected output?
stinodego commentedon Dec 17, 2023
Yes, it seems you can do this with a combination of
min_periodsandwhen(col.is_not_null()).then(rolling_expr).I don't think we need to add anything new here. Thanks for opening an issue though! Closing this now unless I see a good reason to re-open.
MarcoGorelli commentedon Dec 17, 2023
not sure that's quite to same - if I you try applying it to
you'll get
, whereas
min_periods=2would give you all-nullsBut the expect output is
I think being able to express
would be quite nice
It is possible to work around
skip_nullsin rolling aggregations, it's just not that easyMarcoGorelli commentedon Dec 17, 2023
So, to summarise, I think this boils down to a feature request of
skip_nullsinrolling_*functionsTrue, then ignore the null values when aggregatingFalse, then returnnullif a null value is present (just like happens now - so this would probably be a good default)wukan1986 commentedon Dec 17, 2023
the output is not my want.
wukan1986 commentedon Dec 17, 2023
rolling_*andmap_batchesaddskip_nullsis a lot of work.so i suggest to add a function
fill_null or resizesee this #12925 (comment) function
resizeMarcoGorelli commentedon Dec 17, 2023
Right, so just checking I've understood, what you want is:
nulln-1previous non-null values, then aggregate themn-1previous non-null values, then outputnull?
wukan1986 commentedon Dec 17, 2023
I think you are overcomplicating the issue
In fact, it is just a function that moves the elements of a short array into a long array as required.
The aggregation operation is done elsewhere, so that all rolling functions do not need to be modified.
for an example
The pseudo code is as follows
We do not consider aggregation, aggregation is just one usage scenario
MarcoGorelli commentedon Dec 17, 2023
I think you just need
scatterthen?This gives
wukan1986 commentedon Dec 17, 2023
That's great. This is what I want. It would be better if it could support Expr.
MarcoGorelli commentedon Dec 17, 2023
agree - fancy opening a new issue in which you just request
Expr.scatter?cmdlineluser commentedon Dec 17, 2023
Not sure if it's of any use, but this is how it looks to me using the current API:
wukan1986 commentedon Dec 18, 2023
Hi, @cmdlineluser
Your
expr.cum_count().filter(expr.is_not_null())is very creativeruoyu0088 commentedon Dec 19, 2024
You can use
.over()to do this:wukan1986 commentedon Dec 19, 2024
@ruoyu0088 Thank you very much, your idea is very good