3个不稳定版本
0.2.0 | 2024年8月13日 |
---|---|
0.1.0 | 2024年6月28日 |
0.1.0-beta.1 | 2024年6月25日 |
#862 in 数据库接口
每月151次下载
155KB
3.5K SLoC
pyo3-arrow
轻量级的Apache Arrow集成,用于pyo3。旨在使Rust库更容易添加互操作、零拷贝的Python绑定。
具体来说,pyo3-arrow使用arrow
crate实现了Python对象和Rust表示之间的零拷贝FFI转换。这依赖于Arrow PyCapsule接口,以实现Python Arrow生态系统中的无缝互操作性。
使用方法
我们只需用几行代码就可以包装一个在Python中使用的函数。
当您将定义在pyo3_arrow
中的结构体用作函数的参数时,它将通过零拷贝FFI自动将用户输入转换为Rust arrow
对象。然后,一旦完成,调用to_arro3
或to_pyarrow
将数据导回到Python。
use pyo3::prelude::*;
use pyo3_arrow::error::PyArrowResult;
use pyo3_arrow::PyArray;
/// Take elements by index from an Array, creating a new Array from those
/// indexes.
#[pyfunction]
pub fn take(py: Python, values: PyArray, indices: PyArray) -> PyArrowResult<PyObject> {
// We can call py.allow_threads to ensure the GIL is released during our
// operations
// This example just wraps `arrow_select::take::take`
let output_array =
py.allow_threads(|| arrow_select::take::take(values.as_ref(), indices.as_ref(), None))?;
// Construct a PyArray and export it to the arro3 Python Arrow
// implementation
Ok(PyArray::new(output_array, values.field().clone()).to_arro3(py)?)
}
然后在Python端,我们可以调用这个函数(通过arro3.compute.take
导出)
import pyarrow as pa
from arro3.compute import take
arr = pa.array([2, 3, 0, 1])
output = take(arr, arr)
output
# <arro3.core._rust.Array at 0x10787b510>
pa.array(output)
# <pyarrow.lib.Int64Array object at 0x10aa11000>
# [
# 0,
# 1,
# 2,
# 3
# ]
在这个例子中,我们使用pyarrow创建原始数组并查看结果,但不需要使用pyarrow。它确实展示了Arrow PyCapsule接口如何使这些Arrow对象在Python Arrow实现之间共享变得无缝。
使用Arrow数据作为输入
只需在您的函数签名中包含pyo3-arrow的一个结构体,用户输入将自动转换
这使用了Arrow PyCapsule接口。但请注意,它只定义了三个方法,而pyo3-arrow
包含更多的结构体。几个结构体是重载的,并使用相同的底层传输机制。
例如,PySchema
和PyField
都使用__arrow_c_schema__
机制,但行为不同。前者期望传输的字段是结构体类型,其子节点会被解包为模式中的字段,而后者没有约束,以原样传递字段。PySchema
如果传入的字段不是结构体类型,则会出错。
结构体名称 | 解包结构体字段 |
---|---|
PySchema |
是 |
PyField |
否 |
PyArray
和 PyRecordBatch
都使用了 __arrow_c_array__
机制。
结构体名称 | 将 StructArray 解包为 RecordBatch 。 |
---|---|
PyRecordBatch |
是 |
PyArray |
否 |
PyTable
、PyChunkedArray
和 PyRecordBatchReader
都使用了 __arrow_c_stream__
机制。
结构体名称 | 将 StructArray 解包为 RecordBatch 。 |
在内存中实例化。 |
---|---|---|
PyTable |
是 | 是 |
PyRecordBatchReader |
是 | 否 |
PyChunkedArray |
否 | 是 |
PyArrayReader |
否 | 否 |
将 Arrow 数据返回到 Python。
使用您自己的类
如果您正在将您自己的 Arrow 兼容类导出到 Python,您可以直接在您自己的类上实现相关的 Arrow PyCapsule 接口方法。
要导出流数据,请添加一个具有以下签名的类方法
use pyo3_arrow::ffi::to_stream_pycapsule;
fn __arrow_c_stream__<'py>(
&'py self,
py: Python<'py>,
requested_schema: Option<Bound<PyCapsule>>,
) -> PyResult<Bound<'py, PyCapsule>> {
// Construct a PyTable from your data
let table: PyTable = todo!();
table.__arrow_c_stream__(py, requested_schema)
}
导出模式或数组数据类似于,只是使用 __arrow_c_schema__
和 __arrow_c_array__
方法。
如果您不想导出您自己的类,请参考以下解决方案。
使用 arro3.core
arro3.core
是一个非常轻量级的 Python Arrow 实现,设计用于轻量级(<1MB)和相对稳定。相比之下,pyarrow 大约是 ~100MB。
您必须依赖于 arro3-core
Python 包;然后您可以使用每个导出的 Arrow 对象的 to_arro3
方法将数据传递到 arro3.core
类。
Rust 结构体 | arro3 类 |
---|---|
PyField |
arro3.core.字段 |
PySchema |
arro3.core.模式 |
PyArray |
arro3.core.数组 |
PyRecordBatch |
arro3.core.RecordBatch |
PyChunkedArray |
arro3.core.ChunkedArray |
PyTable |
arro3.core.Table |
PyRecordBatchReader |
arro3.core.RecordBatchReader |
使用 pyarrow
pyarrow
,标准的 Python Arrow 实现,是一个非常庞大的依赖项。它本身大约有 100MB 的大小,加上对 numpy 的强依赖性,大约还有 35MB。然而,numpy
很可能在用户环境中已经存在,pyarrow
也相当常见,因此要求 pyarrow
依赖项可能不是问题。
在这种情况下,您必须依赖于 pyarrow
,并且您可以使用 Python 结构体的 to_pyarrow
方法将数据返回到 Python。这需要 pyarrow>=14
(要返回 PyRecordBatchReader
,则需要 pyarrow>=15
)。
Rust 结构体 | pyarrow 类 |
---|---|
PyField |
pyarrow.字段 |
PySchema |
pyarrow.模式 |
PyArray |
pyarrow.数组 |
PyRecordBatch |
pyarrow.RecordBatch |
PyChunkedArray |
pyarrow.ChunkedArray |
PyTable |
pyarrow.Table |
PyRecordBatchReader |
pyarrow.RecordBatchReader |
使用 nanoarrow
nanoarrow
是一个用于处理 Arrow 数据的替代 Python 库。它的目标与 arro3 类似,但它是用 C 而不是 Rust 编写的。此外,它比 pyarrow
或 arro3
具有更小的类型系统,逻辑数组和记录批次都由 nanoarrow.Array
类表示。
在这种情况下,您必须依赖于 nanoarrow
,并且您可以使用 Python 结构体的 to_nanoarrow
方法将数据返回到 Python。
Rust 结构体 | nanoarrow 类 |
---|---|
PyField |
nanoarrow.模式 |
PySchema |
nanoarrow.模式 |
PyArray |
nanoarrow.数组 |
PyRecordBatch |
nanoarrow.数组 |
PyChunkedArray |
nanoarrow.ArrayStream |
PyTable |
nanoarrow.ArrayStream |
PyRecordBatchReader |
nanoarrow.ArrayStream |
为什么不使用 arrow-rs 的 Python 集成?
arrow-rs 有 一些现有的 Python 集成,但有几个原因促使我创建了 pyo3-arrow
- arrow-rs 只支持将数据返回给 pyarrow。Pyarrow 是一个非常大的依赖项(其未打包的 Linux 轮文件大小为 130MB,不包括对 Numpy 的依赖),有些项目可能不希望使用它。现在有了 Arrow PyCapsule 接口,可以采用模块化方法,其中一个非常小的库包含核心 Arrow 对象,并与其他库无缝协作。
- arrow-rs 的 Python FFI 集成不支持 Arrow 扩展类型,因为它在构建
Arc<dyn Array>
时省略了字段元数据。pyo3-arrow 通过在PyArray
结构体中存储一个ArrayRef
(《Arc<dyn Array>)和一个FieldRef
(《Arc<Field>)来解决这个问题。 - arrow-rs 无法处理不是记录批次的裸数组 Arrow 流,因此它无法与
pyarrow.ChunkedArray
或polars.Series
进行互操作。 - 在我看来,arrow-rs 与 pyo3 和 pyarrow 的联系过于紧密。pyo3 的发布版本与 arrow-rs 的发布周期不匹配,这意味着在使用 arrow-rs 时可能需要等待一段时间才能使用最新的 pyo3 版本,尤其是在 arrow-rs 等待较长时间发布破坏性更改的情况下。
依赖项
~18–26MB
~381K SLoC