0

0

Polars LazyFrames中高效实现除索引列外的多列乘法操作

DDD

DDD

发布时间:2025-11-19 12:35:42

|

274人浏览过

|

来源于php中文网

原创

Polars LazyFrames中高效实现除索引列外的多列乘法操作

本教程详细介绍了如何在polars lazyframes中对两个数据帧进行除指定索引列(如时间列)外的所有数值列执行元素级乘法操作。通过利用polars的结构体(`struct`)表达式、高效的连接(`join`)机制以及解嵌套(`unnest`)功能,我们能够优雅地解决在pandas中常见的跨dataframe列操作问题,同时保持polars的惰性计算优势,实现高性能的数据处理。

引言

在数据处理领域,Pandas因其直观的DataFrame操作而广受欢迎。例如,对两个具有相同索引和列名的Pandas DataFrame执行元素级乘法,可以直接使用df1 * df2这样的简洁语法。然而,随着数据规模的增长,高性能数据处理框架如Polars逐渐成为主流。Polars以其内存效率和惰性计算能力著称,但在某些特定操作上,其语法模式与Pandas有所不同。本教程将专注于解决一个常见问题:如何在Polars LazyFrames中,对除一个指定列(如时间戳)之外的所有列执行元素级乘法。

数据准备

首先,我们创建两个Polars LazyFrame,它们模拟了Pandas中的两个DataFrame,包含一个时间列作为“索引”和多个数值列。

import polars as pl
import pandas as pd
import numpy as np

# 设置数据行数
n = 5

# 创建第一个Polars LazyFrame
df1 = 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 LazyFrame
df2 = 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("df1 (LazyFrame):")
print(df1.collect())
print("\ndf2 (LazyFrame):")
print(df2.collect())

Polars中的挑战与常见误区

在Polars中,直接像Pandas那样对两个LazyFrame进行乘法操作并不直接支持。一种常见的尝试是使用pl.concat将两个LazyFrame堆叠,然后按时间列进行group_by,再尝试聚合乘法。

例如,如果尝试对所有列进行聚合求和,这会按预期工作:

# 聚合求和示例
sum_result = pl.concat([df1, df2]).group_by('time').sum().sort('time').collect()
print("\n聚合求和结果:")
print(sum_result)

然而,如果尝试将sum替换为某种乘法聚合,例如pl.col("*").mul(pl.col("*")),结果通常不是我们期望的元素级乘法。这是因为group_by后的聚合操作通常是对每个组内的值进行处理,而不是跨原始的两个DataFrame进行元素级对应。尝试使用agg(pl.col("*").mul(pl.col("*")))会得到一个包含列表的列,其中每个列表包含了组内元素的自乘结果,而非两个原始DataFrame对应列的乘积。

# 错误的乘法聚合尝试
incorrect_product_attempt = pl.concat([df1, df2]).group_by('time').agg(pl.col("*").mul(pl.col("*"))).sort('time').collect()
print("\n错误的乘法聚合尝试结果:")
print(incorrect_product_attempt)

上述输出显示,每个单元格都是一个包含两个浮点数的列表,这表明pl.col("*").mul(pl.col("*"))是在每个组内对数据进行操作,而不是实现df1['foo'] * df2['foo']这样的跨DataFrame乘法。

Polars的解决方案:基于结构体和连接

Polars提供了一种更符合其表达系统和惰性计算范式的方法来解决这个问题,即利用结构体(struct)类型和join操作。

步骤一:封装非索引列为结构体

首先,我们需要将每个LazyFrame中除“time”列之外的所有列打包成一个结构体(struct)。这使得我们可以将所有要进行乘法运算的列视为一个整体。

Tweeze
Tweeze

Tweeze.app是一个AI驱动的个性化新闻简报服务,定位为个人互联网AI阅读助手

下载
# 将df1的非时间列封装为结构体
df1_struct = df1.select("time", cols=pl.struct(pl.exclude("time")))
print("\ndf1封装为结构体后的LazyFrame:")
print(df1_struct.collect())

# 将df2的非时间列封装为结构体
df2_struct = df2.select("time", cols=pl.struct(pl.exclude("time")))
print("\ndf2封装为结构体后的LazyFrame:")
print(df2_struct.collect())

通过pl.struct(pl.exclude("time")),我们创建了一个名为cols的结构体列,它包含了foo、bar和baz这三列的数据。

步骤二:通过时间列连接两个LazyFrame

接下来,我们使用“time”列作为键,对这两个包含结构体的LazyFrame进行左连接(left join)。这将确保两个LazyFrame中相同时间戳的数据行能够对齐。

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

连接操作会产生两列结构体:cols(来自df1_struct)和cols_right(来自df2_struct)。

步骤三:执行结构体乘法并解嵌套

现在,我们可以直接对这两列结构体执行乘法操作。Polars会自动识别并对结构体内部的对应字段进行元素级乘法。然后,使用unnest("cols")将结果结构体展开,恢复到原始的列结构。

# 执行结构体乘法并解嵌套
final_result = (
    df1_struct
    .join(
       df2_struct,
       on = "time",
       how = "left"
    )
    .select("time", pl.col("cols") * pl.col("cols_right")) # 对结构体列进行乘法
    .unnest("cols") # 解嵌套结果结构体
    .sort("time") # 排序以保持一致性
)
print("\n最终乘法结果:")
print(final_result.collect())

最终结果是一个新的LazyFrame,其中包含time列和经过元素级乘法运算后的foo、bar、baz列。

完整代码示例

将上述步骤整合,以下是完整的解决方案代码:

import polars as pl
import pandas as pd
import numpy as np

# 设置数据行数
n = 5

# 创建第一个Polars LazyFrame
df1 = 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 LazyFrame
df2 = 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()

# 核心解决方案
product_of_columns = (
    df1.select("time", cols=pl.struct(pl.exclude("time"))) # 将df1的非时间列打包成结构体
    .join(
       df2.select("time", cols=pl.struct(pl.exclude("time"))), # 将df2的非时间列打包成结构体
       on = "time", # 以时间列为键进行连接
       how = "left" # 使用左连接
    )
    .select("time", pl.col("cols") * pl.col("cols_right")) # 对两个结构体列执行元素级乘法
    .unnest("cols") # 解嵌套结果结构体,恢复原始列结构
    .sort("time") # 对结果按时间列排序
    .collect() # 触发惰性计算并获取结果
)

print("\n最终所有非时间列的元素级乘法结果:")
print(product_of_columns)

注意事项与最佳实践

  1. 惰性计算优势: Polars的LazyFrame在调用.collect()之前不会执行任何计算,这使得它在处理大型数据集时能够进行查询优化,并有效管理内存。
  2. pl.struct的灵活性: pl.struct表达式是Polars中处理多列操作的强大工具。它允许我们将一组相关的列视为一个单一的复合类型,从而简化复杂的数据转换逻辑。
  3. pl.exclude的便捷性: pl.exclude("column_name")表达式非常方便,它允许我们轻松地选择除指定列之外的所有列,避免手动列举大量列名。
  4. join操作的重要性: 在Polars中,当需要对齐来自不同DataFrame的数据时,join操作是核心。确保on参数指定了正确的键列,并且how参数符合您的业务逻辑(例如,left、inner、outer等)。
  5. 列名一致性: 为确保结构体乘法和解嵌套的正确性,两个LazyFrame中被排除的非索引列的名称和顺序应保持一致。Polars的结构体乘法会基于字段名进行匹配。

总结

通过本教程,我们学习了如何在Polars LazyFrames中优雅地实现对除索引列外的所有列进行元素级乘法操作。这种方法利用了Polars强大的表达式系统,特别是pl.struct、join和unnest,提供了一种高效且符合Polars设计哲学的解决方案。掌握这种模式对于从Pandas迁移到Polars,并充分利用Polars的性能优势至关重要。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

82

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

34

2026.01.31

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

510

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

204

2025.07.04

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

448

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

606

2023.08.10

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

68

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

108

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

324

2026.03.11

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6.1万人学习

ASP 教程
ASP 教程

共34课时 | 5.9万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号