我有以下代码:

import polars as pl
from typing import NamedTuple


class Event(NamedTuple):
    name: str
    description: str


def event_table(num) -> list[Event]:
    events = []
    for i in range(num):
        events.append(Event("name", "description"))
    return events


data = {"events": [1, 2]}
df = pl.DataFrame(data).select(events=pl.col("events").map_elements(event_table))

"""
shape: (2, 1)
┌───────────────────────────────────┐
│ events                            │
│ ---                               │
│ list[struct[2]]                   │
╞═══════════════════════════════════╡
│ [{"name","description"}]          │
│ [{"name","description"}, {"name"… │
└───────────────────────────────────┘
"""

但是如果第一个列表是空的,我得到的是list[list[str]]而不是我需要的list[struct[2]]:

data = {"events": [0, 1, 2]}
df = pl.DataFrame(data).select(events=pl.col("events").map_elements(event_table))
print(df)

"""
shape: (3, 1)
┌───────────────────────────────────┐
│ events                            │
│ ---                               │
│ list[list[str]]                   │
╞═══════════════════════════════════╡
│ []                                │
│ [["name", "description"]]         │
│ [["name", "description"], ["name… │
└───────────────────────────────────┘
"""

我试着使用map_elements函数的return_dtype,比如:

data = {"events": [0, 1, 2]}
df = pl.DataFrame(data).select(
    events=pl.col("events").map_elements(
        event_table,
        return_dtype=pl.List(pl.Struct({"name": pl.String, "description": pl.String})),
    )
)

但这失败了:

Traceback (most recent call last):
  File "script.py", line 18, in <module>
    df = pl.DataFrame(data).select(
         ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/polars/dataframe/frame.py", line 8193, in select
    return self.lazy().select(*exprs, **named_exprs).collect(_eager=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".venv/lib/python3.11/site-packages/polars/lazyframe/frame.py", line 1943, in collect
    return wrap_df(ldf.collect())
                   ^^^^^^^^^^^^^
polars.exceptions.SchemaError: expected output type 'List(Struct([Field { name: "name", dtype: String }, Field { name: "description", dtype: String }]))', got 'List(List(String))'; set `return_dtype` to the proper datatype

我怎么才能让它工作?如果第一个列表是空的,我需要这个列的类型是list[struct[2]]事件.

推荐答案

快解决

这里有一个map_batches的实现,应该至少稍微快一些.

def event_table(col: pl.Series) -> pl.Series:
    return pl.Series(
        [
            [
                Event("name", "description")._asdict() #note ._asdict()
                for _ in range(num)
                ]
            for num in col
            ]
        )

它使用嵌套的列表解释,这应该比在显式for循环中附加到列表快一点,但这是Python优化而不是polars.

pl.DataFrame(data).select(events=pl.col("events").map_batches(event_table))
shape: (3, 1)
┌───────────────────────────────────┐
│ events                            │
│ ---                               │
│ list[struct[2]]                   │
╞═══════════════════════════════════╡
│ []                                │
│ [{"name","description"}]          │
│ [{"name","description"}, {"name"… │
└───────────────────────────────────┘

实际上,你只需要使用_asdict()而不是依靠极来推断NamedTuple应该是什么.

中期至长期解决方案

问题是here,特别是在某些路径中,它对待元组和列表是相同的,因为NamedTuple是一个元组,这就是为什么它被返回为列表.

This PR使它判断_asdict方法,并转向将其视为dict/struct.

有了这个公关,你可以做

class Event(NamedTuple):
    name: str
    description: str

def event_table(num: int) -> list[Event]:
    return [Event("name", "desc") for _ in range(num)]

data = {"events": [0, 1, 2]}
pl.DataFrame(data).select(
    events=pl.col("events").map_elements(
        event_table,
        return_dtype=pl.List(
            pl.Struct({"name": pl.String, "description": pl.String})
        ),
    )
)
shape: (3, 1)
┌───────────────────────────────────┐
│ events                            │
│ ---                               │
│ list[struct[2]]                   │
╞═══════════════════════════════════╡
│ []                                │
│ [{"name","desc"}]                 │
│ [{"name","desc"}, {"name","desc"… │
└───────────────────────────────────┘

Python相关问答推荐

Pandas 填充条件是另一列

在Python中对分层父/子列表进行排序

如果条件为真,则Groupby.mean()

Odoo 14 hr. emergency.public内的二进制字段

将特定列信息移动到当前行下的新行

rame中不兼容的d类型

处理(潜在)不断增长的任务队列的并行/并行方法

log 1 p numpy的意外行为

pyscript中的压痕问题

转换为浮点,pandas字符串列,混合千和十进制分隔符

如何启动下载并在不击中磁盘的情况下呈现响应?

旋转多边形而不改变内部空间关系

Python Pandas—时间序列—时间戳缺失时间精确在00:00

剪切间隔以添加特定日期

从源代码显示不同的输出(机器学习)(Python)

从嵌套极轴列的列表中删除元素

numpy数组和数组标量之间的不同行为

PySpark:如何最有效地读取不同列位置的多个CSV文件

如何将列表从a迭代到z-以抓取数据并将其转换为DataFrame?

如何使用Polars从AWS S3读取镶木地板文件