Polars的interpolate_by方法在列首尾存在空值时无法正常执行线性插值
Polars的interpolate_by方法在列首尾存在空值时无法正常执行线性插值
你观察到的这个情况其实是Polars interpolate_by 方法的设计预期行为,不是bug哦~
先给你拆解一下原因:interpolate_by 实现的是线性内插(Linear Interpolation),核心逻辑是在两个已知数据点之间,根据关联列(这里是a)的数值,计算缺失值的线性估计值。而你第二个例子里的空值在列的末尾,属于「没有后续已知数据点」的情况,这种场景已经超出了「内插」的范畴,属于外推(Extrapolation)——Polars的这个方法默认不处理外推需求,所以会保留null。
对比你的两个例子:
- 第一个例子的空值在中间(
a=3),前后都有已知的b值(5和7,对应a=2和a=4),所以能通过两点斜率计算出中间的6.0,这是标准的内插逻辑; - 第二个例子的空值在最后(
a=5),只有前面的已知点,没有后续参考点,内插无法进行,自然就返回null了。
那如果需要处理首尾的缺失值(也就是外推),可以试试这两个方案:
方案1:手动计算外推值(基于关联列a)
如果你的a列是有规律的数值(比如连续递增/递减),可以手动计算已知点的斜率,然后填充末尾的空值。针对你的第二个例子,代码示例如下:
import polars as pl df = pl.DataFrame({ 'a': [1, 2, 3, 4, 5], 'b': [4, 5, 6, 7, None] }) df = df.with_columns( # 先执行内插(这里空值在末尾,内插后还是null) interpolate = pl.col('b').interpolate_by('a') ).with_columns( # 自定义外推逻辑:基于最后两个已知点的斜率推导空值 interpolate = pl.col('interpolate').fill_null( # 取最后一个非空的b值 pl.col('b').drop_nulls().last() + # 计算当前a与最后一个已知a的差值 (pl.col('a') - pl.col('a').filter(pl.col('b').is_not_null()).last()) * # 计算最后两个已知点的斜率 ( (pl.col('b').drop_nulls().last() - pl.col('b').drop_nulls().nth(-2)) / (pl.col('a').filter(pl.col('b').is_not_null()).last() - pl.col('a').filter(pl.col('b').is_not_null()).nth(-2)) ) ) ) print(df)
执行后输出会是:
┌─────┬──────┬─────────────┐ │ a ┆ b ┆ interpolate │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ f64 │ ╞═════╪══════╪═════════════╡ │ 1 ┆ 4 ┆ 4.0 │ │ 2 ┆ 5 ┆ 5.0 │ │ 3 ┆ 6 ┆ 6.0 │ │ 4 ┆ 7 ┆ 7.0 │ │ 5 ┆ null ┆ 8.0 │ └─────┴──────┴─────────────┘
方案2:简单场景下用fill_null的线性策略(基于行索引)
如果你的关联列a和行索引的变化规律完全一致(比如a是连续整数,和行号一一对应),可以直接用fill_null(strategy='linear'),它会基于行索引做线性填充:
df = pl.DataFrame({ 'a': [1, 2, 3, 4, 5], 'b': [4, 5, 6, 7, None] }) df = df.with_columns( interpolate = pl.col('b').fill_null(strategy='linear') )
不过这个方法有局限性:它只基于行的位置计算,不关联a列的数值。如果a列不是连续的(比如a=[1,3,4,5,7]),这个方法就会得到错误结果,这种情况下还是建议用方案1的自定义外推逻辑。
备注:内容来源于stack exchange,提问作者nybhh
相关产品推荐
相关产品推荐

