
本文探讨了在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()表达式已经被解析为实际的列值。
一种直接但效率可能不高的解决方案是利用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)解释:
注意事项:map_elements 虽然解决了问题,但它会在Polars的优化器之外调用Python函数,这会引入Python解释器的开销。对于大型数据集,这种方法可能不是最高效的,因为它无法充分利用Polars的向量化和并行计算能力。
更符合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)解释:
现在,我们有了主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)解释:
在Polars中利用列值作为字典键进行数据过滤时,直接使用pl.col()表达式会导致类型错误。我们有两种主要解决方案:
在实际开发中,应优先考虑将外部查找数据转换为Polars DataFrame,并通过连接操作进行数据关联,以最大限度地发挥Polars的性能潜力。
以上就是Polars中利用列值作为字典键进行数据过滤的策略与实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号