R 和 Python 可以兼得吗?

本文最初发布于 towards data science 网站,经原作者授权由 InfoQ 中文站翻译并分享。

R 和 Python 有很多相似之处,也有很多不同之处。两种语言的数据结构的大多数基础概念都非常相似,并且现在这两种语言都有许多数据科学包可用。

但 R 的设计在我看来是“数据第一,应用第二”,而 Python 从一开始就给人感觉更多是应用程序开发驱动的。例如,纯粹从语法和环境管理的角度来看,Javascript 程序员上手 Python 的速度要比上手 R 的速度快一些。

最近,我在工作中用到 R 和 Python 的次数越来越多,并且遇到了想要同时使用两者的情况。出现这种情况的原因有很多,但最常见的一个场景是你正在用 R 构建某些东西,同时需要自己或其他人之前用 Python 编写的功能。当然,你可以用 R 重写它,但这不是很 DRY 吗?

R 中的 reticulate 这个包让你可以在 R 会话中执行 Python 代码。它实际上已经出来几年了,并且一直在不断改进,所以我想写一个关于它具体用法的简短教程。

如果你是 R 的开发者,要启动和运行 reticulate 需要你了解一些 Python 的运行机制—以及它管理环境的典型做法—所以本教程可以帮助你更快地完成设置,这样就不用你自己去费劲研究了。

R 和 Python 中的环境

任何编程项目都运行在一个环境中,它在这个环境里存储和访问自己在执行过程中需要或创建的所有东西。在 R 中,所有项目都可以使用一个通用的全局环境,在这个环境里可以访问 R 基础语言和所有已安装的包。从这个意义上说,R 中的所有项目通常都运行在相同的公共核心环境中。

换一种方式来看待这个景象,你可以想象你家中所有成员的 iPhone 都共享同一个充电站;他们必须离开自己的房间给手机充电;如果他们出售自己的 iPhone,买家需要自己解决充电问题。

但在 Python 中,每个项目通常都设置为完全自包含的—也就是说有自己的环境、自己的 Python 基础副本和它需要执行的所有模块的独立副本。你可以把这种景象想象成每个人在他们的房间里都有自己的 iPhone 充电器;他们不必走到外面找到统一的充电站来充电;如果他们出售手机,也会附上手机自己的充电器。

Python 的这种模式在安装过程和磁盘/内存资源消耗方面开销更大,但它能让开发者以最少的配置更轻松地在不同人之间转移项目。不难看出,它是直接从软件开发思维中发展出来的,这就是为什么我认为 Python 更像是“应用程序驱动的”。

这里我应该提一下,对于这两种语言的大多数日常用户来说,我所描述的是“典型的”场景。这些模式不是完全不变的,而且如果你知道该怎么做的话,在两种语言中都可以使用两种类型的项目流程。我们还看到 R 语言最近在朝着 Python 风格的环境管理模型迈进——例如 renv 包。

下面是我画的一个小草图,它以简单的方式展示 R 和 Python 中常见环境机制之间的区别。

R 和 Python 中的典型环境工作流

现在,如果你想让 Python 与 R 对话,前者仍然需要找到它自己的环境—你不能告诉它,让它去访问 R 的全局环境。这就像是让一个只会说英语的美国人去找一个只会说中文的中国人问路。

所以,要让 Python 在你的 R 项目中跑起来,你需要做两件事:

  1. 在 R 项目中设置一个 Python 环境,让 Python 可以认出自己的路。

  2. 用于翻译 Python 代码以使其在 R 中工作的 reticulate 包。

设置 Python 环境

从现在开始我会使用一个简单的例子来做说明。

假设我在 RStudio 中有一个 R 项目,它需要使用我用 Python 编写的函数。所以这里有一个简单的函数,我将它保存在我的 R 项目目录 test_python 中的一个名为 light_years.py 的 Python 脚本中(是的,RStudio 允许你创建 Python 脚本!)。

这个函数接收公里或英里为单位的距离作为输入,并计算以光速行进这段距离需要多少年。换句话说,以光年为单位的距离是多少。

from scipy.constants import cdef light_years(dist, unit = "km"):
    
    c_per_year = c * 60 * 60 * 24 * 365.25
    
    if unit == "km":
    
        dist_meters = dist * 1000
        
    elif unit == "mi":
      
        dist_meters = dist * 1.60934 * 1000
    
    else:
      
        sys.exit("Cannot use that unit!")
        
        
    return dist_meters/c_per_year

我在这里使用了一个非常简单的函数示例,以免让这篇文章太过冗长。所以它有点不切实际,也有点蠢,因为我导入整个 scipy 包只是为了获取一个常量的值,但希望它能帮助你领会我的意思。

现在正像我们上面所讨论的,我们需要为这段代码提供一个环境。它需要:

  1. 要使用的 Python 版本

  2. 访问 scipy 包,从而可以获得常数 c=光速

为你的 R 项目设置一个 Python 环境并不难。鉴于项目环境在 Python 中的重要性,市面上存在许多易用的环境管理工具。

我最喜欢的是 Anaconda。它有两个版本可用。完整版包含环境可能需要的所有一大堆东西,包括所有最常用的 Python 模块。然后是 Miniconda,它占用的磁盘空间少很多,更适合条件有限的 Python 用户。

你可以在此处https://docs.conda.io/en/latest/miniconda.html获取适用于你操作系统的 Miniconda。请为要使用的 Python 版本下载对应的 Conda。

安装完 Conda 后,如果你使用的是 macOS 或 Linux,通常会使用命令行来设置环境。只需转到终端中你的 R 项目目录(在我的例子中是 test_python)并使用以下命令:

conda create --name test_python

就这么简单,你现在已经创建了一个 python 环境。我通常将我的环境命名为与项目文件夹相同的名称,以免将来混淆。

现在你需要告诉 Conda,让它为这个项目使用这个环境。当你仍在命令行的 test_python 目录中时,使用以下命令:

conda activate test_python

现在你已将此项目链接到了 Python 环境,并且其中有 Python 基础的一个副本供你的代码运行使用。

最后,我们的函数需要 scipy 包,所以我们需要把它放在环境中。只需在激活的项目文件夹中输入以下内容即可:

conda install scipy

然后,Conda 会将 scipy 及它认为可能需要的所有依赖项安装到你的活动环境中,你就可以开始使用了——可以这么说,就像 scipy 一样简单。

稍后你需要告诉 R,在这个环境中在哪里可以找到 Python。用这条命令可以获得所有环境的列表以及安装环境的路径:

conda info --envs

例如,这能告诉我,我的环境安装在/Users/keithmcnulty/opt/miniconda3/envs/test_python。我总能在 bin 子目录中找到 Python 可执行文件——所以我的项目的 Python 可执行文件的完整路径是/Users/keithmcnulty/opt/miniconda3/envs/test_python/bin/python3,因为我使用的是 Python 3。我们需要告诉 R 的就是这些,这样它就知道在哪里可以找到 Python 环境了。

在 R 中运行你的 Python 函数

现在,无论你是像我一样用 Conda 设置了 Python 环境,还是使用了 virtualenv,你都已经完成了最艰巨的部分。剩下的操作很简单,因为 reticulate 会接手。

首先,当 R 加载项目时,你需要告诉 R,在正确的环境中在哪里可以找到 Python 可执行文件。为此,请启动一个空文本文件并添加以下内容,将我的路径替换为与你创建的项目环境中的 Python 可执行文件匹配的路径。

Sys.setenv(RETICULATE_PYTHON = "/Users/keithmcnulty/opt/miniconda3/envs/test_python/bin/python3")

现在将这个文本文件保存在你的项目目录中,名称为.Renv。这是一个隐藏文件,每当你在 RStudio 中启动项目时,R 都会执行该文件。所以现在关闭 RStudio 并在打开 test_python 项目的同时重新启动它,它现在将指向 Python 环境。

如果你还没有安装 reticulate R 包,你应该在这个时候安装。安装后,你可以在终端中尝试一些测试,看看是否一切正常。

首先,你可以测试 R 是否知道 Python 在哪里。reticulate::py_available()应该返回“TRUE”。你还可以测试项目是否安装了你需要的 Python 模块:reticulate::py_module_available(“scipy”)应返回“TRUE”。假设一切正常,你已准备好将你的函数引入 R 了。

你可以使用简单的一条命令来获取 Python 脚本:

reticulate::source_python("light_years.py")

现在你可以将 light_years()函数用作 R 函数。让我们看看以光速行驶一千万英里需要多少年:

> light_years(1000000000000000, "mi")
[1] 170.1074

很好!显然,这是一个非常简单的示例,但它确实告诉了你关于如何将 Python 代码集成到 R 脚本中的所有信息。你现在可以自由引入目前仅支持 Python 的各种功能或包,并让它们在 R 中工作,这非常令人兴奋。

如果你想查看一些高级示例,进一步了解如何使用 reticulate 将 Python 和 R 结合在一起使用,请查看我最近的几篇文章:

在同一项目中无缝切换R和Python的五种方法https://towardsdatascience.com/five-ways-to-work-seamlessly-between-r-and-python-in-the-same-project-bf173e35fdef

生成参数化Powerpoint文档https://towardsdatascience.com/generating-parameterized-powerpoint-documents-in-python-and-r-333368479038

在R中运行XGBoosthttps://towardsdatascience.com/how-to-run-python-ml-algorithms-easily-in-r-7e3b0f7c7aee

作者介绍

一开始我是一名纯数学家,然后我成为了心理咨询师和数据科学家。我很喜欢将所有这些学科的严谨思想应用在复杂的人性问题上。我也是一位编码极客,还是日本 RPG 的忠实粉丝。可以在LinkedInTwitter上找到我。还可以查看我在 drkeithmcnulty.com 上的博客或我即将发布的关于人类分析的教科书

原文链接:

https://towardsdatascience.com/why-choose-between-r-and-python-b12bf409d0d0

本文文字及图片出自 InfoQ

余下全文(1/3)
分享这篇文章:

请关注我们:

共有 1 条讨论

  1. admin  这篇文章, 并对这篇文章的反应是俺的神呀赞一个

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注