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

如何在Polars DataFrame中填充结构体元素并访问它们

在Polars DataFrame/Series中使用自定义结构体的方案

你想直接在Polars里填充和使用自定义结构体,替代繁琐的JSON转换方法,同时需要了解如何取出存入的结构体,以下是具体实现方案:

前提说明

Polars的列类型基于自身的DataType体系,不支持直接存储任意Rust自定义结构体,但可以通过两种更直接的方式实现需求:


方案一:映射为Polars原生Struct类型(推荐)

将自定义结构体的字段拆解为Polars Struct列的子字段,完全适配Polars的向量化性能,是最优方案。

1. 给自定义类型实现转换逻辑

先让你的枚举和结构体能转换成Polars支持的类型:

use polars::prelude::*;

#[derive(Debug, Clone)]
enum FriendType {
    Goodfriend,
    Badfriend,
}

// 把FriendType转成字符串,方便存入Polars
impl From<FriendType> for String {
    fn from(ft: FriendType) -> Self {
        match ft {
            FriendType::Goodfriend => "Goodfriend".to_string(),
            FriendType::Badfriend => "Badfriend".to_string(),
        }
    }
}

#[derive(Debug, Clone)]
struct MyStruct {
    pub person: Option<String>,
    pub relation: FriendType,
}

impl MyStruct {
    fn new(person: &str, relation: FriendType) -> Self {
        MyStruct {
            person: Some(person.to_string()),
            relation,
        }
    }

    // 把MyStruct转换成Polars的StructValue
    fn to_struct_value(&self) -> StructValue {
        let person_field = self.person.as_ref().map(|s| s.as_str()).into();
        let relation_field: String = self.relation.clone().into();
        
        StructValue::new(vec![
            ("person", person_field),
            ("relation", relation_field.into()),
        ])
    }
}

2. 构建Struct列并加入DataFrame

将MyStruct实例转换为StructValue迭代器,再构建成Series加入DataFrame:

fn main() {
    // 创建结构体实例列表
    let struct_items = vec![
        MyStruct::new("Peter", FriendType::Badfriend),
        MyStruct::new("Mary", FriendType::Goodfriend),
        MyStruct::new("Elon", FriendType::Goodfriend),
        MyStruct::new("Joe", FriendType::Badfriend),
    ];

    // 转换为Polars StructValue集合
    let struct_values: Vec<_> = struct_items.iter().map(|s| s.to_struct_value()).collect();
    
    // 构建Struct类型的Series
    let struct_series = Series::new("MyStruct_col", struct_values);

    // 组装DataFrame
    let df = df![
        "Integer_col" => [1,2,3,4],
        "Float_col" => [1.1,0.3,9.6,4.2],
        "String_col" => ["apple", "pear", "lemon", "plum"],
        "values_nulls" => [Some(1), None, Some(3), Some(8)],
        "MyStruct_col" => struct_series,
    ].unwrap();

    println!("生成的DataFrame:\n{}", df);
}

3. 取出Struct列并还原为MyStruct

遍历Struct列,提取子字段后还原成原始结构体:

// 取出Struct列
let struct_col = df.column("MyStruct_col").unwrap();
let struct_chunked = struct_col.struct_().unwrap();

for struct_val in struct_chunked.iter() {
    if let Some(sv) = struct_val {
        // 提取子字段
        let person: Option<&str> = sv.field_by_name("person").unwrap().try_extract().unwrap();
        let relation_str: &str = sv.field_by_name("relation").unwrap().try_extract().unwrap();

        // 还原FriendType枚举
        let relation = match relation_str {
            "Goodfriend" => FriendType::Goodfriend,
            "Badfriend" => FriendType::Badfriend,
            _ => panic!("未知的关系类型"),
        };

        // 还原MyStruct
        let restored_struct = MyStruct {
            person: person.map(|s| s.to_string()),
            relation,
        };

        println!("还原后的结构体: {:?}", restored_struct);
    }
}

方案二:使用Polars Object类型(仅适合小数据量)

Polars支持Object类型存储任意实现Any trait的对象,但会失去向量化性能优势,不推荐大数据场景使用。

1. 给结构体实现必要Trait

use polars::prelude::*;
use std::any::Any;

#[derive(Debug, Clone)]
enum FriendType {
    Goodfriend,
    Badfriend,
}

#[derive(Debug, Clone)]
struct MyStruct {
    pub person: Option<String>,
    pub relation: FriendType,
}

impl MyStruct {
    fn new(person: &str, relation: FriendType) -> Self {
        MyStruct {
            person: Some(person.to_string()),
            relation,
        }
    }
}

// 实现Any trait
impl Any for MyStruct {
    fn type_id(&self) -> std::any::TypeId {
        std::any::TypeId::of::<Self>()
    }
}

// 实现Polars的ObjectSafe trait
unsafe impl ObjectSafe for MyStruct {}

2. 构建Object列并加入DataFrame

fn main() {
    let struct_items = vec![
        MyStruct::new("Peter", FriendType::Badfriend),
        MyStruct::new("Mary", FriendType::Goodfriend),
        MyStruct::new("Elon", FriendType::Goodfriend),
        MyStruct::new("Joe", FriendType::Badfriend),
    ];

    // 构建Object类型的Series
    let object_series = Series::new("MyStruct_col", struct_items);

    let df = df![
        "Integer_col" => [1,2,3,4],
        "Float_col" => [1.1,0.3,9.6,4.2],
        "String_col" => ["apple", "pear", "lemon", "plum"],
        "values_nulls" => [Some(1), None, Some(3), Some(8)],
        "MyStruct_col" => object_series,
    ].unwrap();
}

3. 取出Object列中的结构体

// 取出Object列
let object_col = df.column("MyStruct_col").unwrap();
let object_chunked = object_col.object::<MyStruct>().unwrap();

for item in object_chunked.iter() {
    if let Some(s) = item {
        println!("取出的结构体: {:?}", s);
    }
}

关键注意点

  • 方案一是首选,完全适配Polars的性能优化,支持所有Polars的向量化操作;
  • 方案二仅适合小数据场景,因为Object类型无法利用Polars的内部优化,查询效率低;
  • 自定义枚举必须转换为Polars支持的基础类型(如字符串、整数)才能存入Struct列,取出时再还原。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:35:23