将Python字典映射到Polars系列

6
在Pandas中,我们可以使用map函数将字典映射到系列上,创建另一个具有映射值的系列。更一般地说,我认为它调用了参数的索引操作符,即[]
import pandas as pd

dic = { 1: 'a', 2: 'b', 3: 'c' }

pd.Series([1, 2, 3, 4]).map(dic) # returns ["a", "b", "c", NaN]

我还没有在 Polars 中找到直接实现的方法,不过找到了几个替代方案。这些方案中有哪个是推荐的方式,或者有更好的方法吗?

import polars as pl

dic = { 1: 'a', 2: 'b', 3: 'c' }

# Approach 1 - apply
pl.Series([1, 2, 3, 4]).apply(lambda v: dic.get(v, None)) # returns ["a", "b", "c", null]

# Approach 2 - left join
(
    pl.Series([1, 2, 3, 4])
    .alias('key')
    .to_frame()
    .join(
        pl.DataFrame({
            'key': list(dic.keys()),
            'value': list(dic.values()),
        }),
        on='key', how='left',
    )['value']
) # returns ["a", "b", "c", null]

# Approach 3 - to pandas and back
pl.from_pandas(pl.Series([1, 2, 3, 4]).to_pandas().map(dic)) # returns ["a", "b", "c", null]

我看到了这个关于将表达式字典映射到数据帧的回答,但由于它使用了when/then/otherwise语句链,对于大型字典可能效果不佳。

3个回答

7

更新 2023-03-20

Polars 有一个专门的 map_dict 表达式。请使用它。

旧答案

在 polars Series 上映射 Python 字典应始终被视为反模式。这将非常缓慢,而你想要的语义等同于连接。

使用连接。它们经过了大量优化,支持多线程,并且不使用 Python。

示例

import polars as pl

dic = { 1: 'a', 2: 'b', 3: 'c' }

mapper = pl.DataFrame({
    "keys": list(dic.keys()),
    "values": list(dic.values())
})

pl.Series([1, 2, 3, 4]).to_frame("keys").join(mapper, on="keys", how="left").to_series(1)

Series: 'values' [str]
[
    "a"
    "b"
    "c"
    null
]


你可以使用以下代码构建映射器 mapper=pl.DataFrame([{'keys':x, 'values':y} for x,y in dic.items()]),以获得轻微的性能提升。 - Dean MacGregor

6

自从版本0.16.3以来,Polars拥有了Expr.map_dict 方法,自0.16.7以来,Series.map_dict 方法也可以这样使用:

import polars as pl

mapping_dict = {1: "a", 2: "b", 3: "c"}

# pl.Series.map_dict
pl.Series([1, 2, 3, 4]).map_dict(mapping_dict)

# pl.Expr.map_dict
pl_df = pl.Series(name="to_map_col", values=[1, 2, 3, 4]).to_frame()

pl_df.with_columns(pl.col("to_map_col").map_dict(mapping_dict))

-1

Polars 是一个很棒的工具,但即使是很棒的工具也不是万能的,这就是其中之一。使用简单的 Python 列表推导式会更快。

你可以这样做:

[dic[x] if x in dic.keys() else None for x in [1,2,3,4]]

在我的电脑上,使用%%timeit计时,时间为800纳秒

与此形成对比的是

pl.Series([1, 2, 3, 4]).to_frame("keys").join(pl.DataFrame([{'keys':x, 'values':y} for x,y in dic.items()]), on="keys", how="left").to_series(1)

需要434微秒。

请注意,第一个是以纳秒为单位测量的,而第二个是以微秒为单位测量的,因此实际上是800纳秒对434000纳秒。


我认为这不具有可扩展性。我想象OP拥有比此MWE中给出的更多行。 - ritchie46
@ritchie46 是的,我也这么认为。我只是觉得奇怪,他们希望他们的输出成为独立系列。 - Dean MacGregor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接