在 pandas 中,我可以根据这样的日期时间进行插值:
df1 = pd.DataFrame(
{
"ts": [
datetime(2020, 1, 1),
datetime(2020, 1, 3, 0, 0, 12),
datetime(2020, 1, 3, 0, 1, 35),
datetime(2020, 1, 4),
],
"value": [1, np.nan, np.nan, 3],
}
)
df1.set_index('ts').interpolate(method='index')
输出:
value
ts
2020-01-01 00:00:00 1.000000
2020-01-03 00:00:12 2.333426
2020-01-03 00:01:35 2.334066
2020-01-04 00:00:00 3.000000
polars中有类似的方法吗?说,从
开始df1 = pl.DataFrame(
{
"ts": [
datetime(2020, 1, 1),
datetime(2020, 1, 3, 0, 0, 12),
datetime(2020, 1, 3, 0, 1, 35),
datetime(2020, 1, 4),
],
"value": [1, None, None, 3],
}
)
shape: (4, 2)
┌─────────────────────┬───────┐
│ ts ┆ value │
│ --- ┆ --- │
│ datetime[μs] ┆ i64 │
╞═════════════════════╪═══════╡
│ 2020-01-01 00:00:00 ┆ 1 │
│ 2020-01-03 00:00:12 ┆ null │
│ 2020-01-03 00:01:35 ┆ null │
│ 2020-01-04 00:00:00 ┆ 3 │
└─────────────────────┴───────┘
编辑:我更新了示例,使其更加“不规则”,因此
upsample
不能用作解决方案,并明确我们需要更通用的东西
看来 pandas 在进行插值之前首先进行上采样。因此,我们可以在 Polars 中执行相同的操作,方法是上采样,然后插值,然后将其自身连接回来,这样我们只保留您最初拥有的日期时间:
(df1
.sort('ts')
.with_columns(pl.col('value').cast(pl.Float64))
.upsample(time_column='ts', every='1d')
.interpolate()
.join(
df1.select('ts'), on='ts'
)
)
您还需要注意列数据类型,它应该是浮点型,否则您会得到整数插值。
ts(日期时间[μs]) | 值(f64) |
---|---|
2020-01-01 00:00:00 | 1.0 |
2020-01-03 00:00:00 | 2.333333 |
2020-01-04 00:00:00 | 3.0 |
这是一个使用
scipy
的解决方案。对于这些值,转换为 numpy
应该是零拷贝,所以我认为它应该是高效的
from scipy import interpolate
df1 = pl.DataFrame(
{
"ts": [
datetime(2020, 1, 1),
datetime(2020, 1, 3, 0, 0, 12),
datetime(2020, 1, 3, 0, 1, 35),
datetime(2020, 1, 4),
],
"value": [1, None, None, 3],
}
)
x = (
df1.filter(pl.col("value").is_not_null())["ts"]
.dt.timestamp()
.to_numpy(zero_copy_only=True)
)
y = df1.filter(pl.col("value").is_not_null())[
"value"
].to_numpy(zero_copy_only=True)
xnew = (
df1["ts"].dt.timestamp().to_numpy(zero_copy_only=True)
)
ynew = interpolate.interp1d(x, y)(xnew)
df1 = df1.with_columns(pl.Series(ynew).alias("value"))
结果是
In [6]: df1
Out[6]:
shape: (4, 2)
┌─────────────────────┬──────────┐
│ ts ┆ value │
│ --- ┆ --- │
│ datetime[μs] ┆ f64 │
╞═════════════════════╪══════════╡
│ 2020-01-01 00:00:00 ┆ 1.0 │
│ 2020-01-03 00:00:12 ┆ 2.333426 │
│ 2020-01-03 00:01:35 ┆ 2.334066 │
│ 2020-01-04 00:00:00 ┆ 3.0 │
└─────────────────────┴──────────┘
Expr.interpolate_by
已添加到 Polars 0.20.28
df1.with_columns(pl.col("value").interpolate_by("ts"))
shape: (4, 2)
┌─────────────────────┬──────────┐
│ ts ┆ value │
│ --- ┆ --- │
│ datetime[μs] ┆ f64 │
╞═════════════════════╪══════════╡
│ 2020-01-01 00:00:00 ┆ 1.0 │
│ 2020-01-03 00:00:12 ┆ 2.333426 │
│ 2020-01-03 00:01:35 ┆ 2.334066 │
│ 2020-01-04 00:00:00 ┆ 3.0 │
└─────────────────────┴──────────┘
不确定这有多有用,但看起来 pandas 调用
np.interp()
来做到这一点:
invalid = pl.when(pl.col('value').is_null()).then(pl.col('ts')).alias('invalid')
valid = pl.when(pl.col('value').is_not_null()).then(pl.col('ts')).alias('valid')
values = pl.when(pl.col('value').is_not_null()).then(pl.col('value')).alias('values')
df.select(
pl.struct(invalid, valid, values)
.map(lambda args:
np.interp(
args.struct['invalid'].drop_nulls().dt.timestamp().to_numpy(zero_copy_only=True),
args.struct['valid'].drop_nulls().dt.timestamp().to_numpy(zero_copy_only=True),
args.struct['values'].drop_nulls().to_numpy(zero_copy_only=True)
)
)
.flatten()
)
shape: (2, 1)
┌──────────┐
│ invalid │
│ --- │
│ f64 │
╞══════════╡
│ 2.333426 │
│ 2.334066 │
└──────────┘
尽管似乎确实还有很多其他事情正在发生。