字符串
字符串数据是处理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提供了replace
和replace_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_chars
、strip_chars_end
和 strip_chars_start
默认删除空格
切片
除了根据模式指定的方式提取子字符串外, 还可以按指定的偏移量对字符串进行切片, 从而生成子字符串
切片的通用函数是slice
, 它接受起始偏移量和切片的可选长度. 如果切片的长度未指定, 或者长度超过了字符串的末尾, Polars会将字符串一直切片到末尾
函数head
和tail
是分别用于切片字符串的开头和结尾的专用版本
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 文档