字符串
以下部分讨论了对字符串数据执行的操作,字符串数据是使用数据帧时常用的数据类型。字符串处理函数可在 `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 crate `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` 命名空间中提供了五个函数,允许您从字符串两端剥离字符
函数 | 行为 |
---|---|
strip_chars |
删除指定字符的前导和尾随出现项。 |
strip_chars_end |
删除指定字符的尾随出现项。 |
strip_chars_start |
删除指定字符的前导出现项。 |
strip_prefix |
如果存在,删除精确的子字符串前缀。 |
strip_suffix |
如果存在,删除精确的子字符串后缀。 |
与 Python 字符串方法的相似性
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 文档。