字符串
以下部分讨论了对字符串数据执行的操作,这是在处理数据框时经常使用的数据类型。字符串处理函数在命名空间 str 中可用。
在其他数据框库中处理字符串可能非常低效,因为字符串的长度不可预测。Polars通过遵循Arrow列式格式规范来缓解这些低效问题,因此您也可以在字符串数据上编写高效的数据查询。
字符串命名空间
在处理字符串数据时,您可能需要访问命名空间 str,该命名空间汇集了40多个函数,让您可以处理字符串。作为如何从该命名空间访问函数的示例,下面的代码片段展示了如何计算列中字符串的字节数和字符数:
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)
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_with或ends_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 接受一个带有一个或多个捕获组的正则表达式模式,并提取指定为第二个参数的捕获组。
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)
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的结果输出是一个包含字符串中所有匹配模式实例的列表。
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)
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"] │
└────────────────┘
替换模式
类似于函数extract和extract_all,Polars提供了函数replace和replace_all。这些函数接受一个正则表达式模式或一个字面量子字符串(如果参数literal设置为True),并执行指定的替换。函数replace最多只会进行一次替换,而函数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_lowercase、to_titlecase 和 to_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 的字符串方法 removeprefix 和 removesuffix。
重要的是要理解,前三个函数将它们的字符串参数解释为一组字符,而函数strip_prefix和strip_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_chars、strip_chars_end 和 strip_chars_start 默认会移除空白字符。
切片
除了根据模式提取子字符串,您还可以在指定的偏移量处切片字符串以生成子字符串。用于切片的通用函数是slice,它接受起始偏移量和可选的切片长度。如果未指定切片的长度或长度超过字符串的末尾,Polars 会将字符串切片到末尾。
函数 head 和 tail 是专门用于分别切片字符串开头和结尾的版本。
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 文档。