讲师中心 微信公众号
AI工具推荐 视频效率加速

Polars groupby 上下文中的数据插值与上采样技术

云宇小哥_9996

云宇小哥_9996

发布时间:2025-09-18 15:08:00

|

794人浏览过

|

来源于php中文网

原创

Polars groupby 上下文中的数据插值与上采样技术

本文详细介绍了如何在 Polars 中高效地对分组数据执行插值操作。针对包含分类列和数值列的数据集,当需要在每个分组内填充缺失的、按固定步长递增的数值时,Polars 提供了强大的解决方案。核心方法是首先为每个分组生成一个包含所有预期数值范围的辅助 DataFrame,然后通过左连接将其与原始数据合并,最后利用 Polars 内置的插值功能填充由此产生的缺失值,实现精确的分组内数据补全。

1. 问题背景与挑战

在数据分析中,我们经常会遇到时间序列或有序数据中存在缺失值的情况,需要通过插值来填充这些空缺。当数据还包含一个或多个分类维度时,插值通常需要在每个分类组内独立进行。例如,一个数据集包含 x(有序数值,可能缺失)、y(待插值数值)和 z(分类标签)三列。我们的目标是针对 z 的每个类别,确保 x 列按照固定的步长(例如5)连续递增,并根据已有的 y 值对缺失的 y 值进行插值。

一个典型的场景是,原始数据可能只记录了特定 x 值的 y 值:

┌─────┬─────┬─────┐
│ x   ┆ y   ┆ z   │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str │
╞═════╪═════╪═════╡
│ 5   ┆ 1   ┆ A   │
│ 10  ┆ 2   ┆ A   │
│ 20  ┆ 4   ┆ A   │
│ 25  ┆ 5   ┆ A   │
│ 10  ┆ 2   ┆ B   │
│ 20  ┆ 4   ┆ B   │
│ 30  ┆ 6   ┆ B   │
└─────┴─────┴─────┘

我们期望的输出是,在每个 z 分组内,x 值以5为步长填充所有缺失的中间值,并且对应的 y 值也被插值:

┌─────┬─────┬─────┐
│ x   ┆ y   ┆ z   │
│ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ str │
╞═════╪═════╪═════╡
│ 5   ┆ 1   ┆ A   │
│ 10  ┆ 2   ┆ A   │
│ 15  ┆ 3   ┆ A   │
│ 20  ┆ 4   ┆ A   │
│ 25  ┆ 5   ┆ A   │
│ 10  ┆ 2   ┆ B   │
│ 15  ┆ 3   ┆ B   │
│ 20  ┆ 4   ┆ B   │
│ 25  ┆ 5   ┆ B   │
│ 30  ┆ 6   ┆ B   │
└─────┴─────┴─────┘

对于不含分类列 z 的简单情况,可以通过生成完整的 x 值范围,然后与原始数据进行左连接,最后应用 interpolate() 方法来解决。然而,如何在 group_by 上下文中实现这一复杂逻辑,是 Polars 用户常遇到的挑战。

2. 单组数据插值回顾

在深入探讨分组插值之前,我们先回顾一下对单个组(即没有分类列 z)进行插值的方法。这种方法是理解分组插值的基础。

假设我们只有 x 和 y 两列,并且希望 x 值以5为步长进行填充,并插值 y:

import polars as pl

# 原始数据
data = dict(x=[10, 20, 30], y=[2, 4, 6])
df = pl.DataFrame(data)

# 生成所有 x 值的完整范围
step = 5
df_full_x = pl.DataFrame(dict(x=range(df["x"].min(), df["x"].max() + step, step)))

# 合并并插值
result_single_group = (
    df_full_x
    .join(df, on="x", how="left")
    .with_columns(pl.col("y").interpolate())
)

print("单组插值结果:")
print(result_single_group)

输出结果如下:

┌─────┬─────┐
│ x   ┆ y   │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═════╪═════╡
│ 10  ┆ 2   │
│ 15  ┆ 3   │
│ 20  ┆ 4   │
│ 25  ┆ 5   │
│ 30  ┆ 6   │
└─────┴─────┘

这种方法的核心是创建一个包含所有期望 x 值的“模板”DataFrame,然后通过左连接将原始数据合并到这个模板上。连接后,那些在原始数据中缺失的 x 值对应的 y 列将变为 null,这时就可以使用 pl.col("y").interpolate() 来填充这些 null 值。

3. Polars 中分组内数据插值的实现

要将上述单组插值逻辑扩展到 group_by 上下文,关键在于如何为每个分组独立地生成其完整的 x 值范围。Polars 的 group_by 和 agg 操作提供了实现这一目标的高效途径。

3.1 准备示例数据

首先,我们定义原始的 Polars DataFrame:

import polars as pl

# 示例数据
data = {
    "x": [5, 10, 20, 25, 10, 20, 30],
    "y": [1, 2, 4, 5, 2, 4, 6],
    "z": ["A", "A", "A", "A", "B", "B", "B"]
}
df = pl.DataFrame(data)

print("原始 DataFrame:")
print(df)

3.2 步骤一:为每个分组生成完整的 x 值范围

我们需要为每个 z 分组,根据其 x 列的最小值和最大值,生成一个以指定步长(例如5)递增的完整 x 值序列。

# 步骤一:为每个分组生成完整的 x 值范围
upsampled_template = (
    df
    .group_by("z")
    .agg(
        # 为每个组生成从 min(x) 到 max(x)+step 的整数范围
        # 并将其命名为 "x"
        pl.int_range(pl.col("x").min(), pl.col("x").max() + 5, step=5).alias("x")
    )
    .explode("x") # 将列表形式的 x 展开为多行
)

print("\n生成的上采样模板 DataFrame:")
print(upsampled_template)

解释:

  1. df.group_by("z"): 按照分类列 z 进行分组。
  2. agg(...): 对每个分组执行聚合操作。
  3. pl.int_range(pl.col("x").min(), pl.col("x").max() + 5, step=5).alias("x"): 这是核心。对于每个分组,它计算 x 列的最小值和最大值,然后生成一个从最小值到最大值(包含最大值,因此 max() + 5)的整数序列,步长为5。alias("x") 确保新列名为 x。
  4. .explode("x"): agg 操作的结果会是每个 z 组对应一个包含 x 值列表的行。explode("x") 将这些列表展开,使得每个 x 值都成为独立的一行,同时保留其所属的 z 分组信息。

经过此步骤,我们得到了一个包含所有 z 分组和其各自完整 x 值序列的 DataFrame,这将作为我们后续连接的左侧 DataFrame。

3.3 步骤二:执行左连接并插值

现在,我们将这个 upsampled_template DataFrame 与原始 DataFrame df 进行左连接。连接键是 x 和 z。连接后,那些在 upsampled_template 中存在但在原始 df 中不存在的 (x, z) 组合,其 y 列将变为 null。最后,我们对 y 列应用 interpolate() 方法来填充这些 null 值。

# 步骤二:执行左连接并插值
final_result = (
    upsampled_template
    .join(
        df,
        on=["x", "z"], # 基于 x 和 z 两列进行连接
        how="left"     # 左连接,保留 upsampled_template 中的所有行
    )
    .with_columns(
        pl.col("y").interpolate() # 对 y 列进行插值
    )
    # 可选:如果需要特定排序,例如按 z, x 排序
    .sort(["z", "x"])
)

print("\n最终分组内插值结果:")
print(final_result)

输出:

最终分组内插值结果:
shape: (10, 3)
┌─────┬─────┬─────┐
│ z   ┆ x   ┆ y   │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ f64 │
╞═════╪═════╪═════╡
│ A   ┆ 5   ┆ 1.0 │
│ A   ┆ 10  ┆ 2.0 │
│ A   ┆ 15  ┆ 3.0 │
│ A   ┆ 20  ┆ 4.0 │
│ A   ┆ 25  ┆ 5.0 │
│ B   ┆ 10  ┆ 2.0 │
│ B   ┆ 15  ┆ 3.0 │
│ B   ┆ 20  ┆ 4.0 │
│ B   ┆ 25  ┆ 5.0 │
│ B   ┆ 30  ┆ 6.0 │
└─────┴─────┴─────┘

可以看到,最终结果完美地实现了在每个 z 分组内对 x 值进行上采样,并对 y 值进行插值。

4. 注意事项与最佳实践

  • LazyFrame 的使用: 原始问题中提到数据集可能非常大,希望使用 pl.LazyFrame。上述解决方案完全兼容 LazyFrame。只需在开始时将 pl.DataFrame 替换为 pl.LazyFrame,并在最后调用 .collect() 即可。例如:

    # 使用 LazyFrame
    df_lazy = pl.LazyFrame(data)
    
    upsampled_template_lazy = (
        df_lazy
        .group_by("z")
        .agg(
            pl.int_range(pl.col("x").min(), pl.col("x").max() + 5, step=5).alias("x")
        )
        .explode("x")
    )
    
    final_result_lazy = (
        upsampled_template_lazy
        .join(
            df_lazy,
            on=["x", "z"],
            how="left"
        )
        .with_columns(
            pl.col("y").interpolate()
        )
        .sort(["z", "x"])
        .collect() # 在 LazyFrame 链的末尾调用 collect() 获取结果
    )
    
    print("\nLazyFrame 分组内插值结果:")
    print(final_result_lazy)

    使用 LazyFrame 可以在处理大规模数据时获得更好的性能,因为它允许 Polars 优化查询计划并延迟计算。

  • 排序的重要性: 在执行插值之前,确保数据在每个分组内是按照 x 列排序的至关重要。join 操作通常不会改变数据的内部顺序,但为了确保 interpolate() 方法的正确性,以及最终输出的可读性,建议在最终结果上使用 .sort(["z", "x"]) 来明确排序。Polars 的 interpolate 方法默认假定数据是排序的。

  • 插值方法: pl.col("y").interpolate() 默认执行线性插值。如果需要更复杂的插值方法(例如,前后填充 fill_null(strategy="forward") 或 fill_null(strategy="backward")),可以根据具体需求调整。但对于这种数值序列的填充,线性插值通常是合适的。

  • 性能考量: Polars 的 group_by 和 join 操作都是高度优化的。通过 explode 生成完整的 x 范围,然后进行连接,这种模式在 Polars 中是处理此类问题的惯用且高效的方式。

5. 总结

本文提供了一种在 Polars 中对分组数据进行插值和上采样的通用且高效的方法。通过结合 group_by、agg、explode 和 join 操作,我们能够为每个分组独立地生成完整的数值序列,并通过左连接引入缺失值,最终利用 interpolate() 方法精确填充这些缺失值。这种方法不仅适用于小规模数据集,通过结合 LazyFrame 也能轻松扩展到大规模数据场景,充分体现了 Polars 在数据处理方面的灵活性和高性能。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1678

2024.03.01

sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

1138

2023.09.04

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

0

2026.10.16

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

0

2026.10.16

OpenJDK怎么部署Java应用
OpenJDK怎么部署Java应用

Java项目开发完成后,怎样部署到目标环境并正常运行?本专题介绍OpenJDK应用部署流程,涵盖运行环境准备、版本兼容检查、应用打包、启动参数配置和日志查看,帮助你理清部署前后的操作顺序,减少环境差异造成的运行问题。

0

2026.10.16

Revel框架模板渲染与表单验证
Revel框架模板渲染与表单验证

本专题围绕Revel框架视图层开发,介绍模板语法、变量输出、布局复用、表单提交、参数绑定与验证规则,帮助开发者完成安全规范的页面交互。

0

2026.10.16

Revel框架MVC路由与控制器
Revel框架MVC路由与控制器

本专题聚焦Revel框架MVC核心机制,讲解路由定义、控制器Action、参数绑定、请求响应和反向路由,并结合常见场景梳理接口请求处理流程。

0

2026.10.16

Revel框架入门与安装配置
Revel框架入门与安装配置

本专题从Revel框架环境搭建讲起,系统介绍命令行工具安装、项目创建、目录结构、配置文件及热代码重载,帮助开发者快速完成首个Go Web应用。

0

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn