R 常见问题解答

关于 R 的常见问题解答

版本 2023-04-05

Kurt Hornik 和 R 核心团队


目录


1 导言

本文档包含关于 R 的一些最常见问题的解答。


1.1 法律声明

版权所有 © 1998–2020 Kurt Hornik
版权所有 © 2021–2023 R 核心团队

本文档是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款重新分发和/或修改它;版本 2 或(根据您的选择)任何更高版本。

本文档按“原样”提供,没有任何形式的保证;甚至没有对适销性或适合特定用途的隐含保证。有关更多详细信息,请参阅 GNU 通用公共许可证。

GNU 通用公共许可证版本的副本可在以下位置获得


1.2 获取本文档

本文档的最新版本始终可从以下位置获得

从那里,您可以获得转换为 ASCII 文本GNU infoHTMLPDF 以及 Texinfo 源代码 的版本,这些版本使用 GNU Texinfo 系统创建。

您也可以从 CRAN 站点的 doc/FAQ 子目录中获取 R FAQ(参见 什么是 CRAN?)。


1.3 引用此文档

在出版物中,请将此 FAQ 引用为 Hornik 和 R 核心团队 (2023),“R FAQ”,并给出上述,官方 URL

@Misc{,
  author        = {Kurt Hornik and the R Core Team},
  title         = {{R} {FAQ}},
  year          = {2023},
  url           = {https://CRAN.R-project.org/doc/FAQ/R-FAQ.html}
}

1.4 符号

所有内容都应该是相当标准的。‘R>’ 用于 R 提示符,而 ‘$’ 用于 shell 提示符(如果适用)。


1.5 反馈

欢迎通过电子邮件发送至 反馈。

R 的 Windows 和 macOS 端口的特定功能在 “R for Windows FAQ“R for Mac OS X FAQ 中描述。如果您有关于 Mac 或 Windows 系统的信息,您认为应该添加到此文档中,请告诉我们。


2 R 基础


2.1 什么是 R?

R 是一个用于统计计算和图形的系统。它包含一门语言,以及一个带有图形、调试器、访问某些系统功能以及运行存储在脚本文件中的程序的运行时环境。

R 的设计受到两种现有语言的很大影响:Becker、Chambers & Wilks 的 S(参见 什么是 S?)和 Sussman 的 Scheme。虽然生成的语言在外观上与 S 非常相似,但底层实现和语义源自 Scheme。有关更多详细信息,请参见 R 和 S 之间的区别是什么?

R 的核心是一门解释型计算机语言,它允许分支和循环,以及使用函数进行模块化编程。R 中大多数对用户可见的函数都是用 R 编写的。用户可以与用 C、C++ 或 FORTRAN 语言编写的过程进行交互以提高效率。R 发行版包含大量统计过程的功能。其中包括:线性模型和广义线性模型、非线性回归模型、时间序列分析、经典参数和非参数检验、聚类和平滑。还存在大量函数,它们提供灵活的图形环境,用于创建各种数据演示。针对各种特定目的提供了额外的模块(“附加包”)(参见 R 附加包)。

R 最初由 在新西兰奥克兰大学统计系编写。此外,一大群人通过发送代码和错误报告为 R 做出了贡献。

自 1997 年中期以来,一直有一个核心小组(“R 核心团队”)可以修改 R 源代码存档。该小组目前包括 Doug Bates、John Chambers、Peter Dalgaard、Robert Gentleman、Kurt Hornik、Ross Ihaka、Tomas Kalibera、Michael Lawrence、Friedrich Leisch、Uwe Ligges、Thomas Lumley、Martin Maechler、Sebastian Meyer、Paul Murrell、Martyn Plummer、Brian Ripley、Deepayan Sarkar、Duncan Temple Lang、Luke Tierney 和 Simon Urbanek。

R 的主页位于 https://www.R-project.org/。它是 自由软件,根据 GNU 风格的 版权许可 分发,并且是 GNU 项目(“GNU S”)的正式组成部分。


2.2 R 在哪些机器上运行?

R 正在为类 Unix、Windows 和 Mac 操作系统系列开发。对 Mac OS Classic 的支持在 R 1.7.1 中结束。

当前版本的 R 可以在许多常见的类 Unix(例如 https://en.wikipedia.org/wiki/Unix-like)平台上配置和构建,包括 cpu-linux-gnu 用于 i386、amd64/x86_64、alpha、arm、arm64、hppa、mips/mipsel、powerpc、s390x 和 sparc CPU(例如 https://buildd.debian.org/build.php?&pkg=r-base)、i386-hurd-gnu、cpu-kfreebsd-gnu 用于 i386 和 amd64、i386-pc-solaris、rs6000-ibm-aix、sparc-sun-solaris、x86_64-apple-darwin、aarch64-apple-darwin、x86_64-unknown-freebsd 和 x86_64-unknown-openbsd。

如果您了解其他平台,请给我们留言。


2.3 R 的当前版本是什么?

R 使用“主版本号.次版本号.修订号”的编号方案。基于此,有 R 的当前发布版本(“r-release”)以及两个开发版本,一个是当前发布版本的修补版本(“r-patched”),另一个是正在开发下一个次版本或最终主版本(“r-devel”)的 R 版本。新功能通常在 r-devel 中引入,而 r-patched 主要用于修复错误。

请参阅 https://CRAN.R-project.org/sources.html 以获取 r-release、r-patched 和 r-devel 的当前版本。


2.4 如何获取 R?

R 的源代码、二进制文件和文档可以通过 CRAN(“全面 R 档案网络”)获取(请参阅 什么是 CRAN)。

源代码也可以通过 https://svn.R-project.org/R/(R Subversion 仓库)获取,但目前无法通过匿名 rsync(或 CVS)获取。

R 的 r-devel 和 r-patched 开发版本的每日快照的 tarball 可以从 https://stat.ethz.ch/R/daily/ 获取。


2.5 如何安装 R?


2.5.1 如何安装 R (类 Unix)

如果 R 已经安装,可以通过在 shell 提示符下输入 R 来启动它(当然,前提是可执行文件在你的路径中)。

如果你的平台有二进制文件可用(参见 R 有类 Unix 的二进制文件吗?),你可以使用它们,并按照附带的说明进行操作。

否则,你可以自己编译和安装 R,这在许多常见的类 Unix 平台上很容易做到(参见 R 在哪些机器上运行?)。R 发行版附带的 INSTALL 文件包含简要介绍,而“R 安装和管理”指南(参见 R 有哪些文档?)则包含完整细节。

请注意,你需要一个 FORTRAN 90 编译器和一个 C 编译器来构建 R。

在最简单的情况下,解压缩 R 源代码,切换到创建的目录,并发出以下命令(在 shell 提示符下)

$ ./configure
$ make

如果这些命令执行成功,R 二进制文件和一个名为 R 的 shell 脚本前端将被创建并复制到 bin 目录。你可以将脚本复制到用户可以调用它的位置,例如 /usr/local/bin。此外,R 手册的 HTML 版本(例如,R-exts.html,“编写 R 扩展”手册)将在 doc/manual 子目录中构建。

使用 make pdf 构建 R 手册的 PDF(便携式文档格式)版本,包括 fullrefman.pdf(R 对象参考索引)。使用 GNU Texinfo 系统编写的用户手册也可以转换为适合使用 Emacs 或独立 GNU Info 在线阅读的 info 文件;使用 make info 创建这些版本(注意,这需要 Makeinfo 版本 4.5)。

最后,使用 make check 检查您的 R 系统是否正常工作。

您还可以使用 make install 执行“系统范围”安装。默认情况下,这将安装到以下目录

${prefix}/bin

前端 shell 脚本

${prefix}/man/man1

手册页

${prefix}/lib/R

所有其他内容(库、在线帮助系统等)。这是安装系统的“R 主目录”(R_HOME)。

在上述内容中,prefix 在配置期间确定(通常为 /usr/local),可以通过运行带有选项的 configure 来设置

$ ./configure --prefix=/where/you/want/R/to/go

(例如,R 可执行文件将安装到 /where/you/want/R/to/go/bin 中。)

要安装手册的 info 和 PDF 版本,请分别使用 make install-infomake install-pdf


2.5.2 如何在 Windows 上安装 R

CRAN 站点的 bin/windows 目录包含用于在 64 位版本的 Windows 7 及更高版本(x86_64 芯片)上运行的基本发行版和来自 CRAN 的附加包的二进制文件(R 4.1.3 是支持 32 位 Windows 的最后一个 R 版本)。Windows 版本的 R 由 Robert Gentleman 和 Guido Masarotto 创建;Brian Ripley 和 Duncan Murdoch 做出了重大贡献,现在由 R 核心团队的其他成员维护。

同一目录包含指向 R 的 r-patched 和 r-devel 版本快照的链接。

有关更多详细信息,请参阅 “Windows 版 R FAQ


2.5.3 如何在 Mac 上安装 R

CRAN 网站的 bin/macosx 目录中,包含一个标准的 Apple 安装程序包,可在 macOS 10.13(“High Sierra”)或更高版本上运行,另一个仅在 macOS 11(“Big Sur”)或更高版本下的“Apple Silicon” Mac 上运行。下载并执行后,安装程序将安装当前版本的 R 和 R.app,即 macOS 的 GUI。此 macOS 版 R 由 (以及之前的 Stefano Iacus)维护。“macOS 版 R FAQ 中有更多详细信息。

r-patched 和 r-devel 版本的 R 快照可作为 Apple 安装程序包从 https://mac.R-project.org 获取。


2.6 R 有 Unix 类二进制文件吗?

许多 Unix 类操作系统上都提供 R 的二进制发行版:这里只能列出一些,因此请检查您操作系统的搜索功能,看看是否有适合您的发行版。

CRAN 网站的 bin/linux 目录中,包含以下软件包。

CPU版本提供者
Debiani386/amd64squeeze/wheezyJohannes Ranke
armelwheezyJohannes Ranke
Ubuntui386/amd64lucid/precise/trustyMichael Rutter

由 Dirk Eddelbuettel 维护的 Debian 软件包长期以来一直是 Debian 发行版的一部分,可以通过 APT(Debian 软件包维护工具)访问。例如,使用 apt-get install r-base r-recommended 安装 R 环境和推荐的软件包。如果您还想从源代码构建 R 软件包,请运行 apt-get install r-base-dev 以获取构建所需的额外工具。Johannes Ranke 提供了至少针对 Debian 稳定 版本的当前 R 软件包的“反向移植”,可从 CRAN 获取。有关 R Debian 软件包和安装反向移植的详细信息,请参见 https://CRAN.R-project.org/bin/linux/debian/index.html,这些反向移植也应该适用于其他 Debian 派生版本。Michael Rutter 提供了适用于 Ubuntu 的原生反向移植。

由 Tom “Spot” Callaway 维护的 Fedora 的 R 二进制文件作为 Fedora 发行版的一部分提供,可以通过 yum(RPM 安装程序/更新程序)访问。请注意,“软件”应用程序(gnome-software),它是 Fedora 20 中软件安装的默认 GUI,无法用于安装 R。因此,建议使用 yum 命令行工具。Fedora R RPM 是一个“元包”,它安装了 R 的所有用户和开发人员组件(分别作为 R-coreR-devel 提供),以及 R-java,确保 R 配置为与 Java 一起使用。R RPM 还安装了独立的 R 数学库(libRmathlibRmath-devel),尽管这对于使用 R 来说不是必需的。当发布 R 的新版本时,Fedora RPM 可能需要长达 2 周的时间才能公开发布,因为它必须通过 Fedora 审查流程。Fedora 还提供了一些 R 包的 RPM。企业 Linux 额外软件包 (EPEL) 项目 (https://docs.fedoraproject.org/en-US/epel/) 为 RedHat Enterprise Linux 和兼容发行版(例如,Centos、Scientific Linux、Oracle Linux)提供了 Fedora RPM 的移植版本。

有关 openSUSE 的 RPM 信息,请参见 https://CRAN.R-project.org/bin/linux/suse/README.html

目前,CRAN 上没有其他公开可用的二进制发行版。


2.7 R 有哪些文档?

大多数 R 函数和变量的在线文档都存在,可以通过在 R 提示符下键入 help(name)(或 ?name)来在屏幕上打印,其中 name 是要查找帮助的主题的名称。(对于一元和二元运算符以及控制流特殊形式,名称可能需要用引号括起来。)

此文档还可以作为一本参考手册,用于在线阅读 HTML 和 PDF 格式,以及通过 LaTeX 获取纸质版,请参见 如何安装 R?。最新的 HTML 版本始终可供在 https://stat.ethz.ch/R-manual/ 上浏览网页。

R 发行版还附带以下手册。

  • “R 简介” (R-intro) 包含有关数据类型、编程元素、统计建模和图形的信息。本文档基于 Bill Venables 和 David Smith 的“S-PLUS 笔记”。
  • “编写 R 扩展” (R-exts) 目前描述了创建 R 附加包、编写 R 文档、R 的系统和外语接口以及 R API 的过程。
  • “R 数据导入/导出” (R-data) 是一个关于将数据导入和导出到 R 的指南。
  • “R 语言定义” (R-lang),是“R 的 Kernighan & Ritchie” 的第一个版本,解释了评估、解析、面向对象编程、在语言上进行计算等等。
  • “R 安装和管理” (R-admin)。
  • “R 内部结构” (R-ints) 是 R 内部结构的指南。(在 R 2.4.0 中添加。)

R 相关出版物的带注释的参考文献(BibTeX 格式)可以在以下位置找到

R 核心团队成员撰写的关于 R 的书籍包括

John M. Chambers (2008), “数据分析软件:使用 R 编程”。施普林格出版社,纽约,ISBN 978-0-387-75935-7,https://statweb.stanford.edu/~jmc4/Rbook/.

Peter Dalgaard (2008), “R 入门统计学”,第二版。施普林格出版社,ISBN 978-0-387-79053-4,http://publicifsv.sund.ku.dk/~pd/ISwR.html.

Robert Gentleman (2008), “生物信息学 R 编程”。Chapman & Hall/CRC,博卡拉顿,佛罗里达州,ISBN 978-1-420-06367-7,https://master.bioconductor.org/help/publications/books/r-programming-for-bioinformatics/.

Stefano M. Iacus (2008), “随机微分方程的模拟和推断:带 R 示例”。施普林格出版社,纽约,ISBN 978-0-387-75838-1。

Deepayan Sarkar (2007), “Lattice:使用 R 进行多变量数据可视化”。施普林格出版社,纽约,ISBN 978-0-387-75968-5。

W. John Braun 和 Duncan J. Murdoch (2007), “使用 R 进行统计编程入门”。剑桥大学出版社,剑桥,ISBN 978-0521872652。

P. Murrell (2005), “R 图形”,Chapman & Hall/CRC,ISBN: 1-584-88486-X,https://www.stat.auckland.ac.nz/~paul/RGraphics/rgraphics.html.

William N. Venables 和 Brian D. Ripley (2002), “现代应用统计学与 S”(第四版)。施普林格出版社,ISBN 0-387-95457-0,https://www.stats.ox.ac.uk/pub/MASS4/.

Jose C. Pinheiro 和 Douglas M. Bates (2000), “S 和 S-Plus 中的混合效应模型”。施普林格出版社,ISBN 0-387-98957-0。

最后但并非最不重要的一点是,Ross 和 Robert 在设计和实现 R 方面的经验在 Ihaka & Gentleman (1996) 的“R:用于数据分析和图形的语言”中有所描述,计算与图形统计杂志5, 299–314 (doi: 10.1080/10618600.1996.10474713).


2.8 引用 R

在出版物中引用 R 时,请使用

@Manual{,
  title        = {R: A Language and Environment for Statistical
                  Computing},
  author       = {{R Core Team}},
  organization = {R Foundation for Statistical Computing},
  address      = {Vienna, Austria},
  year         = YEAR,
  url          = {https://www.R-project.org}
}

其中 YEAR 是所用 R 版本的发布年份,可以通过 R.version$year 确定。

R 和 R 包的引用字符串(或 BibTeX 条目)也可以通过 citation() 获取。


2.9 R 有哪些邮件列表?

感谢 ,有几个专门针对 R 的邮件列表,包括以下内容

R-announce

一个关于 R 开发和新代码发布的主要公告的审核列表。

R-packages

一个关于新发布或增强贡献包的审核列表。

R-help

R 的“主要”邮件列表,用于讨论使用 R 遇到的问题和解决方案,包括使用标准 R 发行版和 CRAN 中的 R 包;公告(不包括在“R-announce”或“R-packages”中);R 的新功能的可用性和 R 的文档;以及发布不错的示例和基准测试。

R-devel

此列表用于关于 R 中代码开发的问题和讨论。

R-package-devel

一个为开发 R 包的人提供论坛的列表。

请在发送任何内容到任何邮件列表之前阅读发布指南

特别注意,R-help 旨在让那些想使用 R 解决问题但并非一定对编程感兴趣或了解编程的人能够理解。可能引发非程序员无法理解的讨论的问题(例如,涉及 C 或 C++ 的问题)应该发送到 R-devel。

通过 https://stat.ethz.ch/mailman/listinfo/ 的网络界面可以方便地访问这些列表的信息、订阅和存档。您也可以通过电子邮件订阅(或取消订阅),例如,通过将“subscribe”(或“unsubscribe”)发送到 的邮件正文(而不是主题!)中来订阅 R-help。

发送电子邮件到 以向 R-help 邮件列表中的所有人发送消息。其他列表的订阅和发布方式类似,将“R-help”分别替换为“R-announce”、“R-packages”和“R-devel”。请注意,R-announce 和 R-packages 列表已接入 R-help。因此,只有在您没有订阅 R-help 的情况下,您才应该订阅其中任何一个。

建议您将邮件发送到 R-help 邮件列表,而不是只发送给 R 核心开发人员(他们当然也订阅了该列表)。这可以为他们节省宝贵的时间,让他们可以用来不断改进 R,并且通常也会让您更快地获得反馈。

当然,在报告错误的情况下,提供能够可靠地重现问题的代码将非常有帮助。此外,请确保您包含有关所用系统和 R 版本的信息。有关更多详细信息,请参见 R 错误报告

有关 R 邮件列表的更多信息,请参见 https://www.R-project.org/mail.html


2.10 什么是 CRAN

“综合 R 档案网络” (CRAN) 是一个包含相同内容的站点集合,包括 R 发行版、贡献的扩展、R 文档和二进制文件。

位于奥地利维也纳经济大学的 CRAN 主站点可以在以下 URL 找到

https://CRAN.R-project.org/

并且每天都会镜像到世界各地的许多站点。有关镜像的完整列表,请参见 https://CRAN.R-project.org/mirrors.html。请使用离您最近的 CRAN 站点以减少网络负载。

CRAN,您可以获得 R 的最新官方版本、R 的每日快照(当前源代码树的副本)、压缩的 tar 文件和 bzip2 压缩的 tar 文件、大量额外的贡献代码,以及各种操作系统的预构建二进制文件(Linux、Mac OS Classic、macOS 和 MS Windows)。CRAN 还提供对 R 文档、现有邮件列表和 R 错误跟踪系统的访问。

自 2016 年 3 月起,“旧”资料从中央 CRAN 档案服务器 (https://CRAN-archive.R-project.org/) 提供。

在提及 CRAN 时,请始终使用主站点的 URL


2.11 我可以将 R 用于商业目的吗?

R 是在 GNU 通用公共许可证 (GPL),版本 2 或版本 3 下发布的。如果您对在任何特定情况下使用 R 的合法性有任何疑问,您应该与您的法律顾问联系。我们无权提供法律建议。

R 核心团队认为,人们可以将 R 用于商业目的(例如,在商业或咨询中)。GPL,与所有开源许可证一样,允许对该软件包进行任何使用。它只限制 R 或包含 R 代码的其他程序的发布。这在 开源定义 的第 6 条(“不歧视领域”)中明确说明。

许可证不得限制任何人将程序用于特定领域。例如,它可能不会限制程序在商业中使用,或用于基因研究。

GPL 的第 0 条也明确说明了这一点,其中部分内容如下:

除了复制、分发和修改之外的活动不受本许可证的约束;它们不在本许可证的范围内。运行程序的行为不受限制,程序的输出仅在内容构成基于程序的作品时才受约束。

大多数附加软件包,包括所有推荐的软件包,也明确允许以这种方式进行商业使用。少数软件包仅限于“非商业用途”;您应该联系作者以澄清这些软件包是否可以使用,或寻求您的法律顾问的建议。

本节中的任何讨论都不构成法律建议。R 核心团队在任何情况下都不会提供法律建议。


2.12 为什么 R 被命名为 R?

这个名字部分源于两位 R 作者(Robert Gentleman 和 Ross Ihaka)的(首)字母,部分是 Bell Labs 语言“S”的名称的谐音(参见 什么是 S?)。


2.13 什么是 R 基金会?

R 基金会是一个非营利组织,致力于公共利益。它由 R 核心团队成员创建,旨在为 R 项目和其他统计计算创新提供支持,为希望支持或与 R 开发社区互动的个人、机构或商业企业提供参考点,并持有和管理 R 软件和文档的版权。更多信息请访问 https://www.R-project.org/foundation/


2.14 什么是 R-Forge?

R-Forge (https://R-Forge.R-project.org/) 为 R 包、R 相关软件和其他项目的开发提供了一个中央平台。它基于 GForge,提供对 SVN、每日构建和检查的包、邮件列表、错误跟踪、留言板/论坛、网站托管、永久文件存档、完整备份和完全基于 Web 的管理的轻松访问。有关更多信息,请参阅 R-Forge 网站和 Stefan Theußl 和 Achim Zeileis (2009) 的文章“使用 R-Forge 进行协作软件开发”,R 杂志, 1(1):9–14。


3 R 和 S


3.1 什么是 S?

S 是一种非常高级的语言,也是用于数据分析和图形的环境。1998 年,美国计算机协会 (ACM) 将其软件系统奖授予 John M. Chambers,他是 S 的主要设计者,以表彰

S 系统,它永远改变了人们分析、可视化和操作数据的方式……

S 是一种优雅、广泛接受且经久不衰的软件系统,它具有概念完整性,这得益于 John Chambers 的洞察力、品味和努力。

S 语言的演变以 John Chambers 及其合著者撰写的四本书为特征,这些书也是 S 的主要参考资料。

  • Richard A. Becker 和 John M. Chambers (1984),“S. An Interactive Environment for Data Analysis and Graphics”,Monterey: Wadsworth and Brooks/Cole。

    这也被称为“棕色书”,仅具有历史意义。

  • Richard A. Becker、John M. Chambers 和 Allan R. Wilks (1988),“The New S Language”,伦敦:Chapman & Hall。

    这本书通常被称为“蓝色书”,并引入了现在称为 S 版本 2 的内容。

  • John M. Chambers 和 Trevor J. Hastie (1992),“Statistical Models in S”,伦敦:Chapman & Hall。

    这也被称为“白色书”,并引入了 S 版本 3,它添加了结构以促进 S 中的统计建模。

  • John M. Chambers (1998),“Programming with Data”,纽约:Springer,ISBN 0-387-98503-4 (https://statweb.stanford.edu/~jmc4/Sbook/).

    这本“绿色书”描述了 S 的版本 4,这是由 John Chambers 设计的 S 的重大修订,旨在提高其在编程过程各个阶段的实用性。

有关“S 语言的演变”的更多信息,请参见 https://statweb.stanford.edu/~jmc4/papers/96.7.ps


3.2 什么是 S-PLUS

S-PLUS 是 S 的增值版本,由 TIBCO Software Inc 作为“TIBCO Spotfire S+”出售。有关更多信息,请参见 https://en.wikipedia.org/wiki/S-PLUS


3.3 R 和 S 之间的区别是什么?

我们可以将 S 视为一种语言,它有三种当前的实现或“引擎”,“旧 S 引擎”(S 版本 3;S-PLUS 3.x 和 4.x),“新 S 引擎”(S 版本 4;S-PLUS 5.x 及更高版本)和 R。有了这种理解,询问“R 和 S 之间的区别”实际上等同于询问 S 语言的 R 实现的细节,即 R 和 S 引擎之间的区别。

在本节的剩余部分,“S”指的是 S 引擎,而不是 S 语言。


3.3.1 词法作用域

与 S 语言的其他实现不同,R 采用了一种评估模型,其中嵌套函数定义是词法作用域的。这类似于 Scheme 中的评估模型。

当函数中出现自由变量时,这种差异就会显现出来。自由变量是指既不是形式参数(出现在函数参数列表中)也不是局部变量(通过在函数体中赋值创建)的变量。在 S 中,自由变量的值由一组全局变量决定(类似于 C,只有局部作用域和全局作用域)。在 R 中,它们由创建函数的环境决定。

考虑以下函数

cube <- function(n) {
  sq <- function() n * n
  n * sq()
}

在 S 中,sq() 不“知道”变量 n,除非它在全局范围内定义

S> cube(2)
Error in sq():  Object "n" not found
Dumped
S> n <- 3
S> cube(2)
[1] 18

在 R 中,还会查看调用 cube() 时创建的“环境”

R> cube(2)
[1] 8

作为一个更“有趣”的现实世界问题,假设您想编写一个函数,该函数返回大小为 n 的样本中第 r 阶统计量的密度函数,该样本来自(连续)分布。为简单起见,我们将使用分布的 cdf 和 pdf 作为显式参数。(示例编译自 Luke Tierney 的各种帖子。)

对于 call()S-PLUS 文档基本上建议以下内容

dorder <- function(n, r, pfun, dfun) {
  f <- function(x) NULL
  con <- round(exp(lgamma(n + 1) - lgamma(r) - lgamma(n - r + 1)))
  PF <- call(substitute(pfun), as.name("x"))
  DF <- call(substitute(dfun), as.name("x"))
  f[[length(f)]] <-
    call("*", con,
         call("*", call("^", PF, r - 1),
              call("*", call("^", call("-", 1, PF), n - r),
                   DF)))
  f
}

相当棘手,不是吗?代码利用了 S 中函数只是具有特殊模式的列表,函数体作为最后一个参数的事实,因此在 R 中不起作用(虽然可以使这个想法起作用)。

一个在 S 和 R 中都似乎有效的版本,它大量使用了 substitute()

dorder <- function(n, r, pfun, dfun) {
  con <- round(exp(lgamma(n + 1) - lgamma(r) - lgamma(n - r + 1)))
  eval(substitute(function(x) K * PF(x)^a * (1 - PF(x))^b * DF(x),
                  list(PF = substitute(pfun), DF = substitute(dfun),
                       a = r - 1, b = n - r, K = con)))
}

(在 S 中不需要 eval())。

然而,在 R 中有一个更简单的解决方案。

dorder <- function(n, r, pfun, dfun) {
  con <- round(exp(lgamma(n + 1) - lgamma(r) - lgamma(n - r + 1)))
  function(x) {
    con * pfun(x)^(r - 1) * (1 - pfun(x))^(n - r) * dfun(x)
  }
}

这似乎是“自然”的实现,它之所以有效是因为返回函数中的自由变量可以在定义环境中查找(这是词法作用域)。

请注意,您真正需要的是函数的闭包,即函数体以及评估它所需的所有变量绑定。由于在上面的版本中,value 函数中的自由变量没有被修改,因此如果您将闭包操作抽象成一个函数MC()(代表“make closure”),您实际上也可以在 S 中使用它。

dorder <- function(n, r, pfun, dfun) {
  con <- round(exp(lgamma(n + 1) - lgamma(r) - lgamma(n - r + 1)))
  MC(function(x) {
       con * pfun(x)^(r - 1) * (1 - pfun(x))^(n - r) * dfun(x)
     },
     list(con = con, pfun = pfun, dfun = dfun, r = r, n = n))
}

给定闭包运算符的适当定义,这在 R 和 S 中都有效,并且比替换/评估解决方案(或通过使用显式访问评估框架来覆盖默认作用域规则的解决方案,这当然在 R 和 S 中都是可能的)要“干净”得多。

对于 R,MC() 只是

MC <- function(f, env) f

(词法作用域!),S 的版本是

MC <- function(f, env = NULL) {
  env <- as.list(env)
  if (mode(f) != "function")
    stop(paste("not a function:", f))
  if (length(env) > 0 && any(names(env) == ""))
    stop(paste("not all arguments are named:", env))
  fargs <- if(length(f) > 1) f[1:(length(f) - 1)] else NULL
  fargs <- c(fargs, env)
  if (any(duplicated(names(fargs))))
    stop(paste("duplicated arguments:", paste(names(fargs)),
         collapse = ", "))
  fbody <- f[length(f)]
  cf <- c(fargs, fbody)
  mode(cf) <- "function"
  return(cf)
}

类似地,大多数优化(或零查找)例程需要一些参数进行优化,并且具有其他取决于数据的参数,但这些参数在优化方面是固定的。使用 R 作用域规则,这是一个微不足道的问题;只需在同一个环境中使用所需的定义来创建函数,作用域就会处理它。对于 S,一种解决方案是向函数和优化器添加一个额外的参数,以传递这些额外的参数,但这只有在优化器支持这种情况下才能起作用。

嵌套的词法作用域函数允许使用函数闭包并维护局部状态。一个简单的例子(取自 Abelson 和 Sussman)可以通过在 R 提示符下键入demo("scoping") 来获得。更多信息请参阅标准 R 参考“R:数据分析和图形语言”(参见R 的文档有哪些?)以及 Robert Gentleman 和 Ross Ihaka (2000) 的“词法作用域和统计计算”,计算与图形统计杂志9, 491–508 (doi: 10.1080/10618600.2000.10474895).

嵌套的词法作用域函数也意味着另一个主要区别。S 将所有对象存储在某个目录中的单独文件中(通常在当前目录下的 .Data),而 R 则不会。R 中的所有对象都存储在内部。当 R 启动时,它会获取一块内存并使用它来存储对象。R 对这块内存进行自己的内存管理,根据需要增大和缩小其大小。将所有内容都存储在内存中是必要的,因为实际上不可能在外部维护所有相关的符号/值对“环境”。这种差异似乎也使 R 比 S 更快

缺点是,如果 R 崩溃,你将丢失当前会话的所有工作。保存和恢复内存“映像”(R 在任何时间点存储在内部内存中的函数和数据)可能有点慢,尤其是当它们很大时。在 S 中不会发生这种情况,因为所有内容都保存在磁盘文件中,如果你崩溃,它们不太可能发生任何事情。(事实上,有人可能会推测,S 开发人员认为改变他们的持久存储方法以适应词法作用域的代价太高了。)因此,在进行重要工作时,你可能需要考虑经常保存(参见 如何保存我的工作区?)以防万一发生崩溃。其他可能性是记录你的会话,或者将你的 R 命令存储在文本文件中,这些文件可以使用 source() 读取。

注意:如果你从 Emacs 中运行 R(参见 R 和 Emacs),你可以将交互缓冲区的内容保存到文件中,并使用 ess-transcript-mode 方便地操作它,以及保存所有使用过的函数和数据的源代码副本。


3.3.2 模型

在建模代码方面有一些区别,例如

  • 在 S 中,你将使用 lm(y ~ x^3)y 回归到 x^3 上,而在 R 中,你必须隔离数值向量的幂(使用 I()),即你必须使用 lm(y ~ I(x^3))
  • glm 族对象在 R 和 S 中的实现方式不同。它们的功能相同,但组件名称不同。
  • 在 R 中,选项 na.action 默认设置为 "na.omit",但在 S 中没有设置。
  • Terms 对象的存储方式不同。在 S 中,terms 对象是一个带有属性的表达式,而在 R 中,它是一个带有属性的公式。属性具有相同的名称,但存储方式大多不同。
  • 最后,在 R 中,y ~ x + 0y ~ x - 1 的替代方案,用于指定没有截距的模型。没有参数的模型可以通过 y ~ 0 指定。

3.3.3 其他

除了词法作用域及其影响之外,R 尽可能遵循蓝皮书和白皮书中的 S 语言定义,因此实际上是 S 的“实现”。在 S 的行为被认为“不干净”的地方,有一些有意为之的差异。一般来说,其基本原理是 R 应该帮助你检测编程错误,同时尽可能与 S 保持兼容。

以下是一些已知的差异。

  • 在 R 中,如果 x 是一个列表,那么 x[i] <- NULLx[[i]] <- NULL 会从 x 中删除指定的元素。第一个与 S 不兼容,在 S 中它是一个无操作。 (请注意,你可以使用 x[i] <- list(NULL) 将元素设置为 NULL。)
  • 在 S 中,.Data 目录中名为 .First.Last 的函数可用于自定义,因为它们分别在会话的开始和结束时执行。

    在 R 中,启动机制如下。除非在命令行中给出了 --no-environ,否则 R 会搜索站点和用户文件以处理设置环境变量。然后,除非给出了命令行选项 --no-site-file,否则 R 会搜索站点范围的启动配置文件。此代码加载到包 base 中。然后,除非给出了 --no-init-file,否则 R 会搜索用户配置文件,并将它源代码到用户工作区。然后,它会从 .RData 中加载用户工作区的保存映像(如果有)(除非指定了 --no-restore-data--no-restore)。接下来,如果在搜索路径上找到了函数 .First(),则会运行它。最后,运行 base 包中的函数 .First.sys。在终止 R 会话时,默认情况下,如果在搜索路径上找到了函数 .Last,则会运行它,然后运行 .Last.sys。如果需要,应该在相应的启动配置文件中定义函数 .First().Last()。有关更多详细信息,请参阅 .First.Last 的帮助页面。

  • 在 R 中,TF 只是设置为 TRUEFALSE 的变量,但它们不是像 S 中那样的保留字,因此可以被用户覆盖。(这在你有级别为 "T""F" 的因子时很有帮助。)因此,在编写代码时,你应该始终使用 TRUEFALSE
  • 在 R 中,dyn.load() 只能加载 共享对象,例如由 R CMD SHLIB 创建的。
  • 在 R 中,attach() 目前仅适用于列表和数据框,而不适用于目录。(实际上,attach() 也适用于使用 save() 创建的 R 数据文件,这类似于在 S 中附加目录。)此外,您不能在位置 1 附加。
  • R 中不存在类别,并且永远不会存在,因为它们现在在 S 中已弃用。请改用因子。
  • 在 R 中,For() 循环不是必需的,因此不支持。
  • 在 R 中,assign() 使用参数 envir= 而不是 where=,如 S 中所示。
  • 随机数生成器不同,种子长度也不同。
  • R 将整数对象传递给 C 作为 int * 而不是 long *,如 S 中所示。
  • R 没有单精度存储模式。但是,从版本 0.65.1 开始,有一个单精度接口用于 C/FORTRAN 子例程。
  • 默认情况下,ls() 分别返回当前(在 R 下)和全局(在 S 下)环境中对象的名称。例如,给定
    x <- 1; fun <- function() {y <- 1; ls()}
    

    然后 fun() 在 R 中返回 "y",在 S 中返回 "x"(以及全局环境的其余部分)。

  • R 允许零范围矩阵(和数组,即 dim 属性向量中的一些元素可以为 0)。这被认为是一个有用的功能,因为它有助于减少对空子集的特殊情况测试的需要。例如,如果 x 是一个矩阵,x[, FALSE] 不是 NULL,而是一个具有 0 列的“矩阵”。因此,需要通过检查其 length() 是否为零(这在 R 和 S 中都有效)来测试此类对象,而不是使用 is.null()
  • 命名向量在 R 中被视为向量,但在 S 中则不被视为向量(例如,is.vector(c(a = 1:3)) 在 S 中返回 FALSE,而在 R 中返回 TRUE)。
  • 数据框在 R 中不被视为矩阵(即,如果 DF 是一个数据框,则 is.matrix(DF) 在 R 中返回 FALSE,而在 S 中返回 TRUE)。
  • R 默认情况下在无序情况下使用处理对比,而 S 使用 Helmert 对比。这是一个有意为之的差异,反映了处理对比更自然的观点。
  • 在 R 中,替换函数的参数,对应于右侧,必须命名为 'value'。例如,f(a) <- b 被评估为 a <- "f<-"(a, value = b)。S 始终采用最后一个参数,无论其名称如何。
  • 在 S 中,substitute() 在三个地方搜索给定表达式中用于替换的名称:匹配调用的实际参数和默认参数,以及局部框架(按此顺序)。R 只在局部框架中查找,并使用特殊规则在变量未评估时使用“承诺”。由于局部框架使用实际参数或默认表达式初始化,因此这通常等同于 S,直到赋值发生。
  • 在 S 中,for() 循环中的索引变量在循环内部是局部的。在 R 中,它在执行 for() 语句的环境中是局部的。
  • 在 S 中,tapply(simplify=TRUE) 返回一个向量,而 R 返回一个一维数组(可以有命名的 dimnames)。
  • 在 S(-PLUS) 中使用 C 本地化,而在 R 中使用当前操作系统本地化来确定哪些字符是字母数字以及如何排序。这会影响 R 对象的有效名称集(例如,R 中可能允许使用重音字符)以及排序和比较中的排序(例如,"aA" < "Bb" 是真还是假)。从版本 1.2.0 开始,可以通过 Sys.setlocale() 函数在 R 中(重新)设置本地化。
  • 在 S 中,如果 arg 随后被修改,missing(arg) 仍然为 TRUE;在 R 中则不会。
  • 从 R 版本 1.3.0 开始,data.frame 在创建(列)名称时会剥离 I()
  • 在 R 中,字符串 "NA" 在字符变量中不被视为缺失值。使用 as.character(NA) 创建缺失字符值。
  • R 不允许在函数调用中重复形式参数。
  • 在 S 中,dump()dput()deparse() 本质上是同一代码的不同接口。在 R 版本 2.0.0 中,只有在使用相同的 control 参数时才为真,但默认情况下并非如此。默认情况下,dump() 尝试写入将评估为复制对象的代码,而 dput()deparse() 默认使用选项来生成可读的解析代码。
  • 在 R 中,使用 [ 使用字符向量索引索引向量、矩阵、数组或数据框时,只查找完全匹配(而 [[$ 允许部分匹配)。在 S 中,[ 允许部分匹配。
  • S 的 atan 函数有两个参数,没有 atan2 函数。在 S 中,atan(x1, x2) 的调用等效于 R 中的 atan2(x1, x2)。但是,要注意命名参数,因为 S 中的 atan(x = a, y = b) 等效于 R 中的 atan2(y = a, x = b),其中 xy 的含义互换。(R 曾经在文档中支持使用位置参数的两个参数 atan 函数,但为了避免进一步混淆,该功能已被移除。)
  • S-PLUS 6.x 或更高版本中,没有小数部分和指数部分(即只有整数部分)的数字常量被视为整数,但在 R 中被视为双精度浮点数。

还有一些差异并非有意,而是由于 R 中的代码缺失或错误导致。开发人员希望了解您可能发现的任何缺陷(请以书面报告的形式详细记录您所看到的差异)。当然,如果您能自己实现更改并确保其有效,将非常有用。


3.4 R 能做而 S-PLUS 不能做的事情?

由于您在 R 中可以做的几乎所有事情都有源代码,您可以轻松地将其移植到 S-PLUS 中,因此您在 R 中可以做的事情,只要您愿意,在 S-PLUS 中也可以做。(请注意,使用词法作用域可能会大大简化操作。)

R 提供了一些 S-PLUS 没有的图形功能,例如更精细的线型处理、更方便的颜色处理(通过调色板)、颜色伽马校正,最重要的是,通过类似 TeX 结构的输入表达式,在绘图文本中进行数学标注。请参阅 plotmath 的帮助页面,其中包含一个令人印象深刻的在线示例。更多详细信息可以在 Paul Murrell 和 Ross Ihaka (2000) 的文章 “An Approach to Providing Mathematical Annotation in Plots” 中找到,该文章发表在 Journal of Computational and Graphical Statistics, 9, 582–599 (doi: 10.1080/10618600.2000.10474900) 上。


3.5 什么是 R-plus?

很长一段时间以来,并没有这样的东西。

Revolution Analytics 发布了 REvolution R,现在可作为 Microsoft R(有关更多信息,请参阅 https://blog.revolutionanalytics.com/2016/01/microsoft-r-open.html)。

另请参阅 https://en.wikipedia.org/wiki/R_programming_language#Commercialized_versions_of_R,以获取指向 R 商业化版本的指针。


4 R Web 接口

请参阅 CRAN 任务视图中的“Web 技术和服务”(https://CRAN.R-project.org/view=WebTechnologies),特别是“Web 和服务器框架”部分,以获取有关 R Web 接口包的最新信息。

有关 R Web 接口的早期参考资料包括 Jeff Banfield(1999),“Rweb:基于 Web 的统计分析”(doi: 10.18637/jss.v004.i01),David Firth(2003),“CGIwithR:使用 R 处理 Web 表单的功能”(doi: 10.18637/jss.v008.i10),以及 Angelo Mineo 和 Alfredo Pontillo(2006),“通过 PHP 使用 R 进行教学:R-php”(doi: 10.18637/jss.v017.i04)。


5 R 附加包


5.1 R 有哪些附加包?


5.1.1 R 中的附加包

R 发行版附带以下包

base

基本 R 函数(以及 R 2.0.0 之前的 数据集)。

compiler

R 字节码编译器(在 R 2.13.0 中添加)。

datasets

基本 R 数据集(在 R 2.0.0 中添加)。

grDevices

基本和网格图形的图形设备(在 R 2.0.0 中添加)。

graphics

R 基本图形函数。

grid

图形布局功能的重写,以及对交互的一些支持。

methods

R 对象的正式定义方法和类,以及其他编程工具,如绿皮书中所述。

parallel

支持并行计算,包括通过分叉和套接字,以及随机数生成(在 R 2.14.0 中添加)。

splines

回归样条函数和类。

stats

R 统计函数。

stats4

使用 S4 类的统计函数。

tcltk

与 Tcl/Tk GUI 元素的接口和语言绑定。

tools

用于包开发和管理的工具。

utils

R 实用程序函数。


5.1.2 来自 CRAN 的附加包

CRANsrc/contrib 区域包含大量附加包,包括以下 推荐 的包,这些包将包含在所有 R 的二进制发行版中。

KernSmooth

用于核平滑(和密度估计)的函数,对应于 M. P. Wand 和 M. C. Jones 1995 年的书籍“核平滑”。

MASS

来自 Venables 和 Ripley 的主要软件包“使用 S 的现代应用统计学”的函数和数据集。

Matrix

对稀疏和密集矩阵的支持

boot

来自 A. C. Davison 和 D. V. Hinkley 1997 年剑桥大学出版社的书籍“Bootstrap 方法及其应用”的引导函数和数据集。

class

用于分类(k 最近邻和 LVQ)的函数。

cluster

用于聚类分析的函数。

codetools

代码分析工具。

foreign

用于读取和写入由 Minitab、S、SAS、SPSS、Stata、Systat 等统计软件存储的数据的函数。

lattice

格子图形,Trellis 图形函数的实现。

mgcv

用于 GAM 和其他广义岭回归问题的例程,通过 GCV 或 UBRE 进行多个平滑参数选择。

nlme

拟合和比较高斯线性与非线性混合效应模型。

nnet

用于单隐层感知器(“前馈神经网络”)和多项式对数线性模型的软件。

rpart

递归 PARTitioning 和回归树。

spatial

来自 W. Venables 和 B. Ripley 的“使用 S 的现代应用统计学”的克里格和点模式分析函数。

survival

用于生存分析的函数,包括惩罚似然。

有关更多信息,请参阅 CRAN 贡献包页面

这些包中的许多被归类为 CRAN 任务视图,允许按主题浏览包,并提供工具来自动安装特定领域的所有包。


5.1.3 来自 Bioconductor 的附加包

Bioconductor 是一个用于分析和理解基因组数据的开源和开放开发软件项目。大多数 Bioconductor 组件以 R 附加包的形式分发。最初,大多数 Bioconductor 软件包 主要集中在 DNA 微阵列数据分析。随着项目的成熟,软件包的功能范围扩展到包括所有类型基因组数据的分析,例如 SAGE、序列或 SNP 数据。此外,还有元数据(注释、CDF 和探针)和实验数据包。请参阅 https://master.bioconductor.org/install/ 以获取可用包和通过 BioC Views 的完整分类。


5.1.4 其他附加包

许多其他包可从除上面讨论的默认存储库(CRAN 和 Bioconductor)以外的地方获得。特别是,R-Forge 在 https://R-Forge.R-project.org/ 提供了一个 CRAN 风格的存储库。

更多代码已发布到 R-help 邮件列表,可以从邮件列表存档中获取。


5.2 如何安装附加包?

(仅限类 Unix 系统。)CRAN 上的附加包以名为 pkg_version.tar.gz 的 gzip 压缩 tar 文件形式提供。设 path 为此类包文件的路径。如果您的系统上提供了 targzip,请在 shell 提示符下键入

$ R CMD INSTALL path/pkg_version.tar.gz

以将安装到根目录位于您的库搜索路径中的第一个目录的库树中(有关如何确定搜索路径的详细信息,请参阅 .libPaths() 的帮助页面)。

要安装到另一个树(例如,您的私有树),请使用

$ R CMD INSTALL -l lib path/pkg_version.tar.gz

其中 lib 指定要安装的库树的路径。

更方便的是,如果您有权访问诸如 CRAN 之类的存储库,则可以在 R 中安装和自动更新软件包。有关更多信息,请参见 available.packages() 的帮助页面。


5.3 如何使用附加软件包?

要找出系统上有哪些其他软件包可用,请键入

library()

在 R 提示符下。

这将产生类似于以下内容的结果

Packages in ‘/home/me/lib/R’:

mystuff       My own R functions, nicely packaged but not documented

Packages in ‘/usr/local/lib/R/library’:

KernSmooth    Functions for kernel smoothing for Wand & Jones (1995)
MASS          Support Functions and Datasets for Venables and Ripley's MASS
Matrix        Sparse and Dense Matrix Classes and Methods
base          The R Base package
boot          Bootstrap R (S-Plus) Functions (Canty)
boot          Bootstrap Functions (Originally by Angelo Canty for S)
class         Functions for Classification
cluster       "Finding Groups in Data": Cluster Analysis Extended
              Rousseeuw et al.
codetools     Code Analysis Tools for R
datasets      The R Datasets Package
foreign       Read Data Stored by Minitab, S, SAS, SPSS, Stata, Systat,
              dBase, ...
grDevices     The R Graphics Devices and Support for Colours and Fonts
graphics      The R Graphics Package
grid          The Grid Graphics Package
lattice       Lattice Graphics
methods       Formal Methods and Classes
mgcv          GAMs with GCV/AIC/REML smoothness estimation and GAMMs
              by PQL
nlme          Linear and Nonlinear Mixed Effects Models
nnet          Feed-forward Neural Networks and Multinomial Log-Linear
              Models
rpart         Recursive Partitioning
spatial       Functions for Kriging and Point Pattern Analysis
splines       Regression Spline Functions and Classes
stats         The R Stats Package
stats4        Statistical functions using S4 Classes
survival      Survival analysis, including penalised likelihood
tcltk         Tcl/Tk Interface
tools         Tools for Package Development
utils         The R Utils Package

您可以通过以下方式“加载”已安装的软件包 pkg

library(pkg)

然后,您可以通过键入以下任一内容来找出它提供的函数

library(help = pkg)
help(package = pkg)

您可以通过以下方式卸载已加载的软件包 pkg

detach("package:pkg", unload = TRUE)

(其中 unload = TRUE 仅在具有命名空间的软件包中需要,请参见 ?unload)。


5.4 如何删除附加软件包?

使用

$ R CMD REMOVE pkg_1 ... pkg_n

R_LIBS 中给出的第一个目录为根的库树中删除软件包 pkg_1、…、pkg_n,如果此目录已设置且非空,否则从默认库中删除。

要从库 lib 中删除,请执行以下操作

$ R CMD REMOVE -l lib pkg_1 ... pkg_n

5.5 如何创建 R 软件包?

软件包由一个子目录组成,该子目录包含一个文件 DESCRIPTION 和子目录 Rdatademoexecinstmanposrctests(其中一些可能不存在)。软件包子目录还可以包含文件 INDEXNAMESPACEconfigurecleanupLICENSELICENCECOPYINGNEWS

有关详细信息,请参见 Writing R Extensions 中的 创建 R 软件包

R 版本 1.3.0 添加了函数 package.skeleton(),它将为一组 R 函数和数据集设置目录、保存数据和代码,以及创建基本帮助文件。

有关将包上传到 CRAN 的信息,请参阅 什么是 CRAN


5.6 如何为 R 做贡献?

R 处于积极开发中,始终存在错误潜入的风险。此外,开发人员无法访问所有可能运行 R 的机器。因此,仅仅使用它并传达问题无疑具有很大的价值。

R 开发者页面 充当 R 统计系统中或多或少最终确定想法和计划的中间存储库。它包含(指向)待办事项列表、RFC、各种其他文档、想法列表和 SVN 杂项。


6 R 和 Emacs


6.1 Emacs 是否支持 R?

有一个名为 ESS(“Emacs Speaks Statistics”)的 Emacs 包,它提供了一个统计程序和统计过程之间的标准接口。它旨在为交互式统计编程和数据分析提供帮助。支持的语言包括:S 方言(R、S 3/4 和 S-PLUS 3.x/4.x/5.x/6.x/7.x)、LispStat 方言(XLispStat、ViSta)、SAS、Stata 和 BUGS。

ESS 诞生于对 S-mode 4.8 进行错误修复和扩展的需要(它是一个仅针对 S/S-PLUS 版本 3 的 GNU Emacs 接口)。当前的开发人员希望支持 XEmacs、R、S4 和 MS Windows。此外,随着针对 R、Stata 和 SAS 的新模式的开发,人们认为,一个统一的用户界面和框架将有利于用户和开发人员,帮助两组人员都符合标准的 Emacs 使用方式。最终的结果是,与通常的工具相比,统计编程和数据分析的效率得到了提高。

R 支持包含用于编辑 R 源代码的代码(源代码的语法缩进和高亮显示、代码的局部评估、代码的加载和错误检查以及源代码修订维护)和文档(源代码的语法缩进和高亮显示、将示例发送到运行的 ESS 进程以及预览)、从 Emacs 内部与下级 R 进程交互(命令行编辑、可搜索的命令历史记录、R 对象和文件名命令行完成、快速访问对象和搜索列表、转录记录以及与帮助系统的接口)以及转录操作(记录和保存转录文件、操作和编辑保存的转录以及重新评估转录文件中的命令)。

ESS 的最新稳定版本可通过 CRANESS 网页 获得。

ESS 附带详细的安装说明。

如需获取有关 ESS 的帮助,请发送电子邮件至

请将有关 ESS 的错误报告和建议发送至 。最简单的方法是在 Emacs 中输入 M-x ess-submit-bug-report 或使用 [ESS] 或 [iESS] 下拉菜单。


6.2 我应该在 Emacs 中运行 R 吗?

是的,而不是仅仅在控制台中运行它,绝对。作为 Rstudio 等其他 IDE 的替代方案,可能,特别是如果您有兴趣使用 Emacs 进行其他计算机交互。您将使用 ESS,Emacs Speaks Statistics,请参阅之前的常见问题解答。

劣等 R 模式提供了一个 readline/history 机制,对象名称完成,以及使用 Font Lock 模式对交互缓冲区进行基于语法的突出显示,以及一个非常方便的 R 帮助系统接口。

当然,它也很好地与使用 Emacs 编辑 R 源代码的机制集成在一起。您可以在一个 Emacs 缓冲区中编写代码,并将全部或部分代码发送到 R 执行;这对数据分析和编程都有帮助。您还可以无缝地与版本控制系统集成,以便维护程序和数据的更改日志,以及允许检索代码的旧版本。

此外,它允许您记录您的会话,这也可以通过使用转录模式来用于错误恢复。

要为劣等 R 进程指定命令行参数,请使用 C-u M-x R 启动 R。


6.3 在 Emacs 中调试 R

要从 Emacs 内部调试 R,有几种方法。要使用 Emacs GUD(Grand Unified Debugger)库和推荐的调试器 GDB,请键入 M-x gdb 并将 R 二进制文件 的路径作为参数。在 gdb 提示符下,设置 R_HOME 和其他环境变量(例如,使用 set env R_HOME /path/to/R/,但请参见下文),并使用所需的参数启动二进制文件(例如,run --quiet)。

如果您有 ESS,您可以执行 C-u M-x R RET - d SPC g d b RET 以使用参数 -d gdb 启动一个下级 R 进程。

第三种方法是通过 ESS (M-x R) 启动一个下级 R 进程,然后启动 GUD (M-x gdb),并将 R 二进制文件(使用其完整路径名)作为要调试的程序。使用程序 ps 查找当前运行的 R 进程的进程号,然后在 gdb 中使用 attach 命令将其附加到该进程。这种方法的一个优点是您拥有独立的 *R**gud-gdb* 窗口。在 *R* 窗口中,您拥有我们熟知和喜爱的所有 ESS 功能,例如对象名称补全。

当使用 GUD 模式从 Emacs 内部进行调试时,您可能会发现将包含代码的目录作为当前工作目录,然后从该目录到 R 二进制文件创建一个符号链接最方便。这样,.gdbinit 可以保留在包含代码的目录中,并用于设置环境和源代码的搜索路径,例如:

set env R_HOME /opt/R
set env R_PAPERSIZE letter
set env R_PRINTCMD lpr
dir /opt/R/src/appl
dir /opt/R/src/main
dir /opt/R/src/nmath
dir /opt/R/src/unix

7 R 杂项


7.1 如何将列表的组件设置为 NULL?

您可以使用

x[i] <- list(NULL)

将列表 x 的组件 i 设置为 NULL,对于命名组件也是如此。不要将 x[i]x[[i]] 设置为 NULL,因为这将从列表中删除相应的组件。

对于删除矩阵 x 的行名,使用 rownames(x) <- NULL 可能更容易,对于列名也是如此。


7.2 如何保存我的工作空间?

save.image() 将用户 .GlobalEnv 中的对象保存到 R 启动目录中的 .RData 文件中。(这与 q("yes") 之后发生的情况相同。)使用 save.image(file) 可以将图像保存为不同的名称。


7.3 如何清理我的工作空间?

要删除当前活动环境(通常是 .GlobalEnv)中的所有对象,您可以执行以下操作

rm(list = ls(all.names = TRUE))

(如果没有 all = TRUE,则只删除名称不以 ‘.’ 开头的对象。)


7.4 如何使 eval() 和 D() 工作?

如果您使用 eval(print(x), envir = e)D(x^2, "x"),将会发生奇怪的事情。第一个将告诉您 "x" 未找到,或者打印错误的 x 的值。另一个如果 x 存在,则可能返回零,否则返回错误。

这是因为在这两种情况下,第一个参数都首先在调用环境中进行评估。然后对结果(应该是模式为 "expression""call" 的对象)进行评估或微分。您(最有可能)真正想要的是通过将第一个参数用 expression() 包围来“引用”它来获得。例如,

R> D(expression(x^2), "x")
2 * x

虽然这种行为最初看起来可能很奇怪,但它是完全合乎逻辑的。可以轻松地实现“直观”的行为,但只要表达式包含在变量中,作为参数传递,或者是一个函数调用的结果,就会出现问题。例如,考虑以下情况下的语义

D2 <- function(e, n) D(D(e, n), n)

g <- function(y) eval(substitute(y), sys.frame(sys.parent(n = 2)))
g(a * b)

有关更多示例,请参阅 deriv() 的帮助页面。


7.5 为什么我的矩阵会丢失维度?

当通过下标操作创建具有单行或单列的矩阵时,例如 row <- mat[2, ],它默认情况下会被转换为向量。类似地,如果通过下标操作创建了一个维度为 2 x 3 x 1 x 4 的数组,它将被强制转换为 2 x 3 x 4 数组,丢失不必要的维度。经过多次讨论,这被确定为一个 特性

要防止这种情况发生,请在对下标操作时添加选项 drop = FALSE。例如,

rowmatrix <- mat[2, , drop = FALSE]  # creates a row matrix
colmatrix <- mat[, 2, drop = FALSE]  # creates a column matrix
a <- b[1, 1, 1, drop = FALSE]        # creates a 1 x 1 x 1 array

在编程时应防御性地使用 drop = FALSE 选项。例如,语句

somerows <- mat[index, ]

如果 index 的长度恰好为 1,则将返回一个向量而不是矩阵,从而导致代码后面的错误。它应该被重写为

somerows <- mat[index, , drop = FALSE]

7.6 自动加载是如何工作的?

自从包成为延迟加载后,自动加载很少使用。

R 拥有一个名为 .AutoloadEnv 的特殊环境。使用 autoload(name, pkg),其中 namepkg 是字符串,分别给出对象名称和包含该对象的包名称,将在该环境中存储一些信息。当 R 尝试评估 name 时,它将加载相应的包 pkg 并在新包的环境中重新评估 name

使用这种机制使 R 的行为就像包已加载一样,但不会占用内存(至少现在不会)。

有关一个非常好的示例,请参阅 autoload() 的帮助页面。


7.7 我应该如何设置选项?

函数 options() 允许设置和检查各种全局“选项”,这些选项会影响 R 计算和显示结果的方式。变量 .Options 保存了这些选项的当前值,但除非你想让自己陷入疯狂,否则永远不要直接赋值给它——简单地把它当作一个“只读”变量。

例如,给定

test1 <- function(x = pi, dig = 3) {
  oo <- options(digits = dig); on.exit(options(oo));
  cat(.Options$digits, x, "\n")
}
test2 <- function(x = pi, dig = 3) {
  .Options$digits <- dig
  cat(.Options$digits, x, "\n")
}

我们得到

R> test1()
3 3.14 
R> test2()
3 3.141593

真正使用的是 全局.Options 值,使用 options(OPT = VAL) 可以正确地更新它。.Options 的本地副本,无论是在 .GlobalEnv 中还是在函数环境(帧)中,都会被默默地忽略。


7.8 Windows 中的文件名如何工作?

由于 R 使用 C 风格的字符串处理,因此 ‘\’ 被视为转义字符,例如,可以将换行符输入为 ‘\n’。当您真正需要 ‘\’ 时,您必须用另一个 ‘\’ 对其进行转义。

因此,在文件名中使用类似 "c:\\data\\money.dat" 的内容。您也可以用 ‘/’ ("c:/data/money.dat") 替换 ‘\’。


7.9 为什么绘图会给出颜色分配错误?

这与现代 X11 安装中很少见的问题有关。

在 X11 设备上,绘图有时(例如,运行 demo("image"))会导致“错误:颜色分配错误”。这是一个 X 问题,与 R 只有间接关系。当在 R 启动之前启动的应用程序已使用所有可用颜色时,就会发生这种情况。(可用颜色数量取决于 X 配置;有时只能使用 256 种颜色。)

您也可以将 X11()colortype 设置为 "pseudo.cube" 而不是默认的 "pseudo"。有关更多信息,请参阅 X11() 的帮助页面。


7.10 如何将因子转换为数值?

当将数值数据读入 R 时(通常是在读取文件时),它们可能会作为因子出现。如果 f 是这样的因子对象,您可以使用

as.numeric(as.character(f))

来获取数字。更有效,但更难记住的是

as.numeric(levels(f))[as.integer(f)]

无论如何,不要直接调用 as.numeric() 或类似函数来执行此任务(因为 as.numeric()unclass() 会给出内部代码)。


7.11 R 中是否实现了 Trellis 显示?

推荐的包 lattice(基于基础包 grid)提供了与大多数 Trellis 命令兼容的图形功能。

您也可以查看 coplot()dotchart(),它们可能至少可以执行您想要做的一些事情。还要注意,R 版本的 pairs() 非常通用,提供了 splom() 的大多数功能,并且 R 的默认绘图方法有一个参数 asp,允许指定(并针对设备调整大小修复)绘图的纵横比。

(由于“Trellis”一词已被注册为商标,因此我们在 R 中不使用它。“lattice”这个名字被选为 R 的等效词。)


7.12 什么是封闭环境和父环境?

在函数内部,您可能希望访问两个额外的环境中的变量:定义函数的环境(“封闭”)和调用函数的环境(“父”)。

如果您在命令行创建函数或在包中加载函数,则其封闭环境是全局工作空间。如果您在另一个函数 g() 内部定义函数 f(),则其封闭环境是 g() 内部的环境。函数的封闭环境在创建函数时固定。您可以使用 environment(f) 找出函数 f() 的封闭环境。

另一方面,“父”环境是在您调用函数时定义的。如果您在命令行调用 lm(),则其父环境是全局工作空间;如果您在函数 f() 内部调用它,则其父环境是 f() 内部的环境。您可以使用 parent.frame()sys.frame(sys.parent()) 找出函数调用的父环境。

因此,对于大多数用户可见的函数,封闭环境将是全局工作空间,因为大多数函数是在那里定义的。父环境将是调用函数的位置。如果函数 f() 在另一个函数 g() 内部定义,则它可能也会在 g() 内部使用,因此其父环境和封闭环境可能相同。

父环境很重要,因为诸如模型公式之类的东西需要在调用函数的环境中进行评估,因为所有变量都将在那里可用。这依赖于父环境在每次调用时都可能不同。

封闭环境很重要,因为函数可以使用封闭环境中的变量与其他函数或自身的其他调用共享信息(参见关于词法作用域的部分)。这依赖于封闭环境在每次调用函数时都相同。(在 C 中,这将使用静态变量来完成。)

作用域 困难的。查看示例会有所帮助。查看在 R 和 S 中工作方式不同的示例并尝试找出它们为何不同的示例特别有启发性。描述 R 和 S 之间作用域差异的一种方法是说,在 S 中,封闭环境 始终 是全局工作空间,但在 R 中,封闭环境是创建函数的位置。


7.13 如何在绘图标签中进行替换?

通常,我们希望在绘图标签(例如标题)中使用 R 对象的值。如果标签是一个简单的字符字符串,可以使用 paste() 很容易实现,但如果标签是一个表达式(用于精细的数学注释),则并不总是很明显。在这种情况下,可以使用 parse() 处理粘贴的字符字符串,或者使用 substitute() 处理表达式。例如,如果 ahat 是您感兴趣的参数 a 的估计量,请使用

title(substitute(hat(a) == ahat, list(ahat = ahat)))

(注意是 ‘==’ 而不是 ‘=’)。有时 bquote() 可以提供更简洁的形式,例如:

title(bquote(hat(a) = .(ahat)))

其中用 ‘.()’ 括起来的子表达式将被其值替换。

邮件列表存档中还有更多示例。


7.14 什么是有效的名称?

使用 data.frame()read.table() 创建数据框时,R 默认情况下会确保变量名称在语法上有效。(这些函数的 check.names 参数控制是否检查变量名称,并在需要时由 make.names() 进行调整。)

要了解哪些名称是“有效的”,需要考虑到“名称”一词在语言中以几种不同的(但相关的)方式使用

  1. 一个 语法名称 是解析器解释为这种类型表达式的字符串。它由字母、数字、点和下划线字符组成,并以字母或不后跟数字的点开头。保留字不是语法名称。
  2. 一个 对象名 是与对象关联的字符串,该字符串在表达式中通过将对象名放在赋值运算符的左侧或作为 assign() 函数的参数来分配。它通常也是一个语法名称,但如果被引用,则可以是任何非空字符串(并且在调用 assign() 时始终被引用)。
  3. 一个 参数名 是在函数调用中提供参数时出现在等号左侧的内容(例如,f(trim=.5))。参数名通常也是语法名称,但如果被引用,则可以是任何内容。
  4. 一个 元素名 是一个字符串,用于标识对象的一部分(例如,列表的组件)。当它用在 ‘$’ 运算符的右侧时,它必须是语法名称或被引用。否则,元素名可以是任何字符串。(当对象用作数据库时,例如在调用 eval()attach() 时,元素名将变为对象名。)
  5. 最后,一个 文件名 是一个字符串,用于标识操作系统中用于读取、写入等的某个文件。它实际上与语言中的名称没有太大关系,但传统上将这些字符串称为文件“名称”。

7.15 R 中是否实现了 GAM?

gam 来自 CRAN 实现了白皮书中 GAM 章节描述的所有广义加性模型 (GAM) 功能。特别是,它实现了使用局部回归和平滑样条的回溯拟合,并且可以扩展。包 mgcv 中有一个 gam() 函数用于 GAM,但它不是白皮书中描述的完全克隆(例如没有 lo())。包 gss 也可以拟合基于样条的 GAM。如果你可以接受回归样条,你可以使用 glm()。对于高斯 GAM,你可以使用包 mda 中的 bruto()


7.16 为什么当我 source() 文件时没有输出?

大多数 R 命令不会生成任何输出。命令

1+1

计算值 2 并返回它;命令

summary(glm(y~x+z, family=binomial))

拟合一个逻辑回归模型,计算一些摘要信息并返回一个类为 "summary.glm" 的对象(参见 如何编写摘要方法?)。

如果你在命令行中输入 ‘1+1’ 或 ‘summary(glm(y~x+z, family=binomial))’,返回的值会自动打印(除非它是 invisible()),但在其他情况下,例如在 source() 的文件中或函数内部,它不会打印,除非你专门打印它。

要打印该值,请使用

print(1+1)

print(summary(glm(y~x+z, family=binomial)))

代替,或者使用 source(file, echo=TRUE)


7.17 为什么 `outer()` 在我的函数中表现异常?

正如 `outer()` 的帮助文档所述,它不像 `apply()` 家族那样适用于任意函数。它需要向量化的函数,以便对数组进行逐元素操作。正如你从代码中看到的那样,`outer(x, y, FUN)` 会创建两个包含 `x` 和 `y` 所有可能元素组合的大向量,然后将它们一次性传递给 `FUN`。你的函数可能无法处理两个大型向量作为参数。

如果你有一个无法处理两个向量但可以处理两个标量的函数,那么你仍然可以使用 `outer()`,但你需要先将你的函数包装起来,以模拟向量化行为。假设你的函数是

foo <- function(x, y, happy) {
  stopifnot(length(x) == 1, length(y) == 1) # scalars only!
  (x + y) * happy
}

如果你定义了通用函数

wrapper <- function(x, y, my.fun, ...) {
  sapply(seq_along(x), FUN = function(i) my.fun(x[i], y[i], ...))
}

那么你可以通过编写以下代码来使用 `outer()`,例如:

outer(1:4, 1:2, FUN = wrapper, my.fun = foo, happy = 10)

标量函数也可以使用 `Vectorize()` 进行向量化。


7.18 为什么 `anova()` 的输出取决于模型中因素的顺序?

在像 `~A+B+A:B` 这样的模型中,R 将报告模型 `~1`、`~A`、`~A+B` 和 `~A+B+A:B` 之间的平方和差异。如果模型是 `~B+A+A:B`,R 将报告 `~1`、`~B`、`~A+B` 和 `~A+B+A:B` 之间的差异。在第一种情况下,`A` 的平方和是比较 `~1` 和 `~A`,在第二种情况下,它是比较 `~B` 和 `~B+A`。在非正交设计中(即大多数不平衡设计),这些比较在概念上和数值上都是不同的。

一些包报告的是基于将完整模型与每个因素被移除一次的模型进行比较的平方和(例如,来自 SAS 的著名的“类型 III 平方和”)。这些不依赖于模型中因素的顺序。关于哪组平方和是正确的问题,在 R-help 上偶尔会引发低级别的圣战。

没有必要对 R 报告的特定平方和感到不安。你可以很容易地计算出你喜欢的平方和。任何两个模型都可以用 `anova(model1, model2)` 进行比较,`drop1(model1)` 将显示删除单个项所产生的平方和。


7.19 如何在批处理模式下生成 PNG 图像?

在类 Unix 系统上,如果您的安装支持 png() 设备的 type="cairo" 选项,则应该没有问题,默认设置应该可以正常工作。此选项在 R 2.7.0 之前的版本或不支持 cairo 的情况下不可用。从 R 2.7.0 开始,png() 默认情况下在 macOS 上使用 Quartz 设备,它也可以在批处理模式下工作。

早期版本的 png() 设备使用 X11 驱动程序,这在批处理模式或远程操作中存在问题。如果您有 Ghostscript,您可以使用 bitmap(),它会生成一个 PostScript 或 PDF 文件,然后将其转换为 Ghostscript 支持的任何位图格式。在某些安装中,这会产生难看的输出,而在其他安装中则完全令人满意。现在许多系统都附带来自 X.Org 的 Xvfb(可能作为可选安装),它是一个不需要屏幕的 X11 服务器。


7.20 如何使命令行编辑工作?

R 的类 Unix 命令行界面只能提供内置的命令行编辑器,该编辑器允许在 R 编译配置时提供 GNU readline 库的情况下,回溯、编辑和重新提交之前的命令。请注意,需要包含适当头文件的 readline 的“开发”版本:Linux 二进制发行版的用户需要安装 libreadline-dev(Debian)或 readline-devel(Red Hat)等软件包。


7.21 如何将字符串转换为变量?

如果您有

varname <- c("a", "b", "d")

您可以执行

get(varname[1]) + 2

对于

a + 2

assign(varname[1], 2 + 2)

对于

a <- 2 + 2

eval(substitute(lm(y ~ x + variable),
                list(variable = as.name(varname[1]))))

对于

lm(y ~ x + a)

至少在前两种情况下,使用列表通常更容易,然后您可以轻松地按名称索引它

vars <- list(a = 1:10, b = rnorm(100), d = LETTERS)
vars[["a"]]

无需任何此类混乱。


7.22 为什么 lattice/trellis 图形不起作用?

最可能的原因是您忘记告诉 R 显示图形。Lattice 函数(如 xyplot())会创建一个图形对象,但不会显示它(ggplot2 图形和 S-PLUS 中的 Trellis 图形也是如此)。图形对象的 print() 方法会生成实际的显示。当您在命令行交互式地使用这些函数时,结果会自动打印,但在 source() 或您自己的函数内部,您将需要一个显式的 print() 语句。


7.23 如何对数据框的行进行排序?

要对数据框中的行进行排序,相对于一个或多个列中的值,只需使用 order()(例如,DF[order(DF$a, DF[["b"]]), ] 对名为 ab 的列的数据框 DF 进行排序)。


7.24 为什么 help.start() 搜索引擎不起作用?

从 R 2.10.0 开始,help.start() 中的基于浏览器的搜索引擎是 help.search()HTML 接口,应该始终有效。在此之前,该引擎使用 Java applet。为了使此功能正常运行,需要在系统上安装并链接到浏览器的兼容版本的 Java,并且在浏览器中启用 Java JavaScript。


7.25 为什么我在更新 R 后 .Rprofile 不起作用了?

您是否阅读了 NEWS 文件?对于不在 base 包中的函数,您需要指定正确的包命名空间,因为代码将在加载包 之前运行。例如,

ps.options(horizontal = FALSE)
help.start()

需要改为

grDevices::ps.options(horizontal = FALSE)
utils::help.start()

(在 R 1.9.x 中为 graphics::ps.options(horizontal = FALSE))。


7.26 所有方法都去哪里了?

许多函数,特别是 S3 方法,现在隐藏在命名空间中。这样做的好处是,它们不会意外地被错误类别的参数调用,但这也使得它们更难查看。

要查看 S3 方法(例如,[.terms)的代码,请使用

getS3method("[", "terms")

要查看包 "bar" 命名空间中未导出函数 foo() 的代码,请使用 bar:::foo。不要在自己的代码中使用这些结构来调用未导出函数——它们很可能由于某种原因而未导出,并且可能会在没有警告的情况下发生变化。


7.27 如何创建旋转的轴标签?

要旋转轴标签(使用基本图形),您需要使用 text(),而不是 mtext(),因为后者不支持 par("srt")

## Increase bottom margin to make room for rotated labels
par(mar = c(7, 4, 4, 2) + 0.1)
## Create plot with no x axis and no x axis label
plot(1 : 8, xaxt = "n",  xlab = "")
## Set up x axis with tick marks alone
axis(1, labels = FALSE)
## Create some text labels
labels <- paste("Label", 1:8, sep = " ")
## Plot x axis labels at default tick marks
text(1:8, par("usr")[3] - 0.25, srt = 45, adj = 1,
     labels = labels, xpd = TRUE)
## Plot x axis label at line 6 (of 7)
mtext(1, text = "X Axis Label", line = 6)

在绘制 x 轴标签时,我们使用 srt = 45 表示文本旋转角度,adj = 1 表示将文本的右端放置在刻度标记处,以及 xpd = TRUE 表示允许文本超出绘图区域。您可以根据需要调整 0.25 偏移量的值,以将轴标签相对于 x 轴向上或向下移动。有关更多信息,请参见 ?par

另请参见 Paul Murrell (2003) 的图 1 及相关代码,“Integrating grid Graphics Output with Base Graphics Output”,R News3/2,7–12。


7.28 为什么 read.table() 效率低下?

默认情况下,read.table() 需要将所有内容读入为字符数据,然后尝试找出哪些变量需要转换为数值或因子。对于大型数据集,这需要相当长的时间和内存。通过使用 colClasses 参数指定表格列的假设类别,可以大幅提高性能。


7.29 包和库有什么区别?

一个 是一个标准化的材料集合,扩展了 R,例如提供代码、数据或文档。一个 是一个地方(目录),R 知道在哪里可以找到它可以使用的包(即已 安装 的包)。R 通过调用 library 函数来告诉它使用一个包(“加载”它并将其添加到搜索路径)。也就是说,library() 用于从包含包的库中加载包。

有关更多详细信息,请参阅 R 附加包。另请参阅 Uwe Ligges (2003),“R 帮助台:包管理”,R 新闻3/3,37–39。


7.30 我安装了一个包,但函数不见了

要真正 使用 该包,需要使用 library() 将其 加载

请参阅 R 附加包包和库有什么区别 以了解更多信息。


7.31 为什么 R 认为这些数字不相等?

R 的数字类型中唯一可以精确表示的数字是整数和分母为 2 的幂的分数。所有其他数字在内部都会被四舍五入到(通常)53 位二进制精度。因此,两个浮点数将不可靠地相等,除非它们是由相同的算法计算出来的,即使这样也不总是如此。例如

R> a <- sqrt(2)
R> a * a == 2
[1] FALSE
R> a * a - 2
[1] 4.440892e-16
R> print(a * a, digits = 18)
[1] 2.00000000000000044

函数 all.equal() 使用 .Machine$double.eps ^ 0.5 的数字容差比较两个对象。如果您想要比这更高的精度,您需要仔细考虑误差传播。

Richard M. Heiberger 和 Burt Holland(Springer 2015,第二版)的《统计分析和数据显示:使用 R 的中级课程》附录 G“计算精度和浮点运算”(第 753-771 页)中包含许多易于理解的示例。该附录可以从 https://link.springer.com/content/pdf/bbm:978-1-4939-2122-5/1.pdf 免费下载。

有关更多信息,请参阅例如 David Goldberg(1991)的“每个计算机科学家应该知道的关于浮点运算的知识”,《ACM 计算调查》,23/1,5-48,也可以通过 https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html 获取。

这里还有另一个例子,这次使用加法

R> .3 + .6 == .9
[1] FALSE
R> .3 + .6 - .9
[1] -1.110223e-16
R> print(matrix(c(.3,  .6,  .9, .3 + .6)), digits = 18)
                     [,1]
[1,] 0.299999999999999989
[2,] 0.599999999999999978
[3,] 0.900000000000000022
[4,] 0.899999999999999911

7.32 如何在长时间模拟中捕获或忽略错误?

使用 try(),它返回一个类为 "try-error" 的对象而不是错误,或者最好使用 tryCatch(),其中返回值可以更灵活地配置。例如

beta[i,] <- tryCatch(coef(lm(formula, data)),
                     error = function(e) rep(NaN, 4))

如果 lm() 调用成功,则返回系数;如果失败,则返回 c(NaN, NaN, NaN, NaN)(假设此示例中应该有 4 个系数)。


7.33 为什么负数的幂计算错误?

你可能看到了类似的东西

R> -2^2
[1] -4

并且误解了 R 中表达式的前缀规则。写

R> (-2)^2
[1] 4

以获得 -2 的平方。

前缀规则在 ?Syntax 中有记录,要查看 R 如何解释表达式,你可以查看解析树

R> as.list(quote(-2^2))
[[1]]
`-`

[[2]]
2^2

7.34 如何将循环中每次迭代的结果保存到单独的文件中?

一种方法是使用 paste()(或 sprintf())将文件名根和迭代次数连接起来,而 file.path() 则构造路径。例如,要将结果保存到当前工作目录的 Results 子目录中的 result1.rda、…、result100.rda 文件中,可以使用

for(i in 1:100) {
  ## Calculations constructing "some_object" ...
  fp <- file.path("Results", paste("result", i, ".rda", sep = ""))
  save(list = "some_object", file = fp)
}

7.35 为什么使用 lmer() 时不显示 p 值?

Doug Bates 在 r-help 列表上的帖子中提供了详细的回复,可以在 https://stat.ethz.ch/pipermail/r-help/2006-May/094765.html 查看。


7.36 为什么在将图形保存到 PS 或 PDF 文件时,会出现不需要的边框、线条或网格状伪影?

当使用诸如 polygon()filled.contour()image() 或其他可能在内部调用这些函数的函数时,可能会发生这种情况。在 polygon() 的情况下,即使将 border 参数设置为 NA"transparent",您也可能会观察到多边形之间不需要的边框。

问题的原因是当图形进行抗锯齿处理时,PS/PDF 查看器。解决方案的详细信息将根据使用的查看器、操作系统而有所不同,并且可能会随着时间的推移而发生变化。对于一些常见的查看器,请考虑以下内容

Acrobat Reader(跨平台)

在“首选项”中,可以选择启用/禁用文本平滑、图像平滑和线条艺术平滑。禁用线条艺术平滑。

预览(macOS)

在“首选项”中,可以选择启用/禁用文本和线条艺术的抗锯齿处理。禁用此选项。

GSview(跨平台)

有文本 Alpha 和图形 Alpha 的设置。将图形 Alpha 从 4 位更改为 1 位以禁用图形抗锯齿处理。

gv(类 Unix X)

可以选择启用/禁用抗锯齿处理。禁用此选项。

Evince(Linux/GNOME)

此查看器中没有禁用抗锯齿处理的选项。

Okular(Linux/KDE)

GUI 中没有启用/禁用抗锯齿处理的选项。从控制台命令行,使用

$ kwriteconfig --file okularpartrc --group ‘Dlg Performance’ \
               --key GraphicsAntialias Disabled

然后重新启动 Okular。将最后一个词更改为“Enabled”以恢复原始设置。


7.37 为什么反斜杠在字符串中表现奇怪?

这个问题最常出现在文件名相关问题中(参见 Windows 中的文件名如何工作?),但人们也抱怨说,他们似乎无法将单个 ‘\’ 字符放入文本字符串中,除非它恰好后面跟着某些其他字符。

要理解这一点,您必须区分字符字符串和字符字符串的表示。大多数情况下,R 中的表示只是字符串两端带有一个或两个引号,但有些字符串无法用这种方式表示,例如,自身包含引号字符的字符串。所以

> str <- "This \"text\" is quoted"
> str
[1] "This \"text\" is quoted"
> cat(str, "\n")
This "text" is quoted

转义序列\"’ 和 ‘\n’ 分别表示双引号和换行符。使用 print() 或在提示符下键入名称来打印文本字符串也会使用转义序列,但 cat() 函数会按原样显示字符串。请注意,‘"\n"’ 是一个单字符字符串,而不是两个字符;反斜杠实际上不在字符串中,它只是在打印的表示中生成。

> nchar("\n")
[1] 1
> substring("\n", 1, 1)
[1] "\n"

那么如何将反斜杠放入字符串中呢?为此,您必须转义转义字符。也就是说,您必须将反斜杠加倍。例如

> cat("\\n", "\n")
\n

某些函数,特别是那些涉及正则表达式匹配的函数,本身使用元字符,这些元字符可能需要使用反斜杠机制进行转义。在这些情况下,您可能需要使用四重反斜杠来表示单个字面反斜杠。

在 R 2.4.1 之前的版本中,未知的转义序列(如 ‘\p’)会静默地解释为 ‘p’。当前版本的 R 会发出警告。


7.38 如何在绘图中添加误差条或置信区间?

一些函数会显示带有误差条的特定类型的绘图,例如 agricolae 包中的 bar.err() 函数,gplots 包中的 plotCI() 函数,plotrix 包中的 plotCI()brkdn.plot() 函数以及 psych 包中的 error.bars()error.crosses()error.bars.by() 函数。在这些类型的函数中,有些会接受离散度量(例如,plotCI),有些会根据原始值计算离散度量(bar.errbrkdn.plot),还有一些会同时进行(error.bars)。还有一些函数只显示误差条,例如 plotrix 包中的离散度函数。上述大多数函数使用基本 graphics 包中的 arrows() 函数来绘制误差条。

上述函数都使用基本图形系统。grid 和 lattice 图形系统也有用于显示误差条的特定函数,例如 grid 包中的 grid.arrow() 函数,以及 ggplot2 包中的 geom_errorbar()geom_errorbarh()geom_pointrange()geom_linerange()geom_crossbar()geom_ribbon() 函数。在 lattice 系统中,可以使用 Hmisc 包中的 Dotplot()xYplot() 以及 latticeExtra 包中的 segplot() 来显示误差条。


7.39 如何创建具有两个 y 轴的绘图?

在 R 中可以创建具有两个 y 轴的图形,即具有两种按相同垂直大小缩放的数据,并在绘图的左侧和右侧显示单独的垂直轴,反映数据的原始比例。但是,不建议这样做。构建此类图形的基本方法是使用 par(new=TRUE)(参见 ?par);函数 twoord.plot()(在 plotrix 包中)和 doubleYScale()(在 latticeExtra 包中)在一定程度上自动化了此过程。


7.40 如何访问函数的源代码?

在大多数情况下,键入函数名称将打印其源代码。但是,代码有时会隐藏在命名空间中或被编译。有关如何访问源代码的完整概述,请参见 Uwe Ligges (2006),“帮助台:访问源代码”,R 新闻6/4,43–45 (https://CRAN.R-project.org/doc/Rnews/Rnews_2006-4.pdf)。


7.41 为什么当我在没有截距的情况下拟合线性模型时,summary() 会报告 R^2 估计值的奇怪结果?

?summary.lm 中所述,当截距为零(例如,来自 y ~ x - 1y ~ x + 0)时,summary.lm() 使用公式 R^2 = 1 - Sum(R[i]^2) / Sum((y[i])^2),这与通常的 R^2 = 1 - Sum(R[i]^2) / Sum((y[i] - mean(y))^2) 不同。有几个原因导致这种情况

  • 否则,R^2 可能为负(因为没有截距的模型的拟合效果可能比它隐式比较的常数均值模型更差)。
  • 如果在通过原点的直线模型中将斜率设置为零,则得到拟合值 y*=0
  • 具有常数非零均值的模型不嵌套在通过原点的直线模型中。

所有这些归结为:如果你事先知道当 x=0E[Y]=0,那么你应该与拟合直线进行比较的“零”模型,即 x 不解释任何方差的模型,是 E[Y]=0 始终成立的模型。(如果你事先不知道当 x=0E[Y]=0,那么你可能不应该拟合一条通过原点的直线。)


7.42 为什么 R 似乎没有释放内存?

这个问题通常以不同的形式出现,例如“我在 R 中删除了对象并运行了 gc(),但 ps/top 仍然显示 R 进程使用了大量内存”,通常发生在 Linux 机器上。

这是操作系统 (OS) 分配内存方式的产物。通常情况下,操作系统无法释放所有未使用的内存。在极端情况下,即使 R 释放了几乎所有内存,操作系统也可能由于其设计而无法释放任何内存,因此 pstop 等工具将报告 R 进程使用了大量驻留 RAM,即使 R 已经释放了所有这些内存。通常情况下,这些工具 不会 报告进程的实际内存使用情况,而是报告操作系统为该进程保留的内存量。

简而言之,这是操作系统中内存分配器的限制,R 无能为力。该空间只是由操作系统保留,希望 R 稍后会请求它。下一段将提供更深入的答案,其中包含有关此过程的技术细节。

大多数系统使用两种不同的方式来分配内存。对于大块内存的分配,它们将使用 mmap 将内存映射到进程地址空间。当这些大块内存完全空闲时,可以立即释放它们,因为它们可以在虚拟内存中的任何位置。但是,这是一个相对昂贵的操作,许多操作系统对这种分配块的数量有限制,因此它仅用于分配大型内存区域。对于较小的分配,系统可以扩展进程的数据段(历史上使用 brk 系统调用),但整个区域始终是连续的。操作系统只能移动此空间的末尾,它不能创建任何“空洞”。由于此操作相当便宜,因此它用于分配小块内存。但是,副作用是,即使在数据段末尾只有一个字节在使用,操作系统也无法释放任何内存,因为它无法更改该字节的地址。这实际上比看起来更常见,因为分配大量中间对象,然后分配结果对象并删除所有中间对象是一种非常常见的做法。由于结果是在末尾分配的,因此它将阻止操作系统释放中间对象使用的任何内存。在实践中,这并不一定是一个问题,因为现代操作系统可以将虚拟内存中未使用的部分分页出去,因此它不一定减少可用于其他应用程序的实际内存量。通常,字符串或配对列表等小型对象会受到这种行为的影响,而长向量等大型对象将使用 mmap 分配,因此不受影响。在 Linux(以及可能的其他类 Unix 系统)上,可以使用 mallinfo 系统调用(另请参见 mallinfo 包)来查询分配器有关分配布局的信息,包括实际使用的内存以及无法释放的未使用的内存。


7.43 如何在 R 中启用安全的 https 下载?

从 R 4.2.0 开始,"libcurl" 下载方法始终可用,并且在所有平台上默认用于 HTTPS。它从 R 3.3.0 开始使用,除了 Windows,在 Windows 上,默认方法 "wininet" 也支持 HTTPS

因此,在最近版本的 R 中,无需执行任何操作即可访问“https://”网站。


7.44 如何获取旧版 R 的 CRAN 包二进制文件?

自 2016 年 3 月起,旧版 R(发布日期超过 5 年)的 CRAN 包的 Windows 和 macOS 二进制文件从中央 CRAN 存档服务器提供,而不是从 CRAN 镜像提供。要获取这些文件,应将 CRAN 的 “镜像” 元素设置为 repos 选项,例如

local({r <- getOption("repos")
       r["CRAN"] <- "http://CRAN-archive.R-project.org"
       options(repos = r)
      })

(有关更多信息,请参见 ?options)。


8 R 编程


8.1 我应该如何编写摘要方法?

假设您想为类 "foo" 提供一个摘要方法。那么 summary.foo() 不应该打印任何内容,而是应该返回一个类为 "summary.foo" 的对象,并且您应该编写一个方法 print.summary.foo(),该方法可以很好地打印摘要信息并隐式返回其对象。这种方法优于让 summary.foo() 打印摘要信息并返回一些有用的东西,因为有时您需要在函数或类似的函数中获取 summary() 计算的内容。在这种情况下,您不希望打印任何内容。


8.2 如何调试动态加载的代码?

简单来说,你需要在调试器中启动 R,加载代码,发送中断,然后设置所需的断点。

参见 Writing R Extensions 中的 在动态加载的代码中查找入口点


8.3 如何在调试时检查 R 对象?

最方便的方法是从符号调试器中调用 R_PV

参见 Writing R Extensions 中的 在调试时检查 R 对象


8.4 如何更改编译标志?

假设你有一个用于动态加载到 R 中的 C 代码文件,但你想使用 R CMD SHLIB 并使用与默认标志不同的编译标志(这些标志是在构建 R 时确定的)。

从 R 2.1.0 开始,用户可以在 $HOME/.R 中提供个人 Makevars 配置文件来覆盖默认标志。参见 R Installation and Administration 中的 附加包


8.5 如何调试 S4 方法?

使用 trace() 函数,并使用参数 signature= 来为将要为相应签名调用的方法添加对浏览器或任何其他代码的调用。有关详细信息,请参见 ?trace


9 R 漏洞


9.1 什么是漏洞?

如果 R 执行了非法指令,或者因操作系统错误消息而崩溃,该消息表明程序存在问题(而不是像“磁盘已满”这样的问题),那么这肯定是一个漏洞。如果您自己调用了 .C().Fortran().External().Call()(或 .Internal())(或在您编写的函数中),您可以始终通过使用错误的参数类型(模式)来使 R 崩溃。这不是漏洞。

完成命令需要很长时间可能是漏洞,但您必须确保确实是 R 的问题。有些命令确实需要很长时间才能完成。如果输入是您知道应该快速处理的,请报告漏洞。如果您不知道该命令是否应该花费很长时间,请查看手册或寻求帮助以了解。

如果您熟悉的命令在通常定义应该合理的用例中导致 R 错误消息,那么这可能是一个漏洞。如果命令执行了错误的操作,那就是一个漏洞。但请确保您确切地知道它应该做什么。如果您不熟悉该命令,或者不确定该命令应该如何工作,那么它实际上可能运行正常。例如,有些人认为 R 的数学运算存在漏洞,因为他们不了解有限精度算术的工作原理。与其妄下结论,不如向了解情况的人展示问题。例如,十进制数比较的意外结果,例如 0.28 * 100 != 280.1 + 0.2 != 0.3,不是漏洞。有关更多详细信息,请参阅 为什么 R 认为这些数字不相等?

最后,命令的预期定义可能不适合统计分析。这是一种非常重要的问题,但也是一个判断问题。此外,由于对某些现有功能的无知,很容易得出这样的结论。在以通常的方式检查文档、确信您理解文档并确信您想要的功能不可用之前,最好不要抱怨此类问题。如果您在仔细阅读手册后仍不确定该命令应该做什么,这表明手册中存在漏洞。手册的职责是使一切清晰明了。报告文档漏洞与报告程序漏洞一样重要。但是,我们知道入门文档严重不足,因此您无需报告此问题。

如果函数的在线参数列表与手册不符,则其中一个一定有误,因此请报告错误。

有关更多信息,请参阅 R 中的错误报告 中的“确保这是一个错误”。


9.2 如何报告错误

当您确定存在错误时,重要的是要报告它,并以一种有用的方式报告它。最有用的是对您键入的命令的精确描述,从运行 R 的 shell 命令开始,直到问题发生。始终包含您使用的 R 版本、机器和操作系统;在 R 中键入 version 以打印此信息。

报告错误的最重要原则是报告 事实,而不是假设或分类。报告事实总是更容易,但人们似乎更喜欢努力提出解释并报告它们。如果解释基于对 R 实现方式的猜测,它们将毫无用处;其他人将不得不尝试弄清楚必须是什么事实才能导致这种推测。有时这是不可能的。但无论如何,对于那些试图解决问题的人来说,这是不必要的。

例如,假设在一个您知道相当大的数据集上,命令

R> data.frame(x, y, z, monday, tuesday)

永远不会返回。不要报告 data.frame() 对于大型数据集失败。也许它在变量名称是星期几时失败。如果是这样,那么当其他人收到您的报告时,他们会尝试在大型数据集上使用 data.frame() 命令,可能没有星期几的变量名称,并且没有看到任何问题。世界上没有办法让其他人猜到他们应该尝试一个星期几的变量名称。

或者,也许该命令失败是因为您使用的最后一个命令是 "["() 的一种方法,该方法存在错误,导致 R 的内部数据结构被破坏,并使 data.frame() 命令从那时起失败。这就是为什么其他人需要知道您键入了哪些其他命令(或从您的启动文件中读取)。

尝试找到产生明显相同错误的简单示例非常有用,找到可能预期会产生错误但实际上没有产生错误的简单示例也有一些用处。如果您想调试问题并找到确切的导致问题的原因,那太好了。您仍然应该报告事实以及任何解释或解决方案。请包含一个可以重现(例如,https://en.wikipedia.org/wiki/Reproducibility)问题的示例,最好是您找到的最简单的示例。

使用 --vanilla 选项调用 R 可能有助于隔离错误。这确保了不会读取站点配置文件和保存的数据文件。

在您实际提交错误报告之前,您应该检查该错误是否已经报告和/或修复。首先,尝试在 https://bugs.R-project.org/ 上使用“显示新到旧的开放错误”或搜索功能。其次,查阅 https://svn.R-project.org/R/trunk/doc/NEWS.Rd,其中记录了将在 R 的下一个版本中出现的更改,包括未出现在错误跟踪器上的错误修复。第三,如果可能,请尝试当前的 r-patched 或 r-devel 版本的 R。如果错误已经报告或修复,请不要再提交有关它的错误报告。最后,仔细检查错误是出在 R 上还是在贡献的包上。有关贡献包的错误报告应首先发送给包维护者,并且只有包维护者才能提交到 R-bugs 存储库,并在主题行中提及包。

可以使用 bug.report() 函数生成错误报告。对于有关 R 的报告,这将打开 R Bugzilla 页面,网址为 https://bugs.R-project.org/:对于贡献的包,它将打开包的错误跟踪器网页或帮助您撰写电子邮件给维护者。自 2016 年以来,只有“成员”(包括所有以前提交过错误的人)才能在 R Bugzilla 上提交新的错误。有关更多信息,请参阅 R 中的错误报告 上的“在哪里提交错误报告和补丁”。

错误存储库中有一个部分用于收集对 R 的增强建议,标签为“wishlist”。建议可以以与错误相同的方式提交,但请确保主题行明确表明这是针对愿望清单而不是错误报告,例如以“Wishlist:”开头。

有关 R 的 Windows 移植的更正和评论应发送至

有关消息翻译的更正和评论应发送给最后一位翻译者(列在相应的“.po”文件顶部)或翻译团队,如 https://developer.R-project.org/TranslationTeams.html 中所列。


10 致谢

当然,非常感谢 Robert 和 Ross 为 R 系统做出的贡献,以及所有为其添加功能的包编写者和移植者。

特别感谢 Doug Bates、Peter Dalgaard、Paul Gilbert、Stefano Iacus、Fritz Leisch、Jim Lindsey、Thomas Lumley、Martin Maechler、Brian D. Ripley、Anthony Rossini 和 Andreas Weingessel,他们的评论帮助我改进了这份 FAQ

更多内容即将推出…