快速了解Rust 的数据分析库Polars

【图书介绍】《Rust编程与项目实战》-CSDN博客

《Rust编程与项目实战》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

17.1.1  什么是Polars

Polars是一个基于 Rust 的数据分析库,它的目标是提供一个高性能的数据分析工具,同时也提供了Python和JavaScript的接口,也就是说这款工具还可以供Python使用。Polars是一个用纯Rust开发的速度极快的DataFrame库,底层使用Apache Arrow内存模型。

数据科学家和数据分析师都对Pandas非常熟悉。对于数据科学领域的从业者来说,几乎无一例外地都会花费大量时间学习用Pandas处理数据。然而Pandas被诟病最多的是其运行速度和大数据集的处理效率。幸运的是,Polars的出现弥补了Pandas的不足。

Polars最核心的概念是表达式(Expressions),也是其拥有快速性能的核心。Polars提供了一个强大的表达式API。表达式API允许你创建和组合多种操作,例如过滤、排序、聚合、窗口函数等。表达式API也可以优化查询性能和内存使用。

17.1.2  Polars和Pandas对比

Polars与Pandas在许多方面具有截然不同的设计与实现。不像Pandas中每个DataFrame都有一个索引列(Pandas的很多操作也是基于索引的,例如join两个DataFrame进行联合查询),Polars并没有索引(Index)概念。主要区别如下:

(1)Polars使用Apache Arrow作为内部数据格式,而Pandas使用NumPy数组。

(2)Polars提供比Pandas更多的并发支持。

(3)Polars支持惰性查询并提供查询优化。

(4)Polars提供了与Pandas相似的API,以便于用户更快地上手。

简单地说,Polars相当于Rust的Pandas,且性能比Pandas要好很多。总体感觉,Polars就是奔着取代Pandas而生的。

17.1.3  为什么需要Polars

跟Pandas比,Polars有如下优势:

(1)Polars取消了DataFrame中的索引。消除索引让Polars更容易操作数据(Pandas中的DataFrame的索引很鸡肋)。

(2)Polars数据底层用Apache Arrow数组表示,而Pandas数据背后用NumPy数组表示。Apache Arrow在加载速度、内存占用和计算效率上都更加高效。

(3)Polars比Pandas支持更多并行操作。因为Polars是用Rust写的,所以可以无畏并发。

(4)Polars支持延迟计算(Lazy Evaluation),Polars会根据请求检验、优化数据以找到加速方法或降低内存占用。另一方面,Pandas仅支持立即计算(Eager Evaluation),即收到请求立即      求值。

Polars就是为了解决Pandas的性能而生的。在很多测试中,Polars比Pandas快2~3倍。Pandas与Polars的对比如表17-1所示。

17.1.4  安装Polars

由于Polars提供Python和JavaScript绑定,因此Polars支持多种语言环境安装。下面阐述针对各种语言的Polars安装。

(1)对于Rust,传统的Rust程序有Cargo进行包管理,只需要在cargo.toml的[dependencies]中加入:

polars = "0.25.1"

或者用cargo add命令即可:

$ cargo add polars

(2)对于Python环境,可以安装Polars的Python语言绑定PyPolars:

$ pip install polars

(3)对于Node环境,可以安装Polars的JavaScript语言绑定:

$ yarn add nodejs-polars

(4)数据科学家和算法工程师更喜欢用Jupyter,在Jupyter环境下需要用evcxr的:dep命令来引入包。在Jupyter中输入代码如下:

:dep polars = {version = "0.25.1"}

17.1.5   创建DataFrame

我们先来看一下如何手动创建DataFrame(数据帧)。

【例17.1】  手动创建DataFrame

打开VS Code,单击菜单Terminal→New Termanal,执行命令cargo new myrust来新建一个Rust工程,工程名是myrust。打开cargo.toml文件准备添加依赖软件包,在[dependencies]下添加如下内容:

polars = { version = "0.25.1", features = ["json"] }

准备添加代码。打开main.rs,添加代码如下:

use polars::prelude::*;  //引用Polars

fn main() {
    let df = df! [    //定义数据
        "Model" => ["iPhone XS", "iPhone 12", "iPhone 13", "iPhone 14", "Samsung S11", "Samsung S12", "Mi A1", "Mi A2"],
        "Company" => ["Apple", "Apple", "Apple", "Apple", "Samsung", "Samsung", "Xiao Mi", "Xiao Mi"],
        "Sales" => [80, 170, 130, 205, 400, 30, 14, 8],
        "Comment" => [None, None, Some("Sold Out"), Some("New Arrival"), None, Some("Sold Out"), None, None],
    ];

    println!("{:?}", &df);   //输出数据
}

Polars提供了df!宏来创建DataFrame。df!按列接受数据,每列含有列名和数据,数据以数组形式提供。这里需要注意,如果数据中存在空数据,则需要用None来表示,而Rust是强类型语言,需要列数据类型一致,因此,如果数据中有None存在,则其他非None数据需要用Some()包裹,达到类型一致。

DataFrame实现了std::fmt::Display方法,因此创建的对象可以直接利用println!宏输出。跟Pandas一样,在Jupyter Notebook中Polars DataFrame会以整齐美观的格式输出,并且还很贴心地将每列的数据类型展示出来,非常方便。

在TERMINAL窗口的命令行中输入运行命令cargo run,运行结果如图17-1所示。

图17‑1

这里需要注意,Polars DataFrame跟Pandas DataFrame有一点不同,Polars DataFrame的列名必须是字符串类型。如果列名不是字符串类型,运行时会报错。请看下面的代码:

let df2 = df! [
    0 => [Some(0), Some(1), Some(2)],
    1 => [Some("x"), Some("y"), Some("z")],
];
println!("{}", &df2);

上面的代码运行会报个错:mismatched types,如图17-2所示。

图17‑2

这是因为列名是i32类型,而不是str字符串类型。除显示列名外,Polars DataFrame还会在列名下面显示该列的数据类型。我们也可以调用dtypes()方法获取各列的数据类型:

df.dtypes()

运行上面的代码会看到下面的输出:

[Utf8, Utf8, Int32, Utf8]

也可以用get_column_names()方法获取所有列名:

df.get_column_names()

输出:

["Model", "Company", "Sales", "Comment"]

也可以通过get_row()方法传入行下标来获取一行数据:

df.get_row(0)

上面的代码会将第一行数据显示出来:

Row([Utf8("iPhone XS"), Utf8("Apple"), Int32(80), Null])

值得注意的是,与Pandas不同,Polars中没有行索引的概念。Polar的设计哲学认为DataFrame不需要行索引。

下面再看一个实例,读取JSON(JavaScript Object Notation,JS对象简谱)数据。JSON是一种轻量级的数据交换格式,它基于 ECMAScript(European Computer Manufacturers Association,欧洲计算机协会制定的JS规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言,易于阅读和编写,同时也易于机器解析和生成,并有效地提升了网络传输效率。JSON是Douglas Crockford在2001年开始推广使用的数据格式,在2005~2006年正式成为主流的数据格式,雅虎和谷歌就在那个时候开始广泛地使用JSON格式。

任何支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等,但是对象和数组是比较特殊且常用的两种类型。

  • 对象:对象在JS中是使用花括号“{}”包裹起来的内容,数据结构为{key1:value1, key2:value2, …}的键-值对结构。在面向对象的语言中,key为对象的属性,value 为对应的值。键名可以使用整数和字符串来表示。值可以是任意类型。
  • 数组:数组在JS中是方括号“[]”包裹起来的内容,数据结构为 ["java", "javascript", "vb", …] 的索引结构。在JS中,数组是一种比较特殊的数据类型,它也可以像对象那样使用键-值对,但还是索引使用得多。同样,值可以是任意类型。

【例17.2】  定义并加载JSON数据

打开VS Code,单击菜单Terminal→New Termanal,执行命令cargo new myrust来新建一个Rust工程,工程名是myrust。打开cargo.toml文件准备添加依赖软件包,在[dependencies]下添加如下内容:

polars = { version = "0.25.1", features = ["json"] }

准备添加代码。打开main.rs,添加代码如下:

use std::io::Cursor;
use polars::prelude::*;

fn main() {
    let data = r#"[					//定义JSON数据
        {"date": "1996-12-16T00:00:00.000", "open": 16.86, "close": 16.86, "high": 16.86, "low": 16.86, "volume": 62442.0, "turnover": 105277000.0},
        {"date": "1996-12-17T00:00:00.000", "open": 15.17, "close": 15.17, "high": 16.79, "low": 15.17, "volume": 463675.0, "turnover": 718902016.0},
        {"date": "1996-12-18T00:00:00.000", "open": 15.28, "close": 16.69, "high": 16.69, "low": 15.18, "volume": 445380.0, "turnover": 719400000.0},
        {"date": "1996-12-19T00:00:00.000", "open": 17.01, "close": 16.4, "high": 17.9, "low": 15.99, "volume": 572946.0, "turnover": 970124992.0}
    ]"#;

    let res = JsonReader::new(Cursor::new(data)).finish();
    println!("{:?}", res);  
    assert!(res.is_ok());
    let df = res.unwrap();
    println!("{:?}", df);				//输出结果
}

在TERMINAL窗口的命令行中输入运行命令cargo run,运行结果如图17-3所示。

图17‑3

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值