Rust 取代Python成为数据科学新宠?

琥珀里有波罗的海 数据如琥珀 2023-11-24 07:23



low profile,no profile























前言
有一段时间没有更新文章了,个中原因很多,但其中一个主要原因:Python数据科学已经被卷到极致了。
那些良莠不齐的教程如同洪水一样淹没了新人们。外行们都知道Python是人工智能,Python是大数据,Python是神。
我每天和所谓的大数据打打交道,但是无非就是写写Python脚本,调用Spark或者Databrcks 的workflow,建个模型或者pipeline,模型和结果保存在新一代data vault或者feature store。
但是每当运行一个Python job要花费好几分钟时,我就开始怀疑人生:Python到底是不是大数据的未来?Scala呢?Java呢?
于是我开始寻找新的未来,辗转了几个语言之后,我终于选中一个新秀:Rust。

图片

Rust 是一门高逼格的语言,为什么高逼格呢?因为国内很少有公司大规模使用该语言,国外使用该语言的又都是一些高大上的公司,比如Intel,Google,Meta,以及我喜欢的InfluxDB 等。
另外坊间传言,Rust的学习曲线异常陡峭,所以Rust就被供奉起来,形成了口碑两极化。
今天就通过一个数据科学案例,介绍一下Rust的一些特性,以及与Python相比的相似与相异。






实战案例

我用Rust 重写了之前的Pandas 处理时序异常的案例。

关于这个案例的Python版本,可以参考文章:

Pandas就能搞定时序异常检测,你学会了吗?(源码)

图片

首先我们看一下项目的架构。在 Rust 中,模块树用于组织代码并建立命名空间。
其中 crate就相当于Python的包,在我们这个包里面有两个子模块:plot和statistics。
plot 主要处理画图,这里采用的是Plotly Rust 包。statistics主要处理数据分析,采用的Polars Rust包。
看着这个项目架构和Python项目相差不大,无非就是关键词的区别。
  • Python用def 定义函数,Rust 用fn。

  • Python 文件的后缀是py,Rust文件的后缀是rs。

  • Python 用_init_来表明模块,Rust 用mod 来声明模块的路径。

这种差别就像是美式英语和英式英语的区别,就像是Fall和Autumn都是秋天。
但是如果你仔细观察,还是感觉有明显的区别:
  • Rust 的main 函数是程序的入口点, 所以每个项目都会带有Main函数。

  • Rust 必须像C语言一样编译成二进制文件,才能运行。但是,比较厉害的是,Rust可以在jupyter notebook里面运行,这就说明Rust 做数据科学指日可待。

  • Rust 对模块和函数的可见性是强制检测的。通过 `pub` 关键字,我们可以控制模块和函数的可见性。在Python语言中,一个类Class的所有对象都是公共的,如果你想让某些函数私有,可以命名为“_function_name”,但这是“君子协议”,不代表任何约束力。







主函数

图片

这段代码的目的是读取 CSV 数据,进行数据处理,然后生成散点图和异常值的可视化。

从这段 Rust 代码管中窥豹,我们可以了解Rust编程的一些特性:
  1. 静态类型Rust 是静态类型语言,变量的类型在编译时是需要确定的。例如, 可以显式或隐式指定 `data_file_path` 的类型为字符串引用。

  2. 所有权:Rust 引入了所有权系统,即每个值都有一个对应的所有者。这可通过 let 关键字将值绑定到变量,所有权的转移由这些 let 语句控制。

  3. 可变性:使用 `let mut df` 表示 `df` 是可变的,这是 Rust 中引入了可变性的概念。这符合 Rust 的借用和所有权系统,确保在运行时不存在数据竞争。借用和所有权是Rust在所有编程语言独树一帜的特性,也是让Rust新手最困惑的部分。

  4. 模块间引用:使用 `use statistics::IS_NORMALY_COL;` 引入了一个常量,显示了模块间的引用关系,以及 Rust 的模块系统如何支持可见性和封装性。

  5. 函数调用与借用Rust 函数的调用通常会涉及DataFrame所有权转移,所以使用 `clone()` 来创建数据的副本,相当于借用,而不是转移所有权。另外需要强调的是这里的clone 其实就是clone 指针,而不是真的deepclone。这是Polars包的一个设计优势。

  6. 末尾分号和花括号Rust 要求在语句的末尾加上分号,以表示语句的结束。这是 Rust 语法的一部分,与 Python 不同,这有助于显式地分隔语句。花括号表明一段生命周期,如果没有转移所有权,当程序运行到花括号外面时,所有的数据自动从内存清除。

  7. 错误处理在这段代码中,使用了 `unwrap()` 来获取 `Result` 类型的值。在实际生产代码中,可能需要更健壮的错误处理方式,例如使用 `match` 或 `?` 运算符。但是这里看到Rust 强调程序的健壮性,已经设计好很多帮你发现错误或者防呆的机制。

总体而言,这段 Rust 代码展示了Rust强调的一些核心概念,包括所有权系统、模块化、静态类型、可变性和明确的错误处理。
相比之下,Python 的动态类型、自动内存管理和更灵活的语法可能使得某些地方看起来更简洁,但在一些特定场景下可能牺牲了一些性能和静态安全性。    






Polars 数据分析

我们再来看一下数据处理和分析部分,这部分我们采用的Polars 包。我称之为包,是因为Python出身,专业点还是叫crate。

图片

Polars和Pandas都使用术语DataFrame,类似于数据库表格。但是Polars的DataFrame其实是源自Spark。如果你对Spark语言比较熟悉,那么就明显感觉Polars的函数名几乎是Spark的翻版。
Polars被设计为内存友好,尤其适用于大规模数据集的处理,主要原因是它使用 Arrow 的内存模型,可以有效地处理大型数据。相比之下,Pandas的内存模型可能在处理大数据时存在一些限制。当然我也知道Pandas V2 现在也支持Arrow了。
Rust和Polars的不可变特性从一开始就旨在提供更优化和更安全的并行计算。Polars强调不可变性,这在并发环境中更加安全。
总体而言,在编写Polars相关的Rust代码时,如果你对Pyspark很熟悉,写起来应该也算是非常简单的。

可能较大的差异是,Rust里面你要经常考虑Option和Result的unwrap。







Plotly 可视化

又到了我最爱的Plotly。没错,Rust也是支持Plotly的,而且体验感也不错。

对于我们之前写的Python代码,几乎可以拿来使用。

图片

和数据处理相同的注意事项是,我们需要不断的unwrap结果,以及需要确保数据格式是完全匹配的。

我们可以看到熟悉的Plotly画面了。

图片







总结


总的来说,Rust 和 Python 是两种完全不同的编程语言。日常工作中,我还是以Python为主,主要原因是目前的项目可重复性不高,经常需要快速试验新的方案,Python特别适合原型验证。
但是遇到想要提高性能的领域,我还是会尝试一下Rust,尤其是实时性要求较高的时序分析。
Rust 的生态系统逐渐发展,特别适用于系统编程、嵌入式开发等领域。它有一个活跃的社区和包管理工具Cargo,里面罗列了很多优秀的Crate。
但是大数据的未来会不会需要在Edge上进行计算呢?而在Edge上计算,谁又是最好的编程语言呢?

图片


当然现在Rust Crates中,和数据科学相关的还不算多,但是我知道有些朋友已经开始踊跃的把一些Python的包用Rust 重写了,包括Kaggle Book的作者。 
你要问我怎么看到Rust的未来,我只会给你讲一个故事:
约10年前,我刚从当当买了一本C++机器视觉,想精湛一下C++技术。回家时遇到了朋友,他神神叨叨的告诉我:你学啥C++,学蟒蛇啊。
我并没有放在心上。
大约5年前,我开始学习蟒蛇Python了。每每想起来,我觉得自己的确是那个只会低头走路,不愿抬头看方向的人。

所以,2023年末,如果你要问我学啥,我会说:学啥Python啊,学螃蟹啊。

图片





END

















近期精选文章

如果感兴趣,可以加工业数据挖掘交流群,请联系群主。
图片
或者你对知识星球更感兴趣,可以申请加入。














异常检测 · 目录
上一篇近实时物联网稳健异常检测框架

微信扫一扫
关注该公众号