我的DataFrame中有一个Utf8
列,我想从该列创建一个List<Utf8>
列。
具体来说,对于每一行,我都将获取HTML文档的文本,并使用soup
解析出<p>
类的所有段落,并将每个单独段落的文本集合存储为Vec<String>
或Vec<&str>
。
fn parse_paragraph(s: &str) -> Vec<&str> {
let soup = Soup::new(s);
soup.tag(p).find_all().iter().map(|&p| p.text()).collect()
}
在尝试修改Rust polars中应用自定义函数的几个可用示例时,我似乎无法编译转换。
以这个MVP为例,使用一个更简单的字符串到字符串向量的例子,借用了文档中的迭代器例子:
use polars::prelude::*;
fn vector_split(text: &str) -> Vec<&str> {
text.split(' ').collect()
}
fn vector_split_series(s: &Series) -> PolarsResult<Series> {
let output : Series = s.utf8()
.expect("Text data")
.into_iter()
.map(|t| t.map(vector_split))
.collect();
Ok(output)
}
fn main() {
let df = df! [
"text" => ["a cat on the mat", "a bat on the hat", "a gnat on the rat"]
].unwrap();
df.clone().lazy()
.select([
col("text").apply(|s| vector_split_series(&s), GetOutput::default())
.alias("words")
])
.collect();
}
(Note:我知道utf8系列有一个内置的split
函数,但我需要一个比解析HTML更简单的示例)
我从cargo check
得到以下错误:
error[E0277]: a value of type `polars::prelude::Series` cannot be built from an iterator over elements of type `Option<Vec<&str>>`
--> src/main.rs:11:27
|
11 | let output : Series = s.utf8()
| ___________________________^
12 | | .expect("Text data")
13 | | .into_iter()
14 | | .map(|t| t.map(vector_split))
| |_____________________________________^ value of type `polars::prelude::Series` cannot be built from `std::iter::Iterator<Item=Option<Vec<&str>>>`
15 | .collect();
| ------- required by a bound introduced by this call
|
= help: the trait `FromIterator<Option<Vec<&str>>>` is not implemented for `polars::prelude::Series`
= help: the following other types implement trait `FromIterator<A>`:
<polars::prelude::Series as FromIterator<&'a bool>>
<polars::prelude::Series as FromIterator<&'a f32>>
<polars::prelude::Series as FromIterator<&'a f64>>
<polars::prelude::Series as FromIterator<&'a i32>>
<polars::prelude::Series as FromIterator<&'a i64>>
<polars::prelude::Series as FromIterator<&'a str>>
<polars::prelude::Series as FromIterator<&'a u32>>
<polars::prelude::Series as FromIterator<&'a u64>>
and 15 others
note: required by a bound in `std::iter::Iterator::collect`
这种过程的正确用法是什么?有没有更简单的方法来应用函数?
1条答案
按热度按时间j5fpnvbx1#
对于未来的研究者,我将解释一般的解决方案,然后解释使这个例子工作的具体代码,我还将指出这个具体例子的一些陷阱。
解释
如果需要使用自定义函数而不是方便的
Expr
表达式,在它的核心,你需要创建一个函数,将输入列的Series
转换成一个Series
,并由一个正确输出类型的ChunkedArray
支持,这个函数就是你在main
的select
语句中给map
的函数。ChunkedArray的类型是您提供的GetOutput
类型。问题中
vector_split_series
内部的代码适用于标准数值类型或数值类型列表的转换函数。它不会自动适用于Utf8
字符串的Lists
,例如,因为它们被特殊处理用于ChunkedArrays
。这是出于性能原因。您需要通过正确的类型构建器显式构建Series
。在这个问题中,我们需要使用一个
ListUtf8ChunkedBuilder
,它将创建一个List<Utf8>
的ChunkedArray
。所以一般来说,这个问题的代码适用于数值或数值列表的转换输出,但是对于字符串列表,需要使用
ListUtf8ChunkedBuilder
。正确代码
问题示例的正确代码如下所示:
核心在
vector_split_series
中,它具有在map
中使用的函数定义。match语句是必需的,因为
Series
可以有null条目,并且为了保持Series
的长度,你需要传递null。我们在这里使用构建器,以便它附加适当的null。对于非空条目,构建器需要追加
Series
。通常可以追加append_from_iter
,但是(从polars 0.26.1开始)没有针对Iterator<Item=Vec<T>>
的FromIterator实现。因此,需要将集合转换为值的迭代器,并将该迭代器转换为新的Series
。一旦构建了更大的
ChunkedArray
(类型为ListUtf8ChunkedArray
),就可以将其转换为PolarsResult<Series>
以返回到map
。抓住你了
在上面的例子中,
vector_split
可以返回Vec<String>
或Vec<&str>
,这是因为split
以一种很好的方式创建了&str
的迭代器。如果你使用的是更复杂的东西--就像我最初通过
Soup
查询提取文本的例子一样--如果它们输出&str
的迭代器,那么引用可能被认为是由临时变量拥有的,然后你就会遇到返回临时变量的引用的问题。这就是为什么在工作代码中,我将
Vec<String>
传递回构建器,尽管它不是严格要求的。