跳转至内容

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 支持的文章——因此这将是 PyArrow 对 R 用户有益的另一个例子。

R 用户可能希望使用 PyArrow 的第二个原因是在 R 和 Python 之间高效地传递数据对象。对于大型数据集,执行将 R 中的原生数据结构(例如,数据框)转换为 Python 中的类似结构(例如,Pandas DataFrame)所需的复制和转换操作,以及反之亦然,在时间和 CPU 周期方面成本可能相当高。由于 Arrow 数据对象(如表)在 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

假设在 R 中已加载 arrow 和 reticulate,则您的第一步是确保正在使用正确的 Python 环境。要使用虚拟环境执行此操作,请使用如下命令

use_virtualenv("my-pyarrow-env")

对于 conda 环境,请使用以下命令

use_condaenv("my-pyarrow-env")

完成此操作后,下一步是将 pyarrow 导入 Python 会话,如下所示

pa <- import("pyarrow")

在 R 中执行此命令等效于在 Python 中执行以下导入

import pyarrow as pa

检查您的 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 表的相同数据

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 表几乎是瞬间完成的

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 对象组合,并且您可以使用 PyArrow 方法(如 pa$concat_arrays())来执行此操作

b <- Array$create(c(5, 6, 7, 8, 9))
a_and_b <- pa$concat_arrays(list(a, b))
a_and_b
## Array
## <double>
## [
##   1,
##   2,
##   3,
##   5,
##   6,
##   7,
##   8,
##   9
## ]

现在您在 R 中有一个 Array。

延伸阅读