0

0

Polars中利用列值作为字典键进行数据过滤的策略与实践

霞舞

霞舞

发布时间:2025-10-13 14:01:34

|

566人浏览过

|

来源于php中文网

原创

Polars中利用列值作为字典键进行数据过滤的策略与实践

本文探讨了在polars中尝试使用列值作为字典键时遇到的`typeerror: unhashable type: 'expr'`错误及其解决方案。核心问题在于polars表达式无法直接作为python字典的键。文章提供了两种主要策略:一是利用`map_elements`进行行级别转换,直接实现字典查找,但效率相对较低;二是推荐通过扁平化嵌套字典并与主dataframe进行连接(join)操作,这是一种更符合polars高性能特性的优化方法,能够显著提升数据过滤的效率和可维护性。

在Polars数据处理中,我们经常需要根据DataFrame中某一列或多列的值去查找外部Python字典中的对应数据。然而,直接将Polars的列表达式(如pl.col("cliente"))作为Python字典的键使用,会导致TypeError: unhashable type: 'Expr'错误。这是因为pl.col()返回的是一个Polars表达式对象,而非具体的、可哈希的值,Python字典无法将其作为键进行查找。本教程将详细介绍如何优雅且高效地解决这一问题。

理解问题根源:表达式的不可哈希性

当我们尝试执行类似以下代码时:

# 假设 nested_dict 是一个嵌套字典
# 例如: nested_dict = {'A': {'X': 10, 'Y': 20}, 'B': {'X': 30, 'Y': 40}}
# 假设 df_x 包含 'cliente', 'cluster', 'score' 列
df_x = (
    df_x
    .filter(pl.col("score") == nested_dict[pl.col("cliente")][pl.col("cluster")])
)

nested_dict[pl.col("cliente")]会尝试使用pl.col("cliente")这个Polars表达式对象作为字典的键。由于Polars表达式是代表计算逻辑的对象,而非具体的数据值,它不具备Python字典键所需的哈希性,因此会抛出TypeError: unhashable type: 'Expr'。

为了解决这个问题,我们需要确保在字典查找发生时,pl.col()表达式已经被解析为实际的列值。

解决方案一:使用 map_elements 进行行级别转换

一种直接但效率可能不高的解决方案是利用Polars的map_elements方法。map_elements允许我们将一个Python函数应用到Polars Series的每个元素上,从而在Python环境中解析列值并进行字典查找。

为了处理嵌套字典,我们首先需要将涉及到的多列(例如cliente和cluster)组合成一个结构体(struct),然后对这个结构体应用map_elements。

import polars as pl

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

nested_dict = {
    'A': {'X': 10, 'Y': 20},
    'B': {'X': 30, 'Y': 40},
    'C': {'X': 50, 'Y': 60}
}

# 使用 map_elements 进行过滤
filtered_df_map = (
    df_x
    .filter(
        pl.col('score').eq(
            pl.struct('cliente', 'cluster')
                .map_elements(lambda x: (
                    nested_dict[x['cliente']][x['cluster']]
                    ), return_dtype=pl.Int64) # 指定返回类型
        )
    )
)

print("使用 map_elements 过滤结果:")
print(filtered_df_map)

解释:

  1. pl.struct('cliente', 'cluster') 将 cliente 和 cluster 两列打包成一个结构体Series。
  2. .map_elements(lambda x: nested_dict[x['cliente']][x['cluster']], return_dtype=pl.Int64) 对这个结构体Series的每个元素(即每一行对应的 {'cliente': val1, 'cluster': val2} 字典)应用一个匿名函数。
  3. 在匿名函数内部,x['cliente'] 和 x['cluster'] 已经解析为具体的Python值,可以安全地作为nested_dict的键进行查找。
  4. return_dtype 参数非常重要,它告诉Polars map_elements 函数返回的数据类型,有助于Polars进行类型推断和优化。

注意事项:map_elements 虽然解决了问题,但它会在Polars的优化器之外调用Python函数,这会引入Python解释器的开销。对于大型数据集,这种方法可能不是最高效的,因为它无法充分利用Polars的向量化和并行计算能力。

MagickPen
MagickPen

在线AI英语写作助手,像魔术师一样在几秒钟内写出任何东西。

下载

解决方案二:优化方法 - 扁平化字典并进行连接(Join)

更符合Polars高性能哲学的做法是将外部的嵌套字典转换为一个Polars DataFrame,然后通过连接(join)操作将其与主DataFrame关联起来。这种方法将字典查找转换为DataFrame之间的列匹配,从而能够利用Polars的优化查询引擎。

步骤一:扁平化嵌套字典

首先,我们需要将 nested_dict 转换为一个扁平的Polars DataFrame,其中包含 cliente、cluster 和对应的 cluster_value 列。

# 扁平化嵌套字典
df_nested_prelim = pl.from_dict(nested_dict)

df_nested_parts = []
for col_name in df_nested_prelim.columns:
    df_nested_parts.append(
        df_nested_prelim.lazy()
        .select(pl.col(col_name).alias("cluster_data")) # 重命名,避免unnest后列名冲突
        .unnest("cluster_data") # 展开内部字典
        .unpivot(index_columns=[], variable_name='cluster', value_name='cluster_value') # 将cluster键转换为行
        .with_columns(cliente=pl.lit(col_name)) # 添加cliente列
    )

df_nested = pl.concat(df_nested_parts).collect()

print("\n扁平化后的字典DataFrame:")
print(df_nested)

解释:

  1. pl.from_dict(nested_dict) 将顶层字典键(A, B, C)转换为列名,内部字典作为单元格值。
  2. 循环遍历这些列:
    • .select(pl.col(col_name).alias("cluster_data")) 选取当前列并重命名为 cluster_data。
    • .unnest("cluster_data") 将 cluster_data 列中的嵌套字典展开成新的列(X, Y)。
    • .unpivot(index_columns=[], variable_name='cluster', value_name='cluster_value') 是关键一步,它将展开后的 X, Y 等列转换为行,cluster 列存储原列名(X或Y),cluster_value 存储对应的值。index_columns=[] 表示所有列都参与unpivot。
    • .with_columns(cliente=pl.lit(col_name)) 添加 cliente 列,其值为当前循环的顶层字典键。
  3. pl.concat(df_nested_parts).collect() 将所有扁平化后的部分DataFrame合并成一个完整的DataFrame。

步骤二:使用 join 进行过滤

现在,我们有了主DataFrame df_x 和扁平化的字典DataFrame df_nested。我们可以通过在 cliente 和 cluster 列上进行内连接(join),然后基于连接结果进行过滤。

# 使用 join 进行过滤
filtered_df_join = (
    df_x
    .join(df_nested, on=['cliente', 'cluster'], how='inner') # 内连接,只保留匹配项
    .filter(pl.col('score') == pl.col('cluster_value')) # 过滤条件
    .select(df_x.columns) # 只保留原始 df_x 的列
)

print("\n使用 join 过滤结果:")
print(filtered_df_join)

解释:

  1. df_x.join(df_nested, on=['cliente', 'cluster'], how='inner') 将 df_x 与 df_nested 基于 cliente 和 cluster 两列进行内连接。这意味着只有在两张表中 cliente 和 cluster 值都匹配的行才会被保留。
  2. filter(pl.col('score') == pl.col('cluster_value')) 过滤连接后的结果,只保留 score 列与 cluster_value 列(来自扁平化字典)相等。
  3. .select(df_x.columns) 这一步是可选的,用于确保最终输出的DataFrame只包含 df_x 的原始列,去除 join 操作可能引入的额外列(如 cluster_value)。

总结与建议

在Polars中利用列值作为字典键进行数据过滤时,直接使用pl.col()表达式会导致类型错误。我们有两种主要解决方案:

  1. map_elements 方法: 适用于逻辑复杂、涉及少量数据或对性能要求不高的场景。它提供了直接的Python函数集成能力,但会牺牲部分Polars的性能优势。
  2. 扁平化字典并 join 方法: 这是处理此类问题的推荐方法,尤其适用于大型数据集和对性能有严格要求的场景。它将Python字典查找转换为Polars的DataFrame操作,充分利用了Polars的向量化、并行化和查询优化能力,从而实现更高的效率和更好的可扩展性。

在实际开发中,应优先考虑将外部查找数据转换为Polars DataFrame,并通过连接操作进行数据关联,以最大限度地发挥Polars的性能潜力。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

309

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

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

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

240

2025.06.09

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

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

192

2025.07.04

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

208

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

191

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

55

2026.01.05

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

2

2026.01.29

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.4万人学习

Django 教程
Django 教程

共28课时 | 3.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

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

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