在Polars中高效利用列值作为字典键进行数据筛选

霞舞
发布: 2025-10-13 11:43:01
原创
146人浏览过

在Polars中高效利用列值作为字典键进行数据筛选

本文探讨了在polars dataframe中,如何解决直接使用`expr`作为字典键导致`typeerror`的问题。我们提供了两种解决方案:一种是使用`map_elements`结合`pl.struct`实现直接但效率较低的列值到字典键映射;另一种是推荐的优化方案,通过将嵌套字典扁平化为polars dataframe,然后利用高效的`join`操作进行数据筛选,从而显著提升性能。

在Polars中进行数据处理时,有时我们需要根据DataFrame中某列或多列的值去查询一个外部的Python字典,并将查询结果用于筛选。然而,直接尝试将Polars的表达式(pl.col(...))作为Python字典的键会导致TypeError: unhashable type: 'Expr'错误,因为Expr对象本身是不可哈希的,无法直接作为字典键。本文将详细介绍如何优雅地解决这一问题,并提供两种不同的实现方案及其优缺点。

问题阐述

假设我们有一个Polars DataFrame df_x 和一个嵌套的Python字典 nested_dict。我们希望根据 df_x 中的 cliente 和 cluster 列的值,从 nested_dict 中获取对应的值,然后用这个值来筛选 df_x 中 score 列的数据。

错误的尝试示例:

import polars as pl

# 示例数据和字典
df_x = pl.DataFrame({
    "cliente": ["A", "A", "B", "B", "C"],
    "cluster": ["X", "Y", "X", "Y", "X"],
    "score": [10, 20, 30, 40, 50]
})

nested_dict = {
    "A": {"X": 10, "Y": 25},
    "B": {"X": 35, "Y": 40},
    "C": {"X": 50, "Y": 55}
}

# 错误的尝试,会导致 TypeError: unhashable type: 'Expr'
try:
    df_x_filtered = (
        df_x
        .filter(pl.col("score") == nested_dict[pl.col("cliente")][pl.col("cluster")])
    )
except TypeError as e:
    print(f"捕获到错误: {e}")
登录后复制

上述代码尝试在filter表达式内部直接使用pl.col("cliente")和pl.col("cluster")作为字典键,这在Polars的表达式上下文中是无效的,因为pl.col(...)返回的是一个表达式对象,而不是实际的列值。

解决方案一:使用 map_elements 实现列值到字典键的映射

map_elements方法允许我们将Polars DataFrame中的结构化数据(例如,由多列组成的结构体)传递给一个Python函数进行处理。通过这种方式,我们可以在Python函数内部解析出列的实际值,并用它们来查询字典。

import polars as pl

# 示例数据和字典(同上)
df_x = pl.DataFrame({
    "cliente": ["A", "A", "B", "B", "C"],
    "cluster": ["X", "Y", "X", "Y", "X"],
    "score": [10, 20, 30, 40, 50]
})

nested_dict = {
    "A": {"X": 10, "Y": 25},
    "B": {"X": 35, "Y": 40},
    "C": {"X": 50, "Y": 55}
}

# 解决方案一:使用 map_elements
df_x_filtered_map = (
    df_x
    .filter(
        pl.col('score').eq(
            pl.struct('cliente','cluster') # 将多列组合成一个结构体
                .map_elements(lambda x: ( # 对每个结构体元素应用Python函数
                    nested_dict[x['cliente']][x['cluster']] # 在Python函数内部解析值并查询字典
                    ), return_dtype=pl.Int64 # 指定返回数据类型
                )
        )
    )
)
print("使用 map_elements 过滤后的结果:")
print(df_x_filtered_map)
登录后复制

说明:

  1. pl.struct('cliente','cluster') 将 cliente 和 cluster 两列组合成一个结构体(struct)列。
  2. .map_elements(lambda x: ..., return_dtype=...) 对这个结构体列的每一个元素(行)应用一个Python lambda函数。x 在这里是一个Python字典,其键是列名,值是当前行的列值。
  3. 在lambda函数内部,我们可以安全地使用 x['cliente'] 和 x['cluster'] 来访问字典 nested_dict。
  4. return_dtype 参数是必需的,它告诉Polars map_elements 返回的数据类型。

注意事项:

  • 效率问题: map_elements 会在Polars的内部优化器和Python解释器之间进行数据传递,这引入了Python的用户定义函数(UDF)开销。对于大型数据集,这种方法可能不是最有效的。
  • 类型指定: 必须为 map_elements 指定 return_dtype,以确保Polars能够正确处理返回结果。

解决方案二:优化方案——扁平化字典并使用 join 操作

为了获得更好的性能,尤其是处理大规模数据时,推荐的方法是将外部的Python字典转换为一个Polars DataFrame,然后使用Polars原生的 join 操作来合并数据并进行筛选。这种方法能够充分利用Polars的向量化和并行处理能力。

步骤一:扁平化 nested_dict 为 Polars DataFrame

我们需要将 nested_dict 转换为一个包含 cliente、cluster 和 cluster_value 三列的Polars DataFrame。

怪兽AI数字人
怪兽AI数字人

数字人短视频创作,数字人直播,实时驱动数字人

怪兽AI数字人 44
查看详情 怪兽AI数字人
import polars as pl

# 示例数据和字典(同上)
df_x = pl.DataFrame({
    "cliente": ["A", "A", "B", "B", "C"],
    "cluster": ["X", "Y", "X", "Y", "X"],
    "score": [10, 20, 30, 40, 50]
})

nested_dict = {
    "A": {"X": 10, "Y": 25},
    "B": {"X": 35, "Y": 40},
    "C": {"X": 50, "Y": 55}
}

# 扁平化 nested_dict 为 Polars DataFrame
df_nested_prelim = pl.from_dict(nested_dict) # 转换为初步的DataFrame
# print("初步转换的 df_nested_prelim:")
# print(df_nested_prelim)

df_nested_parts = []
for col_name in df_nested_prelim.columns:
    df_nested_parts.append(
        df_nested_prelim.lazy()
        .select(pl.col(col_name)).unnest(col_name) # 展开嵌套结构
        .unpivot(variable_name='cluster', value_name='cluster_value') # 将列名转换为cluster,值转换为cluster_value
        .with_columns(cliente=pl.lit(col_name)) # 添加cliente列,值为当前外部键
    )

df_nested = pl.concat(df_nested_parts).collect()
print("\n扁平化后的 df_nested:")
print(df_nested)
登录后复制

说明:

  1. pl.from_dict(nested_dict) 将字典转换为一个初步的DataFrame,其中外部键("A", "B", "C")成为列名,内部字典成为列表中的结构体。
  2. 我们遍历 df_nested_prelim 的每一列(即 cliente),然后:
    • .unnest(col_name) 展开该列中的嵌套结构。
    • .unpivot(variable_name='cluster', value_name='cluster_value') 将内部字典的键("X", "Y")转换为 cluster 列的值,将对应的值转换为 cluster_value 列。
    • .with_columns(cliente=pl.lit(col_name)) 添加 cliente 列,其值为当前外部键。
  3. pl.concat(df_nested_parts).collect() 将所有部分DataFrame合并成最终的扁平化DataFrame df_nested。

步骤二:使用 join 进行高效筛选

有了扁平化的 df_nested,我们就可以将其与原始DataFrame df_x 进行 join 操作,然后直接进行筛选。

# 解决方案二:使用 join 进行高效筛选
df_x_filtered_join = (
    df_x
    .join(df_nested, on=['cliente','cluster'], how='inner') # 根据 cliente 和 cluster 进行内连接
    .filter(pl.col('score')==pl.col('cluster_value')) # 筛选 score 等于 cluster_value 的行
    .select(df_x.columns) # 仅保留原始 df_x 的列,移除 join 引入的 cluster_value
)
print("\n使用 join 过滤后的结果:")
print(df_x_filtered_join)
登录后复制

说明:

  1. df_x.join(df_nested, on=['cliente','cluster'], how='inner') 通过 cliente 和 cluster 列将 df_x 与 df_nested 进行内连接。这意味着只有在两边都存在的 (cliente, cluster) 组合才会被保留,并且 df_nested 中的 cluster_value 列会被添加到 df_x 的行中。
  2. .filter(pl.col('score')==pl.col('cluster_value')) 接着使用Polars原生的筛选操作,比较 score 列和连接后得到的 cluster_value 列。
  3. .select(df_x.columns) 这一步是可选的,用于在筛选完成后,将结果DataFrame的列恢复到与原始 df_x 相同的结构,移除 join 操作引入的辅助列 cluster_value。

总结与选择建议

在Polars中利用列值作为字典键进行筛选时,直接使用Expr对象是不可行的。我们提供了两种有效的解决方案:

  1. map_elements 方法:

    • 优点: 语法上更接近于直接在Python中操作,对于小规模数据集或需要复杂Python逻辑的场景较为方便。
    • 缺点: 涉及Python UDF,效率相对较低,不适合大规模数据集。
  2. 扁平化字典并 join 方法:

    • 优点: 充分利用Polars的优化查询引擎,性能卓越,尤其适合大规模数据集。
    • 缺点: 需要额外的步骤将外部字典转换为Polars DataFrame,代码相对复杂一些。

选择建议:

  • 对于数据量较小,或者字典查询逻辑非常复杂难以用Polars表达式表达时,可以考虑使用 map_elements。
  • 对于绝大多数生产环境和大数据场景,强烈推荐将外部字典扁平化为Polars DataFrame,并使用 join 操作进行处理。这种方法虽然初期设置稍显复杂,但能带来显著的性能提升。

理解Polars的核心理念——尽可能使用其原生的、向量化的操作,避免Python UDF的频繁调用,是编写高效Polars代码的关键。

以上就是在Polars中高效利用列值作为字典键进行数据筛选的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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