arrow 程序包提供了 reticulate 方法,用于在同一个进程中于 R 和 Python 之间传递数据。本文简要介绍了相关内容。
本文中的代码假设 arrow 和 reticulate 均已加载
library(arrow, warn.conflicts = FALSE)
library(reticulate, warn.conflicts = FALSE)动机
您可能希望在 R 中使用 PyArrow 的一个原因是利用那些在 Python 中比在 R 中得到更好支持的功能。例如,在某一时期,R 的 arrow 程序包不支持 concat_arrays(),但 PyArrow 支持,因此这在当时是一个很好的用例。在撰写本文时,PyArrow 对 Arrow Flight 的支持比 R 程序包更全面——但请参阅关于 arrow 中 Flight 支持的文章——因此对于 R 用户来说,这又是 PyArrow 的一个优势场景。
R 用户可能希望使用 PyArrow 的第二个原因是能够在 R 和 Python 之间高效地传递数据对象。对于大数据集,执行将 R 原生数据结构(例如数据框)转换为 Python 中的类似结构(例如 Pandas DataFrame)所需的复制和转换操作可能会非常昂贵——就时间和 CPU 周期而言。由于 Arrow 数据对象(如 Table)在 R 和 Python 中具有相同的内存格式,因此可以执行“零拷贝”数据传输,仅需在两种语言之间传递元数据。如下文所示,这极大地提高了性能。
安装 PyArrow
要在 Python 中使用 Arrow,需要安装 pyarrow 库。例如,您可能希望创建一个包含 pyarrow 库的 Python 虚拟环境。虚拟环境是专为某个项目或目的创建的特定 Python 安装。在 Python 中使用特定环境是一种良好的习惯,这样更新一个程序包就不会影响其他项目中的程序包。
您可以直接在 R 中执行设置。假设您想将虚拟环境命名为 my-pyarrow-env。您的设置代码如下所示
virtualenv_create("my-pyarrow-env")
install_pyarrow("my-pyarrow-env")如果您想在虚拟环境中安装 pyarrow 的开发版本,请在 install_pyarrow() 命令中添加 nightly = TRUE
install_pyarrow("my-pyarrow-env", nightly = TRUE)请注意,您不必非要使用虚拟环境。如果您更喜欢 conda 环境,可以使用以下设置代码
conda_create("my-pyarrow-env")
install_pyarrow("my-pyarrow-env")要了解关于从 R 安装和配置 Python 的更多信息,请参阅 reticulate 文档,其中对此主题有更详细的讨论。
导入 PyArrow
假设 arrow 和 reticulate 均已在 R 中加载,第一步是确保正在使用正确的 Python 环境。对于虚拟环境,请使用如下命令
use_virtualenv("my-pyarrow-env")对于 conda 环境,请使用以下命令
use_condaenv("my-pyarrow-env")完成此操作后,下一步是将 pyarrow 导入 Python 会话,如下所示
pa <- import("pyarrow")在 R 中执行此命令等同于在 Python 中执行以下导入
检查您的 pyarrow 版本也是一个好主意,如下所示
pa$`__version__`## [1] "8.0.0"
pyarrow 0.17 及更高版本包含了对与 R 进行数据传递的支持。
使用 PyArrow
您可以使用 reticulate 函数 r_to_py() 将对象从 R 传递到 Python,同样可以使用 py_to_r() 将对象从 Python 会话拉取到 R 中。为了说明这一点,让我们在 R 中创建两个对象:df_random 是一个包含 1 亿行随机数据的 R 数据框,tb_random 是存储为 Arrow Table 的相同数据
set.seed(1234)
nrows <- 10^8
df_random <- data.frame(
x = rnorm(nrows),
y = rnorm(nrows),
subset = sample(10, nrows, replace = TRUE)
)
tb_random <- arrow_table(df_random)在没有 Arrow 的情况下将数据从 R 传输到 Python 是一个耗时的过程,因为底层的对象必须被复制并转换为 Python 数据结构
system.time({
df_py <- r_to_py(df_random)
})## user system elapsed
## 0.307 5.172 5.529
相比之下,发送 Arrow Table 几乎是瞬间完成的
system.time({
tb_py <- r_to_py(tb_random)
})## user system elapsed
## 0.004 0.000 0.003
然而,“发送”并不是一个准确的词。在内部,我们是在运行于同一进程中的 R 和 Python 解释器之间传递数据指针,没有任何复制操作。没有东西被发送:我们只是在共享和访问相同的内部 Arrow 内存缓冲区。
也可以反向发送数据。例如,让我们在 pyarrow 中创建一个 Array。
a <- pa$array(c(1, 2, 3))
a## Array
## <double>
## [
## 1,
## 2,
## 3
## ]
请注意,a 现在是您 R 会话中的一个 Array 对象——即使它是在 Python 中创建的——并且您可以在其上应用 R 方法
a[a > 1]## Array
## <double>
## [
## 2,
## 3
## ]
同样,您可以将此对象与在 R 中创建的 Arrow 对象结合使用,并且可以使用像 pa$concat_arrays() 这样的 PyArrow 方法来实现
## Array
## <double>
## [
## 1,
## 2,
## 3,
## 5,
## 6,
## 7,
## 8,
## 9
## ]
现在您在 R 中拥有了一个单一的 Array。
进一步阅读
- 要了解关于从 R 安装和配置 Python 的更多信息,请参阅 reticulate 文档。
- 要学习 PyArrow,请参阅官方 PyArrow 文档 和 Apache Arrow Python 食谱 (Cookbook)。
- Arrow 中的 R/Python 集成也在 PyArrow 集成文档以及这篇关于 Arrow 中 reticulate 集成的博文中进行了讨论。
- R Arrow 和 PyArrow 之间的集成是通过 Arrow C 数据接口实现的。
- 要了解关于 Arrow 数据对象的更多信息,请参阅 数据对象文章。