字符串

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

由于字符串的长度不可预测, 在其他DataFrame库中使用字符串可能会比较低效。 Polars通过遵循Arrow Columnar Format规范来缓解这些低效问题,因此也可以对字符串数据编写高性能的数据查询。Polars 中使用正则表达式,可能需要参考其语法文档来了解其支持哪些功能和标志。特别需要注意的是,Polars 支持的正则表达式风格与 Python 的re模块有所不同。

str命名空间

str.len_bytes str.len_chars

处理字符串的数据时,可能需要使用到命名空间 str, 该命名空间聚合了40多个可用于处理字符串的函数。下面代码片段展示了如何使用命名空间以及函数, 并展示了如何根据字节数和字符数计算列中字符串的长度:

Python
1df = pl.DataFrame(
2    {
3        "language": ["English", "Dutch", "Portuguese", "Finish", "chinese","misc"],
4        "fruit": ["pear", "peer", "pêra", "päärynä", "大鸭梨","akwif23!@#$%^&*"],
5    }
6)
7
8result = df.with_columns(
9    pl.col("fruit").str.len_bytes().alias("byte_count"),
10    pl.col("fruit").str.len_chars().alias("letter_count"),
11)
12print(result)
1shape: (6, 4)
2┌────────────┬─────────────────┬────────────┬──────────────┐
3│ language   ┆ fruit           ┆ byte_count ┆ letter_count │
4│ ---        ┆ ---             ┆ ---        ┆ ---          │
5│ str        ┆ str             ┆ u32        ┆ u32          │
6╞════════════╪═════════════════╪════════════╪══════════════╡
7│ English    ┆ pear            ┆ 4          ┆ 4            │
8│ Dutch      ┆ peer            ┆ 4          ┆ 4            │
9│ Portuguese ┆ pêra            ┆ 5          ┆ 4            │
10│ Finish     ┆ päärynä         ┆ 10         ┆ 7            │
11│ chinese    ┆ 大鸭梨          ┆ 9          ┆ 3            │
12│ misc       ┆ akwif23!@#$%^&* ┆ 15         ┆ 15           │
13└────────────┴─────────────────┴────────────┴──────────────┘
TIP

如果只处理ASCII文本, 那么字节数和字符数是一样的, 而且建议使用更快的str.len_bytes()

解析字符串

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

包含字符串-contains

str.contains

Expr.str.contains( pattern: str | Expr, *, literal: bool = False, strict: bool = True, ) → Expr

函数contains用来判断目标字符串中是否存在匹配字符串。默认情况下,参数会被当作正则表达式进行匹配。如果需要进行包含特殊符号的子字符串匹配时,则还需要添加另外一个参数literal,并且将其值设置为True

Python
1result = df.select(
2pl.col("fruit"),
3pl.col("fruit").str.starts_with("p").alias("starts_with_p"), # 以p开头
4pl.col("fruit").str.ends_with("r").alias("ends_with_r"), # 以r结尾
5pl.col("fruit").str.contains("p..r").alias("p..r"), # "p"和"r"之间可以有任意的两个字符
6pl.col("fruit").str.contains("e+").alias("e+"), # 至少出现一个"e"
7pl.col("fruit").str.contains("梨").alias("大鸭梨"), # 是否包含匹配字符串"梨"
8pl.col("fruit").str.contains("$").alias("special_char_1"),
9pl.col("fruit").str.contains("$", literal=True).alias("special_char_2") # 是否包含特殊字符$
10)
11print(result)
1shape: (6, 8)
2┌──────────────┬──────────────┬─────────────┬───────┬───────┬────────┬──────────────┬──────────────┐
3│ fruit        ┆ starts_with_ ┆ ends_with_r ┆ p..r  ┆ e+    ┆ 大鸭梨 ┆ special_char ┆ special_char │
4│ ---          ┆ p            ┆ ---         ┆ ---   ┆ ---   ┆ ---    ┆ _1           ┆ _2           │
5│ str          ┆ ---          ┆ bool        ┆ bool  ┆ bool  ┆ bool   ┆ ---          ┆ ---          │
6│              ┆ bool         ┆             ┆       ┆       ┆        ┆ bool         ┆ bool         │
7╞══════════════╪══════════════╪═════════════╪═══════╪═══════╪════════╪══════════════╪══════════════╡
8│ pear         ┆ true         ┆ true        ┆ true  ┆ true  ┆ false  ┆ true         ┆ false        │
9│ peer         ┆ true         ┆ true        ┆ true  ┆ true  ┆ false  ┆ true         ┆ false        │
10│ pêra         ┆ true         ┆ false       ┆ false ┆ false ┆ false  ┆ true         ┆ false        │
11│ päärynä      ┆ true         ┆ false       ┆ true  ┆ false ┆ false  ┆ true         ┆ false        │
12│ 大鸭梨       ┆ false        ┆ false       ┆ false ┆ false ┆ true   ┆ true         ┆ false        │
13│ akwif23!@#$% ┆ false        ┆ false       ┆ false ┆ false ┆ false  ┆ true         ┆ true         │
14│ ^&*          ┆              ┆             ┆       ┆       ┆        ┆              ┆              │
15└──────────────┴──────────────┴─────────────┴───────┴───────┴────────┴──────────────┴──────────────┘

提取字符串-extract

str.extract str.extract_all

Expr.str.extract(pattern: IntoExprColumn, group_index: int = 1) → Expr

str.extract() 提取正则表达式匹配到的字符串。其中第二个参数group_index表示要取的值的索引号,只能选1,0,选零表示返回整个表达式的匹配,选1返回第一个括号里的匹配。

Python
1df = pl.DataFrame(
2{
3    "urls": [
4    "http://vote.com/ballon_dor?candidate=messi&ref=polars&candidate=testdm",
5    "http://vote.com/ballon_dor?candidat=jorginho&ref=polars&candidate=testdm",
6    "http://vote.com/ballon_dor?candidate=ronaldo&ref=polars&candidate=testdm",
7    ]
8}
9)
10result = df.select(
11pl.col("urls").str.extract(r"candidate=(\w+)", group_index=0).alias("url_0"),
12pl.col("urls").str.extract(r"candidate=(\w+)", group_index=1).alias("url_1"),
13)
14print(result)
1shape: (3, 2)
2┌───────────────────┬─────────┐
3│ url_0             ┆ url_1   │
4│ ---               ┆ ---     │
5│ str               ┆ str     │
6╞═══════════════════╪═════════╡
7│ candidate=messi   ┆ messi   │
8│ candidate=testdm  ┆ testdm  │
9│ candidate=ronaldo ┆ ronaldo │
10└───────────────────┴─────────┘

如果要提取匹配字符串所有出现的位置,可以使用函数extract_all, 返回值是一个列表。

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

替换字符串-replace

str.replace str.replace_all

Expr.str.replace( pattern: str | Expr, value: str | Expr, *, literal: bool = False, n: int = 1, ) → Expr

与函数extractextract_all类似,Polars 也提供了函数replacereplace_all来实现字符串的替换。函数replace只会进行一次替换,而replace_all会替换所有匹配的字符串。

Python
1df = pl.DataFrame({"text": ["123abc", "abc456"]})
2result = df.with_columns(
3pl.col("text").str.replace(r"\d", "-"),
4pl.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│ -abc ┆ ---abc           │
8│ abc- ┆ abc---           │
9└──────┴──────────────────┘

修改字符串

大小写转换

str.to_lowercase str.to_titlecase str.to_uppercase

Polars通过函数 str.to_lowercase,str.to_titlecase(), str.to_uppercase来实现字符串的大小写转换。

  • str.to_titlecase():将单词首字母大写,其他字母小写。
  • str.to_lowercase():将字符串全部转换为小写。
  • str.to_uppercase():将字符串全部转换为大写。
Python
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(
13pl.col("addresses").alias("originals"),
14pl.col("addresses").str.to_titlecase(),
15pl.col("addresses").str.to_lowercase().alias("lower"),
16pl.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 如果以指定字符串结尾,则在尾部删除出现的该字符串
Python
1addresses = pl.DataFrame(
2    {
3        "addresses": [
4            " 128 PE 123 RF st",
5            "Rust bl 45,6 VD, 158 ",
6            " PoLaRs 56 Av, 12 ",
7            "1042 Query 223,12 sq",
8        ]
9    }
10)
11
12addr = pl.col("addresses")
13chars = ", 0123456789"
14result = addresses.select(
15    addr.str.strip_chars().alias("no_strip"),
16    addr.str.strip_chars_end().alias("no_end"),
17    addr.str.strip_chars_start().alias("no_start"),
18    addr.str.strip_chars(chars).alias("strip"),
19    addr.str.strip_chars_end(chars).alias("end"),
20    addr.str.strip_chars_start(chars).alias("start"),
21    addr.str.strip_prefix("128 ").alias("prefix"),
22    addr.str.strip_suffix(", 158").alias("suffix"),
23)
24print(result)
1shape: (4, 8)
2┌────────────┬────────────┬────────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
3│ no_strip   ┆ no_end     ┆ no_start   ┆ strip     ┆ end       ┆ start     ┆ prefix    ┆ suffix    │
4│ ---        ┆ ---        ┆ ---        ┆ ---       ┆ ---       ┆ ---       ┆ ---       ┆ ---       │
5│ str        ┆ str        ┆ str        ┆ str       ┆ str       ┆ str       ┆ str       ┆ str       │
6╞════════════╪════════════╪════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
7│ 128 PE 123 ┆ 128 PE 123 ┆ 128 PE 123 ┆ PE 123 RF ┆ 128 PE    ┆ PE 123 RF ┆ 128 PE    ┆ 128 PE    │
8│ RF st      ┆ RF st      ┆ RF st      ┆ st        ┆ 123 RF st ┆ st        ┆ 123 RF st ┆ 123 RF st │
9│ Rust bl    ┆ Rust bl    ┆ Rust bl    ┆ Rust bl   ┆ Rust bl   ┆ Rust bl   ┆ Rust bl   ┆ Rust bl   │
10│ 45,6 VD,   ┆ 45,6 VD,   ┆ 45,6 VD,   ┆ 45,6 VD   ┆ 45,6 VD   ┆ 45,6 VD,  ┆ 45,6 VD,  ┆ 45,6 VD,  │
11│ 158        ┆ 158        ┆ 158        ┆           ┆           ┆ 158       ┆ 158       ┆ 158       │
12│ PoLaRs 56  ┆ PoLaRs 56  ┆ PoLaRs 56  ┆ PoLaRs 56 ┆ PoLaRs 56 ┆ PoLaRs 56 ┆ PoLaRs 56 ┆ PoLaRs 56 │
13│ Av, 12     ┆ Av, 12     ┆ Av, 12     ┆ Av        ┆ Av        ┆ Av, 12    ┆ Av, 12    ┆ Av, 12    │
14│ 1042 Query ┆ 1042 Query ┆ 1042 Query ┆ Query     ┆ 1042      ┆ Query     ┆ 1042      ┆ 1042      │
15│ 223,12 sq  ┆ 223,12 sq  ┆ 223,12 sq  ┆ 223,12 sq ┆ Query     ┆ 223,12 sq ┆ Query     ┆ Query     │
16│            ┆            ┆            ┆           ┆ 223,12 sq ┆           ┆ 223,12 sq ┆ 223,12 sq │
17└────────────┴────────────┴────────────┴───────────┴───────────┴───────────┴───────────┴───────────┘

切片

str.slice str.head str.tail

除了通过extract根据匹配条件进行提取子字符串外,还可以使用slice函数指定偏移量获取子字符串。接受起始偏移量和切片的可选长度。如果切片的长度未指定,或者长度超过了字符串的末尾,Polars 会将字符串一直切片到末尾。针对于在字符串首尾进行操作时,专门提供了headtail函数。

TIP
  • Expr.str.slice( offset: int | IntoExprColumn, length: int | IntoExprColumn | None = None, ) → Expr:两个参数,第一个参数是起始位置,必须;第二个参数是切取长度,可选。如果切片的长度未指定,或者长度超过了字符串的末尾,Polars 会将字符串一直切片到末尾。如果第一个参数为负数(-n),则表示从字符串倒数第n个字符开始切片。
  • Expr.str.head(n: int | IntoExprColumn) → Expr:切取字符串前n个字符。
  • Expr.str.tail(n: int | IntoExprColumn) → Expr:切取字符串最后的n个字符。

理解head和tail:本质是通过一个区间获取字符串。 针对于head,相当于区间开始坐标固定就是字符串头,而区间结束坐标就是n的位置。如果n是正数,就从头往后数n个字符;如果是负数,就从尾往前数n个字符。 对于tail,相当于区间结束坐标固定就是字符串尾,而区间开始坐标就是n的位置。如果n是正数,就从尾往前数;如果是负数,就从头往后数

Python
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"),
10    pl.col("fruits").str.head(pl.col("n")).alias("head"),
11    pl.col("fruits").str.tail(pl.col("n")).alias("tail"),
12)
13print(result)
1shape: (4, 5)
2┌──────────────┬─────┬─────────┬──────────┬──────────┐
3│ fruits       ┆ n   ┆ slice   ┆ head     ┆ tail     │
4│ ---          ┆ --- ┆ ---     ┆ ---      ┆ ---      │
5│ str          ┆ i64 ┆ str     ┆ str      ┆ str      │
6╞══════════════╪═════╪═════════╪══════════╪══════════╡
7│ pear         ┆ 1   ┆ ear     ┆ p        ┆ r        │
8│ mango        ┆ -1  ┆ o       ┆ mang     ┆ ango     │
9│ dragonfruit  ┆ 4   ┆ onfruit ┆ drag     ┆ ruit     │
10│ passionfruit ┆ -4  ┆ ruit    ┆ passionf ┆ ionfruit │
11└──────────────┴─────┴─────────┴──────────┴──────────┘