
本教程详细介绍了如何在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中,直接像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提供了一种更符合其表达系统和惰性计算范式的方法来解决这个问题,即利用结构体(struct)类型和join操作。
首先,我们需要将每个LazyFrame中除“time”列之外的所有列打包成一个结构体(struct)。这使得我们可以将所有要进行乘法运算的列视为一个整体。
# 将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这三列的数据。
接下来,我们使用“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)通过本教程,我们学习了如何在Polars LazyFrames中优雅地实现对除索引列外的所有列进行元素级乘法操作。这种方法利用了Polars强大的表达式系统,特别是pl.struct、join和unnest,提供了一种高效且符合Polars设计哲学的解决方案。掌握这种模式对于从Pandas迁移到Polars,并充分利用Polars的性能优势至关重要。
以上就是Polars LazyFrames中高效实现除索引列外的多列乘法操作的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号