字符串
字符串数据是处理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
。
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└──────────────┴──────────────┴─────────────┴───────┴───────┴────────┴──────────────┴──────────────┘
str.extract
str.extract_all
Expr.str.extract(pattern: IntoExprColumn, group_index: int = 1) → Expr
str.extract()
提取正则表达式匹配到的字符串。其中第二个参数group_index
表示要取的值的索引号,只能选1,0,选零表示返回整个表达式的匹配,选1返回第一个括号里的匹配。
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
, 返回值是一个列表。
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
与函数extract
和extract_all
类似,Polars 也提供了函数replace
和replace_all
来实现字符串的替换。函数replace
只会进行一次替换,而replace_all
会替换所有匹配的字符串。
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():将字符串全部转换为大写。
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 会将字符串一直切片到末尾。针对于在字符串首尾进行操作时,专门提供了head
和tail
函数。
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└──────────────┴─────┴─────────┴──────────┴──────────┘