You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不通过List直接构造Array Series?附Polars相关技术疑问

关于Polars中FixedSizeList构建及相关API的疑问

我目前通过以下方式实现创建固定长度数组列的需求:

let mut col1: ListPrimitiveChunkedBuilder<Float64Type> = ListPrimitiveChunkedBuilder::new(
    "array", 
    1,
    2, 
    DataType::Float64);

col1.append_slice(&[1.1,2.2]);
col1.append_slice(&[2.1,2.2]);
col1.append_slice(&[3.1,2.2]);
let s = col1.finish().into_series();
let sa = s.cast(&DataType::Array(Box::new(DataType::Float64), 2)).unwrap();
let df = DataFrame::new(vec![sa]);
println!("{:?}",df)

输出结果:

shape: (3, 1)
┌───────────────┐
│ array         │
│ ---           │
│ array[f64, 2] │
╞═══════════════╡
│ [1.1, 2.2]    │
│ [2.1, 2.2]    │
│ [3.1, 2.2]    │
└───────────────┘

但先创建List再转换为FixedSize Array的方式显得冗余。我在polars-core/src/chunked_array_builder/fixed_size_list.rs中找到了FixedSizeListNumericBuilder,但不知道如何使用。

另外还有两个附加问题:

  1. 在ListPrimitiveChunkedBuilder中,capacity和values_capacity的作用是什么?它们似乎没有约束力(即我可以指定更多的横向和纵向值)。
  2. 为什么必须用Box::new()包裹DataType::Float64?

解答

1. 直接使用FixedSizeListNumericBuilder构建固定长度数组列

不需要先创建List再转换,直接用FixedSizeListNumericBuilder可以一步到位生成FixedSize List类型的列,示例代码如下:

use polars::prelude::*;

fn main() {
    // 初始化构建器:参数依次为列名、每个数组的固定长度、预分配的行容量(可选,优化内存)
    let mut builder = FixedSizeListNumericBuilder::<Float64Type>::new(
        "array",
        2,  // 固定每个数组元素长度为2
        3,  // 预分配3行的空间,可根据实际情况调整
    );

    // 追加元素组,每组长度必须等于设定的固定长度
    builder.append_slice(&[1.1, 2.2]).unwrap();
    builder.append_slice(&[2.1, 2.2]).unwrap();
    builder.append_slice(&[3.1, 2.2]).unwrap();

    // 完成构建并转为Series,直接就是array[f64,2]类型
    let sa = builder.finish().into_series();
    let df = DataFrame::new(vec![sa]).unwrap();
    println!("{:?}", df);
}

执行后会直接得到和之前一致的DataFrame,省去了类型转换的步骤。

2. capacity和values_capacity的作用

这两个参数都是内存预分配优化参数,而非硬性限制:

  • capacity:预分配的行数量(即最终生成的List列有多少行),提前分配对应空间可以避免后续频繁扩容带来的性能开销。Polars的Builder支持动态扩容,所以即使你添加的行数超过这个值,它也会自动调整内存,不会报错。
  • values_capacity:预分配的所有List元素的总数量(比如3行×2个元素=6个总元素),同样是为了减少内存扩容次数。当实际元素总数超过这个值时,Builder会自动扩容,因此看起来没有约束力。

3. 为什么要用Box::new()包裹DataType

DataType是一个递归枚举类型,比如DataType::Array内部需要包含另一个DataType(元素类型),而这个内部的DataType又可能是Array、Struct等递归结构。

在Rust中,编译器需要在编译时确定每个类型的大小,如果枚举变体包含递归的自身类型,会导致大小无限递归无法确定。使用Box::new()将内部的DataType包装成一个指针,指针的大小是固定的(在64位系统是8字节),这样就能解决递归类型的大小计算问题,让DataType枚举可以正常定义和使用。


内容的提问来源于stack exchange,提问作者Dean MacGregor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:06:20