当前位置:

首页 > 编程开发 > LazyFrame多列乘积计算方法

LazyFrame多列乘积计算方法

本文详细介绍了如何在PolarsLazyFrame中,对两个具有相同结构和时间索引的数据帧进行列式乘法运算,同时排除作为索引的时间列。与Pandas的隐式操作不同,Polars通过利用pl.struct将非索引列打包、执行基于时间列的join操作,然后对结构体进行元素级乘法,最后使用unnest展开结果,从而实现高效且明确的多列乘积计算,尤其适用于大规模数据集的惰性计算场景。

本文目录

    在Polars LazyFrame中执行除索引列外的多列乘积运算

    本文详细介绍了如何在Polars LazyFrame中,对两个具有相同结构和时间索引的数据帧进行列式乘法运算,同时排除作为索引的时间列。与Pandas的隐式操作不同,Polars通过利用`pl.struct`将非索引列打包、执行基于时间列的`join`操作,然后对结构体进行元素级乘法,最后使用`unnest`展开结果,从而实现高效且明确的多列乘积计算,尤其适用于大规模数据集的惰性计算场景。

    Polars LazyFrame 多列乘积运算:排除特定列的结构化方法

    在数据处理和分析中,经常需要对两个结构相似的数据集执行逐列操作,例如乘法。对于熟悉Pandas的用户来说,直接使用df1 * df2即可实现基于索引的列式乘积。然而,当迁移到Polars,特别是使用其强大的LazyFrame功能时,实现类似操作需要更具Polars特色的方法。本文将详细阐述如何在Polars LazyFrame中,高效地对两个DataFrame(排除作为索引的列)执行列式乘积运算。

    1. 问题背景与Pandas对比

    在Pandas中,如果两个DataFrame具有相同的索引和列名,可以直接通过乘法运算符进行逐元素相乘:

    import pandas as pd
    import numpy as np
    import polars as pl
    
    n = 5 # 示例数据行数
    
    # 创建示例Pandas DataFrame
    df_pd1 = pd.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size=n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size=n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size=n).astype(np.float64)
    }).set_index('time')
    
    df_pd2 = pd.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size=n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size=n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size=n).astype(np.float64)
    }).set_index('time')
    
    # Pandas中的直接乘法
    result_pd = df_pd1 * df_pd2
    print("Pandas 乘法结果:")
    print(result_pd)

    输出结果会是两个DataFrame对应列的乘积,且索引保持不变。

    在Polars中,直接对两个LazyFrame执行类似操作(如df1 * df2)并不直接支持,因为Polars的运算符重载和表达式系统设计有所不同。尝试通过concat后group_by并聚合乘积,例如pl.concat([df1, df2]).group_by('time').agg(pl.col("*").mul(pl.col("*"))),通常会导致每个分组返回一个列表,而不是期望的单个乘积值,因为mul操作在聚合上下文中对列表中的每个元素执行,而非聚合整个组的乘积。

    2. Polars LazyFrame 的解决方案:结构体与连接

    Polars的强大之处在于其表达式系统和对结构化数据的灵活处理。解决此问题的核心思路是:将需要进行乘法运算的列(即除索引列外的所有列)打包成一个结构体(struct),然后通过索引列进行连接(join),最后对连接后的结构体执行乘法,并将结果结构体展开。

    首先,创建两个Polars LazyFrame:

    # 创建示例Polars LazyFrame
    df_pl1 = pl.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size= n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size= n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size= n).astype(np.float64)
    }).lazy()
    
    df_pl2 = pl.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size= n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size= n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size= n).astype(np.float64)
    }).lazy()
    
    print("\n原始 Polars LazyFrame 1:")
    print(df_pl1.collect())
    print("\n原始 Polars LazyFrame 2:")
    print(df_pl2.collect())

    接下来,我们分步实现列式乘法:

    步骤 1: 将非索引列打包成结构体

    使用pl.struct表达式将除“time”列之外的所有列打包成一个名为“cols”的结构体。这使得我们可以将所有相关列作为一个整体进行处理。

    # 将df1的非时间列打包成结构体
    df_pl1_struct = df_pl1.select("time", cols=pl.struct(pl.exclude("time")))
    print("\nLazyFrame 1 结构体化:")
    print(df_pl1_struct.collect())
    
    # 将df2的非时间列打包成结构体
    df_pl2_struct = df_pl2.select("time", cols=pl.struct(pl.exclude("time")))
    print("\nLazyFrame 2 结构体化:")
    print(df_pl2_struct.collect())

    pl.exclude("time")是一个非常实用的表达式,它会自动选择除“time”列之外的所有列。结果会是一个包含time列和一个名为cols的结构体列的LazyFrame。

    步骤 2: 基于索引列进行连接

    通过time列对两个结构体化的LazyFrame进行左连接(left join)。这将确保两个DataFrame中对应时间戳的结构体行被对齐。连接后,我们将得到一个包含time、cols(来自df_pl1)和cols_right(来自df_pl2)的LazyFrame。

    # 连接两个结构体化的LazyFrame
    joined_df = (
        df_pl1_struct
        .join(
           df_pl2_struct,
           on = "time",
           how = "left"
        )
    )
    print("\n连接后的 LazyFrame:")
    print(joined_df.collect())

    步骤 3: 对结构体执行元素级乘法

    Polars允许直接对结构体列执行元素级运算,前提是结构体中的字段兼容。因此,我们可以直接将cols结构体乘以cols_right结构体。结果将是一个新的结构体,其中包含每个原始字段的乘积。

    # 对连接后的结构体执行乘法
    multiplied_struct_df = (
        joined_df
        .select("time", pl.col("cols") * pl.col("cols_right"))
    )
    print("\n结构体乘法后的 LazyFrame:")
    print(multiplied_struct_df.collect())

    步骤 4: 展开结果结构体

    最后一步是使用unnest()方法将乘法结果结构体(这里仍然名为cols,因为它是pl.col("cols") * pl.col("cols_right")的结果,并被赋予了默认名cols)展开回独立的列。这将恢复原始的DataFrame结构,但其中的值已经是乘积结果。

    # 展开结构体以获取最终结果
    final_result_df = (
        multiplied_struct_df
        .unnest("cols")
    )
    print("\n最终结果 LazyFrame:")
    print(final_result_df.collect())

    3. 完整代码示例

    将上述步骤整合到一起,形成完整的Polars解决方案:

    import pandas as pd
    import numpy as np
    import polars as pl
    
    n = 5 # 示例数据行数
    
    # 创建示例Polars LazyFrame
    df_pl1 = pl.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size= n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size= n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size= n).astype(np.float64)
    }).lazy()
    
    df_pl2 = pl.DataFrame(data={
        'time': pd.date_range('2023-01-01', periods=n, freq='1 min'),
        'foo': np.random.uniform(0,127, size= n).astype(np.float64),
        'bar': np.random.uniform(1e3,32767, size= n).astype(np.float64),
        'baz': np.random.uniform(1e6,2147483, size= n).astype(np.float64)
    }).lazy()
    
    # Polars 解决方案
    result_pl = (
        df_pl1.select("time", cols=pl.struct(pl.exclude("time"))) # 将df1的非时间列打包
        .join(
           df_pl2.select("time", cols=pl.struct(pl.exclude("time"))), # 将df2的非时间列打包并连接
           on = "time",
           how = "left"
        )
        .select("time", pl.col("cols") * pl.col("cols_right")) # 对结构体执行乘法
        .unnest("cols") # 展开结果结构体
        .collect() # 收集结果
    )
    
    print("\nPolars LazyFrame 乘法结果:")
    print(result_pl)

    4. 注意事项与总结

    • 惰性计算的优势: 这种方法充分利用了Polars的惰性计算特性。在.collect()被调用之前,所有的操作都只是构建查询计划,这对于处理大规模数据集非常高效。
    • 明确性与控制: 相比Pandas的隐式行为,Polars要求更明确地定义操作。通过pl.struct和join,我们清晰地表达了“对齐这些数据,然后对特定部分执行操作”的意图。
    • 灵活性: 这种结构体-连接-展开的模式不仅适用于乘法,也适用于其他需要对多列进行并行、元素级操作的场景。
    • 列名冲突: 在join操作中,如果两个DataFrame有除连接键之外的同名列,Polars会自动为右侧DataFrame的列添加后缀(如_right)。本例中,由于我们将非索引列打包成结构体,并分别命名为cols和cols_right,避免了直接的列名冲突。
    • 性能: 对于大型数据集,这种基于表达式和结构体的方法通常比迭代或使用apply函数更具性能优势,因为它允许Polars优化底层的计算。

    通过理解和运用pl.struct、join和unnest这些核心概念,Polars用户可以高效且灵活地处理复杂的跨DataFrame操作,充分发挥其在数据处理方面的强大能力。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    OpenCode PTY 0.5.2:发布 protocol 8 终端服务组件
    OpenCode PTY 0.5.2:发布 protocol 8 终端服务组件

    OpenCode发布opencode-pty 0.5.2终端服务组件,使用protocol 8并以@opencode/pty@0.5.2发布到npm。它面向集成终端子进程的开发者,采用MIT许可,仍被项目说明为实验性服务核心,不是OpenCode整款应用的版本号。

    vLLM 0.34.0rc2 源码标签:调整可扩展 KV Cache 测试内存参数
    vLLM 0.34.0rc2 源码标签:调整可扩展 KV Cache 测试内存参数

    vLLM出现0.34.0rc2候选源码标签,关联提交移除多处可扩展KV Cache测试和基准中的固定gpu_memory_utilization设置,并调整相关文档。当前核验的是标签与代码提交,不能将其当作正式版安装包或全局显存默认值变更。

    llama.cpp b12318:新增 CUDA 调试缓冲池与 Sanitizer 说明
    llama.cpp b12318:新增 CUDA 调试缓冲池与 Sanitizer 说明

    llama.cpp b12318新增CUDA调试缓冲池,并补充与compute-sanitizer相关的代码说明。它是面向CUDA开发调试路径的预发布构建,不能据此推断普通推理速度、显存占用或所有后端发生同等变化。

    llama.cpp b12307:修复 Metal SOLVE_TRI 内核越界读取
    llama.cpp b12307:修复 Metal SOLVE_TRI 内核越界读取

    llama.cpp b12307修复Metal后端SOLVE_TRI内核的越界读取,属于预发布构建中的特定计算路径补丁。使用Metal后端的开发者应核对是否涉及该算子,并验证模型加载与推理;公告未给出CVE、确认利用或统一性能结论。

    n8n 2.48.2 Beta:修复新草稿中的 Agent 标签页可用性
    n8n 2.48.2 Beta:修复新草稿中的 Agent 标签页可用性

    n8n 2.48.2是预发布版本,修复编辑器中新草稿的Agent标签页可用性,并包含只读凭据节点API约束及边框修复。本条新增关注Beta编辑器路径,稳定版用户无需仅因版本号较高就切换渠道。

    Tug 0.9.2:新增上手帮助,改进会话创建与主题显示
    Tug 0.9.2:新增上手帮助,改进会话创建与主题显示

    Tug 0.9.2新增Getting Started帮助卡,可在会话选择器中创建不存在的目录,并改进拖放输入、会话状态与主题颜色。它是MIT开源的macOS AI编程IDE,仍属beta,需要Apple silicon Mac及macOS 15或更新版本,并依赖包含Claude Code的付费计划。

    Ducter 0.0.38 源码标签:修复 Slack 主动提醒与启动后邮件回看
    Ducter 0.0.38 源码标签:修复 Slack 主动提醒与启动后邮件回看

    Ducter 0.0.38源码标签修复Slack提及和私信进入watcher、启动后账号未就绪导致邮件回看空跑等问题。本轮确认的是源码标签及更新日志,核验时官网安装入口仍指向0.0.37,不能当作0.0.38安装包已上线。

    LangChain TypeSafe 0.1.0:分类器接口迁移,Python 与 JS 依赖要求调整
    LangChain TypeSafe 0.1.0:分类器接口迁移,Python 与 JS 依赖要求调整

    LangChain TypeSafe 0.1.0将TypeSafeClassifier对齐决策模型接口。JavaScript的问题改为随请求传入,响应和usage字段调整,旧中间件入口弃用;Python版最低Python 3.11、langchain-core 1.8.0,跨语言依赖要求需要分别核对。

    GitHub Copilot CLI 1.0.110-3:改进 Entra 登录,修复 MCP 配置保存
    GitHub Copilot CLI 1.0.110-3:改进 Entra 登录,修复 MCP 配置保存

    GitHub Copilot CLI 1.0.110-3是预发布补丁,改善Microsoft Entra登录与终端显示,并在MCP配置文件无效时保留原配置、显示保存错误。技能描述中的不可见转义内容不再参与命令匹配,升级需验证相应终端和集成路径。

    LiteLLM 修复分支扩展:1.106.9、1.108.6 与 1.110.0-rc.2 更新
    LiteLLM 修复分支扩展:1.106.9、1.108.6 与 1.110.0-rc.2 更新

    LiteLLM将请求字段与部署配置冲突的修复覆盖到1.106.9、1.108.6及1.110.0-rc.2。维护旧稳定分支或候选分支的团队应选择对应修复版本;1.108.6还包含预算重置、用量细分和其他回移,不能将RC视为稳定版。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。