什么是 polars 中的 `pandas.Series.map(json.loads)`?

What's the equivalent of `pandas.Series.map(json.loads)` in polars?

根据polars的文档,可以使用json_path_match将JSON个字段提取为字符串系列。

但是我们可以像 pandas.Series.map(json.loads) 那样一次性转换整个 JSON 字符串吗?然后可以进一步将加载的 JSON 系列转换为另一个具有合理数据类型的数据帧。

我知道我可以先在 pandas 中完成,但我正在 polars 中寻找方法。

我应该首先指出有一个polars.read_json方法。例如:

import polars as pl
import io

json_file = """[{"a":"1", "b":10, "c":[1,2,3]},
{"a":"2", "b":20, "c":[3,4,5]},
{"a":"3.1", "b":30.2, "c":[8,8,8]},
{"a":"4", "b":40.0, "c":[9,9,90]}]
"""

pl.read_json(io.StringIO(json_file))
shape: (4, 3)
┌─────┬──────┬────────────┐
│ a   ┆ b    ┆ c          │
│ --- ┆ ---  ┆ ---        │
│ str ┆ f64  ┆ list [i64] │
╞═════╪══════╪════════════╡
│ 1   ┆ 10.0 ┆ [1, 2, 3]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ 20.0 ┆ [3, 4, 5]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3.1 ┆ 30.2 ┆ [8, 8, 8]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4   ┆ 40.0 ┆ [9, 9, 90] │
└─────┴──────┴────────────┘

但是为了回答您关于 JSON 数据已经加载到系列中的具体问题,我认为您正在寻找的是 polars.Series.apply 方法,它将对每个单元格应用一个可调用函数极地系列。

例如,假设我们有以下 JSON 字段已经加载到 Polars DataFrame 的系列中:

import json
import polars as pl

df = pl.DataFrame(
    {
        "json_val": [
            '{"a":"1", "b":10, "c":[1,2,3]}',
            '{"a":"2", "b":20, "c":[3,4,5]}',
            '{"a":"3.1", "b":30.2, "c":[8,8,8]}',
            '{"a":"4", "b":40.0, "c":[9,9,90]}',
        ]
    }
)
print(df)
shape: (4, 1)
┌─────────────────────────────────────┐
│ json_val                            │
│ ---                                 │
│ str                                 │
╞═════════════════════════════════════╡
│ {"a":"1", "b":10, "c":[1,2,3]}      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"a":"2", "b":20, "c":[3,4,5]}      │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"a":"3.1", "b":30.2, "c":[8,8,8... │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"a":"4", "b":40.0, "c":[9,9,90]... │
└─────────────────────────────────────┘

我们可以使用applyjson.loads函数。在这个例子中,这将产生一个类型为 struct:

的系列
df.select(pl.col("json_val").apply(json.loads))
shape: (4, 1)
┌──────────────────────────┐
│ json_val                 │
│ ---                      │
│ struct[3]{'a', 'b', 'c'} │
╞══════════════════════════╡
│ {"1",10,[1, 2, 3]}       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"2",20,[3, 4, 5]}       │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"3.1",30,[8, 8, 8]}     │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"4",40,[9, 9, 90]}      │
└──────────────────────────┘

(注意,请注意第 b 列如何被截断为整数。)

根据 JSON 的结构,您还可以使用 polars.DataFrame.unnest 函数将 json_val 结构列拆分为单独的列。

df.select(pl.col("json_val").apply(json.loads)).unnest("json_val")
shape: (4, 3)
┌─────┬─────┬────────────┐
│ a   ┆ b   ┆ c          │
│ --- ┆ --- ┆ ---        │
│ str ┆ i64 ┆ list [i64] │
╞═════╪═════╪════════════╡
│ 1   ┆ 10  ┆ [1, 2, 3]  │
├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ 20  ┆ [3, 4, 5]  │
├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3.1 ┆ 30  ┆ [8, 8, 8]  │
├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4   ┆ 40  ┆ [9, 9, 90] │
└─────┴─────┴────────────┘

这对您有帮助吗?

编辑:处理 type-conversion 个问题

我对任何 un-typed 输入文件(尤其是 csv 文件)使用的一个通用策略是 return 所有值作为字符串/polars.Utf8 类型。这样,在我有机会目视检查结果后,我可以稍后显式转换类型。 (我经常被“自动”类型转换弄得焦头烂额。)

json.loads 方法有两个有用的关键字选项 parse_floatparse_int 在这种情况下会有所帮助。我们可以使用一个简单的 lambda 函数来告诉 json 解析器将整数和浮点列保留为字符串。

# define our own translate function to keep floats/ints as strings
def json_translate(json_str: str):
    return json.loads(json_str, parse_float=lambda x: x, parse_int=lambda x: x)

df.select(pl.col("json_val").apply(f=json_translate))
shape: (4, 1)
┌────────────────────────────────┐
│ json_val                       │
│ ---                            │
│ struct[3]{'a', 'b', 'c'}       │
╞════════════════════════════════╡
│ {"1","10",["1", "2", "3"]}     │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"2","20",["3", "4", "5"]}     │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"3.1","30.2",["8", "8", "8"]} │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ {"4","40.0",["9", "9", "90"]}  │
└────────────────────────────────┘

请注意,所有整数和浮点值都保留为字符串,并且在我们使用 unnest 函数时仍然如此(下面的 headers 列显示“str”)。

df.select(pl.col("json_val").apply(f=json_translate)).unnest('json_val')
shape: (4, 3)
┌─────┬──────┬──────────────────┐
│ a   ┆ b    ┆ c                │
│ --- ┆ ---  ┆ ---              │
│ str ┆ str  ┆ list [str]       │
╞═════╪══════╪══════════════════╡
│ 1   ┆ 10   ┆ ["1", "2", "3"]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ 20   ┆ ["3", "4", "5"]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 3.1 ┆ 30.2 ┆ ["8", "8", "8"]  │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 4   ┆ 40.0 ┆ ["9", "9", "90"] │
└─────┴──────┴──────────────────┘

从这一点来看,您可以使用 Polars 的 cast expression to convert the strings to the specific numeric types that you want. Here's a Stack Overflow question 来帮助 cast