字符串

字符串数据是处理DataFrame时经常使用的数据类型, 命名空间str提供了字符串处理函数

由于字符串的长度不可预测, 在其他DataFrame库中使用字符串可能会比较低效. Polars通过遵循Arrow Columnar Format规范来缓解这些问题

str命名空间

处理字符串的数据时,可能需要访问命名空间 str, 该命名空间聚合了40多个可用于处理字符串的函数.

下面代码片段展示了如何从该命名空间访问函数, 并展示了如何根据字节数和字符数计算列中字符串的长度

1import polars as pl
2
3df = pl.DataFrame(
4    {
5        "language": ["English", "Dutch", "Portuguese","中文"],
6        "fruit": ["pear", "peer", "pêra","苹果"],
7    }
8)
9
10result = df.select(
11    pl.col("fruit"),
12    pl.col("fruit").str.len_bytes().alias("byte_count"),
13    pl.col("fruit").str.len_chars().alias("letter_count"),
14)
15print(result)

注意到 "苹果" 占用6个字节, 是因为常用汉字占3个字节

1shape: (4, 3)
2┌───────┬────────────┬──────────────┐
3│ fruit ┆ byte_count ┆ letter_count │
4│ ---   ┆ ---        ┆ ---          │
5│ str   ┆ u32        ┆ u32          │
6╞═══════╪════════════╪══════════════╡
7│ pear  ┆ 4          ┆ 4            │
8│ peer  ┆ 4          ┆ 4            │
9│ pêra  ┆ 5          ┆ 4            │
10│ 苹果  ┆ 6          ┆ 2            │
11└───────┴────────────┴──────────────┘
NOTE

如果确定只处理ASCII文本, 那么字节数和字符数是一样的, 所以建议使用更快的str.len_bytes()方法

解析字符串

Polars提供了多种方法来检查和解析字符串列中的元素, 例如检查给定的子字符串或模式是否存在, 以及对它们计数, 提取或替换

检查是否包含模式

  • 使用 str.contains() 检查字符串是否包含与模式匹配的子字符串, 子列表是形参参数说明
    • pattern: 一个有效的正则表达式模式
    • literal: 将pattern视为文本字符串, 而不是正则表达式
    • strict : 如果底层模式不是有效的正则表达式, 则引发错误, 否则使用null值进行覆盖
  • 使用 str.starts_with() 检查给定字符串列是否以给定子字符串开头
  • 使用 str.ends_with() 检查给定字符串列是否以给定子字符串结尾
1result = df.select(
2    pl.col("fruit"),
3    pl.col("fruit").str.starts_with("p").alias("starts_with_p"), # 以p开头
4    pl.col("fruit").str.ends_with("r").alias("ends_with_r"),     # 以r结尾
5    pl.col("fruit").str.contains("p..r").alias("p..r"),          # "p"和"r"之间可以有任意的两个字符
6    pl.col("fruit").str.contains("e+").alias("e+"),              # 至少出现一个"e"
7    pl.col("fruit").str.contains("苹").alias("苹")                # 也支持中文!
8)
9print(result)
1shape: (4, 6)
2┌───────┬───────────────┬─────────────┬───────┬───────┬───────┐
3│ fruit ┆ starts_with_p ┆ ends_with_r ┆ p..r  ┆ e+    ┆ 苹    │
4│ ---   ┆ ---           ┆ ---         ┆ ---   ┆ ---   ┆ ---   │
5│ str   ┆ bool          ┆ bool        ┆ bool  ┆ bool  ┆ bool  │
6╞═══════╪═══════════════╪═════════════╪═══════╪═══════╪═══════╡
7│ pear  ┆ true          ┆ true        ┆ true  ┆ true  ┆ false │
8│ peer  ┆ true          ┆ true        ┆ true  ┆ true  ┆ false │
9│ pêra  ┆ true          ┆ false       ┆ false ┆ false ┆ false │
10│ 苹果  ┆ false         ┆ false       ┆ false ┆ false ┆ true  │
11└───────┴───────────────┴─────────────┴───────┴───────┴───────┘

正则表达式规范

INFO

Polars使用Rust crate regex 库来解析正则表达式, 语法和Python的re模块略有不同, 具体可以查看文档

提取模式

str.extract() 提取匹配到的字符串

1import polars as pl
2
3df = pl.DataFrame(
4    {
5        "urls": [
6            "http://vote.com/ballon_dor?candidate=messi&ref=polars",
7            "http://vote.com/ballon_dor?candidat=jorginho&ref=polars",
8            "http://vote.com/ballon_dor?candidate=ronaldo&ref=polars",
9        ]
10    }
11)
12result = df.select(
13    pl.col("urls").str.extract(r"candidate=(\w+)", group_index=1), # (\w+): 匹配一个或多个单词字符
14)
15print(result)
1shape: (3, 1)
2┌─────────┐
3│ urls    │
4│ ---     │
5│ str     │
6╞═════════╡
7│ messi   │
8│ null    │
9│ ronaldo │
10└─────────┘

要匹配某个模式的所有出现位置, 可以使用函数extract_all, 看下面的代码

1import polars as pl
2df = pl.DataFrame({"text": ["123 bla 45 asd", "xyz 678 910t"]})
3result = df.select(
4    pl.col("text").str.extract_all(r"(\d+)").alias("extracted_nrs"),
5)
6print(result)
1shape: (2, 1)
2┌────────────────┐
3│ extracted_nrs  │
4│ ---            │
5│ list[str]      │
6╞════════════════╡
7│ ["123", "45"]  │
8│ ["678", "910"] │
9└────────────────┘

替换模式

Polars提供了replacereplace_all, replace最多进行一次替换

1df = pl.DataFrame({"text": ["123abc", "abc456"]})
2result = df.with_columns(
3    pl.col("text").str.replace(r"\d", "-"),
4    pl.col("text").str.replace_all(r"\d", "-").alias("text_replace_all"),
5)
6print(result)
1shape: (2, 2)
2┌────────┬──────────────────┐
3│ text   ┆ text_replace_all │
4│ ---    ┆ ---              │
5│ str    ┆ str              │
6╞════════╪══════════════════╡
7│ -23abc ┆ ---abc           │
8│ abc-56 ┆ abc---           │
9└────────┴──────────────────┘

修改字符串

大小写转换

Polars提供了: str.to_titlecase(), str.to_lowercase, str.to_uppercase

1addresses = pl.DataFrame(
2    {
3        "addresses": [
4            "128 PERF st",
5            "Rust blVD, 158",
6            "PoLaRs Av, 12",
7            "1042 Query sq",
8        ]
9    }
10)
11
12addresses = addresses.select(
13    pl.col("addresses").alias("originals"),
14    pl.col("addresses").str.to_titlecase(),
15    pl.col("addresses").str.to_lowercase().alias("lower"),
16    pl.col("addresses").str.to_uppercase().alias("upper"),
17)
18print(addresses)
1shape: (4, 4)
2┌────────────────┬────────────────┬────────────────┬────────────────┐
3│ originals      ┆ addresses      ┆ lower          ┆ upper          │
4│ ---            ┆ ---            ┆ ---            ┆ ---            │
5│ str            ┆ str            ┆ str            ┆ str            │
6╞════════════════╪════════════════╪════════════════╪════════════════╡
7│ 128 PERF st    ┆ 128 Perf St    ┆ 128 perf st    ┆ 128 PERF ST    │
8│ Rust blVD, 158 ┆ Rust Blvd, 158 ┆ rust blvd, 158 ┆ RUST BLVD, 158 │
9│ PoLaRs Av, 12  ┆ Polars Av, 12  ┆ polars av, 12  ┆ POLARS AV, 12  │
10│ 1042 Query sq  ┆ 1042 Query Sq  ┆ 1042 query sq  ┆ 1042 QUERY SQ  │
11└────────────────┴────────────────┴────────────────┴────────────────┘

从末尾剥离字符

Polars在命名空间str提供了5各函数, 可以从字符串末尾去除字符

功能 行为
strip_chars 删除指定字符的前导和尾随出现的内容
strip_chars_end 删除指定字符的尾随出现部分
strip_chars_start 删除指定字符的前导出现
strip_prefix 如果存在,则删除精确的子字符串前缀
strip_suffix 如果存在,则删除精确的子字符串后缀
1addr = pl.col("addresses") # 上面例子中的数据
2chars = ", 0123456789"
3result = addresses.select(
4    addr.str.strip_chars(chars).alias("strip"),
5    addr.str.strip_chars_end(chars).alias("end"),
6    addr.str.strip_chars_start(chars).alias("start"),
7    addr.str.strip_prefix("128 ").alias("prefix"),
8    addr.str.strip_suffix(", 158").alias("suffix"),
9)
10print(result)
1shape: (4, 5)
2┌───────────┬───────────────┬────────────────┬────────────────┬───────────────┐
3│ strip     ┆ end           ┆ start          ┆ prefix         ┆ suffix        │
4│ ---       ┆ ---           ┆ ---            ┆ ---            ┆ ---           │
5│ str       ┆ str           ┆ str            ┆ str            ┆ str           │
6╞═══════════╪═══════════════╪════════════════╪════════════════╪═══════════════╡
7│ Perf St   ┆ 128 Perf St   ┆ Perf St        ┆ Perf St        ┆ 128 Perf St   │
8│ Rust Blvd ┆ Rust Blvd     ┆ Rust Blvd, 158 ┆ Rust Blvd, 158 ┆ Rust Blvd     │
9│ Polars Av ┆ Polars Av     ┆ Polars Av, 12  ┆ Polars Av, 12  ┆ Polars Av, 12 │
10│ Query Sq  ┆ 1042 Query Sq ┆ Query Sq       ┆ 1042 Query Sq  ┆ 1042 Query Sq │
11└───────────┴───────────────┴────────────────┴────────────────┴───────────────┘

如果没有提供参数, 则三个函数strip_charsstrip_chars_endstrip_chars_start默认删除空格

切片

除了根据模式指定的方式提取子字符串外, 还可以按指定的偏移量对字符串进行切片, 从而生成子字符串

切片的通用函数是slice, 它接受起始偏移量和切片的可选长度. 如果切片的长度未指定, 或者长度超过了字符串的末尾, Polars会将字符串一直切片到末尾

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

1# 我们先看slice定义, 如果要传递`length`, 就必须显式传递
2def slice(self,
3          offset: int | Expr | Series | str,
4          length: int | Expr | Series | str | None = None) -> Expr
1df = pl.DataFrame(
2    {
3        "fruits": ["pear", "mango", "dragonfruit", "passionfruit"],
4        "n": [1, -1, 4, -4],
5    }
6)
7
8result = df.with_columns(
9    pl.col("fruits").str.slice(pl.col("n")).alias("slice"), # 针对fruits的每一行数据, 分别从对应的n的位置开始切片
10    pl.col("fruits").str.slice(1).alias("slice_1"), # 针对fruits的每一行数据, 都扔掉第一个字符
11    pl.col("fruits").str.head(pl.col("n")).alias("head"),
12    pl.col("fruits").str.tail(pl.col("n")).alias("tail"),
13)
14print(result)
1shape: (4, 6)
2┌──────────────┬─────┬─────────┬─────────────┬──────────┬──────────┐
3│ fruits       ┆ n   ┆ slice   ┆ slice_1     ┆ head     ┆ tail     │
4│ ---          ┆ --- ┆ ---     ┆ ---         ┆ ---      ┆ ---      │
5│ str          ┆ i64 ┆ str     ┆ str         ┆ str      ┆ str      │
6╞══════════════╪═════╪═════════╪═════════════╪══════════╪══════════╡
7│ pear         ┆ 1   ┆ ear     ┆ ear         ┆ p        ┆ r        │
8│ mango        ┆ -1  ┆ o       ┆ ango        ┆ mang     ┆ ango     │
9│ dragonfruit  ┆ 4   ┆ onfruit ┆ ragonfruit  ┆ drag     ┆ ruit     │
10│ passionfruit ┆ -4  ┆ ruit    ┆ assionfruit ┆ passionf ┆ ionfruit │
11└──────────────┴─────┴─────────┴─────────────┴──────────┴──────────┘

API文档

Polars String API 文档