Skip to content

字符串

以下部分讨论了对字符串数据执行的操作,这是在处理数据框时经常使用的数据类型。字符串处理函数在命名空间 str 中可用。

在其他数据框库中处理字符串可能非常低效,因为字符串的长度不可预测。Polars通过遵循Arrow列式格式规范来缓解这些低效问题,因此您也可以在字符串数据上编写高效的数据查询。

字符串命名空间

在处理字符串数据时,您可能需要访问命名空间 str,该命名空间汇集了40多个函数,让您可以处理字符串。作为如何从该命名空间访问函数的示例,下面的代码片段展示了如何计算列中字符串的字节数和字符数:

str.len_bytes · str.len_chars

import polars as pl

df = pl.DataFrame(
    {
        "language": ["English", "Dutch", "Portuguese", "Finish"],
        "fruit": ["pear", "peer", "pêra", "päärynä"],
    }
)

result = df.with_columns(
    pl.col("fruit").str.len_bytes().alias("byte_count"),
    pl.col("fruit").str.len_chars().alias("letter_count"),
)
print(result)

str.len_bytes · str.len_chars

use polars::prelude::*;

let df = df! (
    "language" => ["English", "Dutch", "Portuguese", "Finish"],
    "fruit" => ["pear", "peer", "pêra", "päärynä"],
)?;

let result = df
    .clone()
    .lazy()
    .with_columns([
        col("fruit").str().len_bytes().alias("byte_count"),
        col("fruit").str().len_chars().alias("letter_count"),
    ])
    .collect()?;

println!("{}", result);

shape: (4, 4)
┌────────────┬─────────┬────────────┬──────────────┐
│ language   ┆ fruit   ┆ byte_count ┆ letter_count │
│ ---        ┆ ---     ┆ ---        ┆ ---          │
│ str        ┆ str     ┆ u32        ┆ u32          │
╞════════════╪═════════╪════════════╪══════════════╡
│ English    ┆ pear    ┆ 4          ┆ 4            │
│ Dutch      ┆ peer    ┆ 4          ┆ 4            │
│ Portuguese ┆ pêra    ┆ 5          ┆ 4            │
│ Finish     ┆ päärynä ┆ 10         ┆ 7            │
└────────────┴─────────┴────────────┴──────────────┘

注意

如果您只处理ASCII文本,那么两种计算的结果将是相同的,并且建议使用len_bytes,因为它更快。

解析字符串

Polars 提供了多种方法来检查和解析字符串列的元素,即检查给定子字符串或模式的存在,并对它们进行计数、提取或替换。我们将在接下来的示例中演示其中一些操作。

检查模式是否存在

我们可以使用函数contains来检查字符串中是否存在某个模式。默认情况下,函数contains的参数被解释为正则表达式。如果你想指定一个字面量的子字符串,将参数literal设置为True

对于您想要检查字符串是否以固定子字符串开头或结尾的特殊情况,您可以分别使用函数starts_withends_with

str.contains · str.starts_with · str.ends_with

result = df.select(
    pl.col("fruit"),
    pl.col("fruit").str.starts_with("p").alias("starts_with_p"),
    pl.col("fruit").str.contains("p..r").alias("p..r"),
    pl.col("fruit").str.contains("e+").alias("e+"),
    pl.col("fruit").str.ends_with("r").alias("ends_with_r"),
)
print(result)

str.contains · str.starts_with · str.ends_with · 在功能 regex 上可用

let result = df
    .clone()
    .lazy()
    .select([
        col("fruit"),
        col("fruit")
            .str()
            .starts_with(lit("p"))
            .alias("starts_with_p"),
        col("fruit").str().contains(lit("p..r"), true).alias("p..r"),
        col("fruit").str().contains(lit("e+"), true).alias("e+"),
        col("fruit").str().ends_with(lit("r")).alias("ends_with_r"),
    ])
    .collect()?;

println!("{}", result);

shape: (4, 5)
┌─────────┬───────────────┬───────┬───────┬─────────────┐
│ fruit   ┆ starts_with_p ┆ p..r  ┆ e+    ┆ ends_with_r │
│ ---     ┆ ---           ┆ ---   ┆ ---   ┆ ---         │
│ str     ┆ bool          ┆ bool  ┆ bool  ┆ bool        │
╞═════════╪═══════════════╪═══════╪═══════╪═════════════╡
│ pear    ┆ true          ┆ true  ┆ true  ┆ true        │
│ peer    ┆ true          ┆ true  ┆ true  ┆ true        │
│ pêra    ┆ true          ┆ false ┆ false ┆ false       │
│ päärynä ┆ true          ┆ true  ┆ false ┆ false       │
└─────────┴───────────────┴───────┴───────┴─────────────┘

正则表达式规范

Polars 依赖于 Rust 的 regex 库来处理正则表达式,因此你可能需要 参考语法文档 来查看支持的功能 和标志。特别要注意的是,Polars 支持的正则表达式风格与 Python 的 re 模块不同。

提取模式

函数 extract 允许我们从列中的字符串值中提取模式。函数 extract 接受一个带有一个或多个捕获组的正则表达式模式,并提取指定为第二个参数的捕获组。

str.extract

df = pl.DataFrame(
    {
        "urls": [
            "http://vote.com/ballon_dor?candidate=messi&ref=polars",
            "http://vote.com/ballon_dor?candidat=jorginho&ref=polars",
            "http://vote.com/ballon_dor?candidate=ronaldo&ref=polars",
        ]
    }
)
result = df.select(
    pl.col("urls").str.extract(r"candidate=(\w+)", group_index=1),
)
print(result)

str.extract

let df =& df! (
    "urls" => [
        "http://vote.com/ballon_dor?candidate=messi&ref=polars",
        "http://vote.com/ballon_dor?candidat=jorginho&ref=polars",
        "http://vote.com/ballon_dor?candidate=ronaldo&ref=polars",
    ]
)?;

let result =& df
    .clone()
    .lazy()
    .select([col("urls").str().extract(lit(r"candidate=(\w+)"), 1)])
    .collect()?;

println!("{}", result);

shape: (3, 1)
┌─────────┐
│ urls    │
│ ---     │
│ str     │
╞═════════╡
│ messi   │
│ null    │
│ ronaldo │
└─────────┘

要提取字符串中所有出现的模式,我们可以使用函数extract_all。在下面的示例中,我们使用正则表达式模式(\d+)从字符串中提取所有数字,该模式匹配一个或多个数字。函数extract_all的结果输出是一个包含字符串中所有匹配模式实例的列表。

str.extract_all

df = pl.DataFrame({"text": ["123 bla 45 asd", "xyz 678 910t"]})
result = df.select(
    pl.col("text").str.extract_all(r"(\d+)").alias("extracted_nrs"),
)
print(result)

str.extract_all

let df = df! (
    "text" => ["123 bla 45 asd", "xyz 678 910t"]
)?;

let result = df
    .clone()
    .lazy()
    .select([col("text")
        .str()
        .extract_all(lit(r"(\d+)"))
        .alias("extracted_nrs")])
    .collect()?;

println!("{}", result);

shape: (2, 1)
┌────────────────┐
│ extracted_nrs  │
│ ---            │
│ list[str]      │
╞════════════════╡
│ ["123", "45"]  │
│ ["678", "910"] │
└────────────────┘

替换模式

类似于函数extractextract_all,Polars提供了函数replacereplace_all。这些函数接受一个正则表达式模式或一个字面量子字符串(如果参数literal设置为True),并执行指定的替换。函数replace最多只会进行一次替换,而函数replace_all则会进行所有非重叠的替换。

str.replace · str.replace_all

df = pl.DataFrame({"text": ["123abc", "abc456"]})
result = df.with_columns(
    pl.col("text").str.replace(r"\d", "-"),
    pl.col("text").str.replace_all(r"\d", "-").alias("text_replace_all"),
)
print(result)

str.replace · str.replace_all · 在功能 regex 上可用

let df = df! (
    "text" => ["123abc", "abc456"]
)?;

let result = df
    .clone()
    .lazy()
    .with_columns([
        col("text").str().replace(lit(r"\d"), lit("-"), false),
        col("text")
            .str()
            .replace_all(lit(r"\d"), lit("-"), false)
            .alias("text_replace_all"),
    ])
    .collect()?;

println!("{}", result);

shape: (2, 2)
┌────────┬──────────────────┐
│ text   ┆ text_replace_all │
│ ---    ┆ ---              │
│ str    ┆ str              │
╞════════╪══════════════════╡
│ -23abc ┆ ---abc           │
│ abc-56 ┆ abc---           │
└────────┴──────────────────┘

修改字符串

大小写转换

转换字符串的大小写是一个常见的操作,Polars 通过函数 to_lowercaseto_titlecaseto_uppercase 提供了开箱即用的支持:

str.to_lowercase · str.to_titlecase · str.to_uppercase

addresses = pl.DataFrame(
    {
        "addresses": [
            "128 PERF st",
            "Rust blVD, 158",
            "PoLaRs Av, 12",
            "1042 Query sq",
        ]
    }
)

addresses = addresses.select(
    pl.col("addresses").alias("originals"),
    pl.col("addresses").str.to_titlecase(),
    pl.col("addresses").str.to_lowercase().alias("lower"),
    pl.col("addresses").str.to_uppercase().alias("upper"),
)
print(addresses)

str.to_lowercase · str.to_titlecase · str.to_uppercase · 可在功能 nightly 上使用

let addresses = df! (
    "addresses" => [
        "128 PERF st",
        "Rust blVD, 158",
        "PoLaRs Av, 12",
        "1042 Query sq",
    ]
)?;

let addresses = addresses
    .clone()
    .lazy()
    .select([
        col("addresses").alias("originals"),
        col("addresses").str().to_titlecase(),
        col("addresses").str().to_lowercase().alias("lower"),
        col("addresses").str().to_uppercase().alias("upper"),
    ])
    .collect()?;

println!("{}", addresses);

shape: (4, 4)
┌────────────────┬────────────────┬────────────────┬────────────────┐
│ originals      ┆ addresses      ┆ lower          ┆ upper          │
│ ---            ┆ ---            ┆ ---            ┆ ---            │
│ str            ┆ str            ┆ str            ┆ str            │
╞════════════════╪════════════════╪════════════════╪════════════════╡
│ 128 PERF st    ┆ 128 Perf St    ┆ 128 perf st    ┆ 128 PERF ST    │
│ Rust blVD, 158 ┆ Rust Blvd, 158 ┆ rust blvd, 158 ┆ RUST BLVD, 158 │
│ PoLaRs Av, 12  ┆ Polars Av, 12  ┆ polars av, 12  ┆ POLARS AV, 12  │
│ 1042 Query sq  ┆ 1042 Query Sq  ┆ 1042 query sq  ┆ 1042 QUERY SQ  │
└────────────────┴────────────────┴────────────────┴────────────────┘

从两端去除字符

Polars 在命名空间 str 中提供了五个函数,允许你从字符串的末尾去除字符:

Function Behaviour
strip_chars Removes leading and trailing occurrences of the characters specified.
strip_chars_end Removes trailing occurrences of the characters specified.
strip_chars_start Removes leading occurrences of the characters specified.
strip_prefix Removes an exact substring prefix if present.
strip_suffix Removes an exact substring suffix if present.
Similarity to Python string methods

strip_chars 类似于 Python 的字符串方法 strip,而 strip_prefix/strip_suffix 分别类似于 Python 的字符串方法 removeprefixremovesuffix

重要的是要理解,前三个函数将它们的字符串参数解释为一组字符,而函数strip_prefixstrip_suffix则将它们的字符串参数解释为字面字符串。

str.strip_chars · str.strip_chars_end · str.strip_chars_start · str.strip_prefix · str.strip_suffix

addr = pl.col("addresses")
chars = ", 0123456789"
result = addresses.select(
    addr.str.strip_chars(chars).alias("strip"),
    addr.str.strip_chars_end(chars).alias("end"),
    addr.str.strip_chars_start(chars).alias("start"),
    addr.str.strip_prefix("128 ").alias("prefix"),
    addr.str.strip_suffix(", 158").alias("suffix"),
)
print(result)

str.strip_chars · str.strip_chars_end · str.strip_chars_start · str.strip_prefix · str.strip_suffix

let addr = col("addresses");
let chars = lit(", 0123456789");
let result = addresses
    .clone()
    .lazy()
    .select([
        addr.clone().str().strip_chars(chars.clone()).alias("strip"),
        addr.clone()
            .str()
            .strip_chars_end(chars.clone())
            .alias("end"),
        addr.clone()
            .str()
            .strip_chars_start(chars.clone())
            .alias("start"),
        addr.clone().str().strip_prefix(lit("128 ")).alias("prefix"),
        addr.clone()
            .str()
            .strip_suffix(lit(", 158"))
            .alias("suffix"),
    ])
    .collect()?;

println!("{}", result);

shape: (4, 5)
┌───────────┬───────────────┬────────────────┬────────────────┬───────────────┐
│ strip     ┆ end           ┆ start          ┆ prefix         ┆ suffix        │
│ ---       ┆ ---           ┆ ---            ┆ ---            ┆ ---           │
│ str       ┆ str           ┆ str            ┆ str            ┆ str           │
╞═══════════╪═══════════════╪════════════════╪════════════════╪═══════════════╡
│ Perf St   ┆ 128 Perf St   ┆ Perf St        ┆ Perf St        ┆ 128 Perf St   │
│ Rust Blvd ┆ Rust Blvd     ┆ Rust Blvd, 158 ┆ Rust Blvd, 158 ┆ Rust Blvd     │
│ Polars Av ┆ Polars Av     ┆ Polars Av, 12  ┆ Polars Av, 12  ┆ Polars Av, 12 │
│ Query Sq  ┆ 1042 Query Sq ┆ Query Sq       ┆ 1042 Query Sq  ┆ 1042 Query Sq │
└───────────┴───────────────┴────────────────┴────────────────┴───────────────┘

如果没有提供参数,三个函数 strip_charsstrip_chars_endstrip_chars_start 默认会移除空白字符。

切片

除了根据模式提取子字符串,您还可以在指定的偏移量处切片字符串以生成子字符串。用于切片的通用函数是slice,它接受起始偏移量和可选的切片长度。如果未指定切片的长度或长度超过字符串的末尾,Polars 会将字符串切片到末尾。

函数 headtail 是专门用于分别切片字符串开头和结尾的版本。

str.slice · str.head · str.tail

df = pl.DataFrame(
    {
        "fruits": ["pear", "mango", "dragonfruit", "passionfruit"],
        "n": [1, -1, 4, -4],
    }
)

result = df.with_columns(
    pl.col("fruits").str.slice(pl.col("n")).alias("slice"),
    pl.col("fruits").str.head(pl.col("n")).alias("head"),
    pl.col("fruits").str.tail(pl.col("n")).alias("tail"),
)
print(result)

str.str_slice · str.str_head · str.str_tail

let df = df! (
    "fruits" => ["pear", "mango", "dragonfruit", "passionfruit"],
    "n" => [1, -1, 4, -4],
)?;

let result = df
    .clone()
    .lazy()
    .with_columns([
        col("fruits")
            .str()
            .slice(col("n"), lit(NULL))
            .alias("slice"),
        col("fruits").str().head(col("n")).alias("head"),
        col("fruits").str().tail(col("n")).alias("tail"),
    ])
    .collect()?;

println!("{}", result);

shape: (4, 5)
┌──────────────┬─────┬─────────┬──────────┬──────────┐
│ fruits       ┆ n   ┆ slice   ┆ head     ┆ tail     │
│ ---          ┆ --- ┆ ---     ┆ ---      ┆ ---      │
│ str          ┆ i64 ┆ str     ┆ str      ┆ str      │
╞══════════════╪═════╪═════════╪══════════╪══════════╡
│ pear         ┆ 1   ┆ ear     ┆ p        ┆ r        │
│ mango        ┆ -1  ┆ o       ┆ mang     ┆ ango     │
│ dragonfruit  ┆ 4   ┆ onfruit ┆ drag     ┆ ruit     │
│ passionfruit ┆ -4  ┆ ruit    ┆ passionf ┆ ionfruit │
└──────────────┴─────┴─────────┴──────────┴──────────┘

API 文档

除了上面提到的例子之外,Polars 还提供了各种其他字符串操作函数。要探索这些额外的方法,您可以前往您选择的编程语言的 Polars API 文档。