R 入门

最后更新于 2026-11-07 | 编辑此页

概述

问题

  • R 的初步命令

学习目标

  • 定义与 R 相关的以下术语:对象、赋值、调用、函数、参数、选项。
  • 为 R 中的对象赋值。
  • 学习如何为对象命名
  • 使用注释为脚本提供说明。
  • 在 R 中执行简单的算术运算。
  • 调用函数并使用参数来更改其默认选项。
  • 检查向量的内容并对其内容进行操作。
  • 从向量中提取子集并提取值。
  • 分析包含缺失数据的向量。

本节基于 Data Carpentries 的 Data Analysis and Visualisation in R for Ecologists 课程。

在 R 中创建对象


您只需在控制台中输入数学表达式,即可获得 R 的输出结果:

R

3 + 5

输出

[1] 8

R

12 / 7

输出

[1] 1.714286

然而,为了实现实用且有趣的操作,我们需要为对象赋予值。要创建一个对象,我们需要为其命名,随后加上赋值运算符 <- 以及我们想要赋予的值:

R

weight_kg <- 55

<- 是赋值运算符。它将右侧的值赋给左侧的对象。因此,执行 x <- 3 后,x 的值就是 3。这个箭头可以理解为 3 进入 x。出于历史原因,你也可以使用 = 进行赋值,但并非在所有场景下都适用。由于语法上存在细微差别,建议始终使用 <- 进行赋值。

在 RStudio 中,输入 Alt + -(在按下 - 键的同时按下 Alt)即可在 PC 上一次按键写入 <-,而输入 Option + -(在按下 - 键的同时按下 Option)则在 Mac 上实现同样的效果。

命名变量

对象可以使用任何名称,例如 x、current_temperature 或 subject_id。建议对象名称要明确且不要过长。名称不能以数字开头(2x 无效,但 x2 有效)。R 区分大小写(例如,weight_kg 与 Weight_kg 不同)。某些名称因是 R 的基本函数名称而不能使用(例如 if、else、for,完整列表请参阅 R 中的保留字 手册页)。通常情况下,即使允许使用,最好也不要使用其他函数名称(例如 c、T、mean、data、df、weights)。如有疑问,请查看帮助文档以确认该名称是否已被占用。此外,最好避免在对象名称中使用点号(如 my.dataset 中的 .)。出于历史原因,R 中有许多函数名称包含点号,但由于点号在 R(用于方法)和其他编程语言中具有特殊含义,因此最好避免使用。同时建议对对象名称使用名词,对函数名称使用动词。保持代码格式的一致性(如空格的位置、对象的命名方式等)至关重要。使用一致的编码风格能让你未来的自己和合作者更清晰地阅读代码。在 R 中,一些常用的风格指南包括 Google 的、tidyverse 的 风格以及 Bioconductor 风格指南。tidyverse 的指南非常详尽,起初可能会让人感到不知所措。你可以安装 lintr 包,以自动检查代码格式是否存在问题。

对象与变量:在 R 中被称为 objects 的内容,在许多其他编程语言中则被称为 variables。根据上下文的不同,object 和 variable 的含义可能截然不同。不过,在本节课中,这两个词被互换使用。欲了解更多信息,请参阅此处。

在为对象赋值时,R 不会输出任何内容。你可以通过使用括号或直接输入对象名称来强制 R 输出该值:

R

weight_kg <- 55    # doesn't print anything
(weight_kg <- 55)  # but putting parenthesis around the call prints the value of `weight_kg`

输出

[1] 55

R

weight_kg          # and so does typing the name of the object

输出

[1] 55

现在 R 的内存中已经有了 weight_kg,我们可以对其进行算术运算。例如,我们可能想将这个重量转换为磅(磅重量是公斤重量的 2.2 倍):

R

2.2 * weight_kg

输出

[1] 121

我们还可以通过赋值新值来更改对象的值:

R

weight_kg <- 57.5
2.2 * weight_kg

输出

[1] 126.5

这意味着为一个对象赋值不会改变其他对象的值。例如,让我们将动物的重量(单位为磅)存储在一个新对象 weight_lb 中:

R

weight_lb <- 2.2 * weight_kg

然后将 weight_kg 更改为 100。

R

weight_kg <- 100
讨论

挑战:

你认为对象 weight_lb 目前的内容是什么?126.5 还是 220?

注释


R 中的注释符号是 #,脚本中 # 右侧的任何内容都将被 R 忽略。在脚本中留下备注和说明非常有用。

RStudio 让注释或取消注释段落变得简单便捷:选中想要注释的行后,同时按下键盘上的 Ctrl + Shift + C。如果你只想注释一行,可以将光标置于该行的任意位置(即无需选中整行),然后按下 Ctrl + Shift + C。

讨论

挑战

以下每条语句后的值分别是什么?

R

mass <- 47.5            # mass?
age  <- 122             # age?
mass <- mass * 2.0      # mass?
age  <- age - 20        # age?
mass_index <- mass/age  # mass_index?

函数及其参数


函数是“预设脚本”,用于自动化更复杂的命令集,包括运算赋值等。许多函数是预定义的,或者可以通过导入 R 包(稍后详述)来使用。函数通常接收一个或多个称为 参数 的输入。函数通常(但并非总是)返回一个 值。一个典型的例子是函数 sqrt()。输入(参数)必须是一个数字,而返回值(实际上就是输出)则是该数字的平方根。执行函数(“运行”它)被称为 调用 该函数。函数调用的一个示例如下:

R

b <- sqrt(a)

这里,a 的值被传递给 sqrt() 函数,sqrt() 函数计算平方根并返回该值,随后将该值赋值给对象 b。这个函数非常简单,因为它只接收一个参数。

函数的返回“值”不必是数值(如 sqrt() 那样),也不必是单一项:它可以是一组内容,甚至是一个数据集。我们在将数据文件读入 R 时将看到这一点。

参数可以是任何内容,不仅限于数字或文件名,还可以是其他对象。每个参数的具体含义因函数而异,必须在文档中查阅(见下文)。某些函数接收参数,这些参数可以由用户指定,或者在未指定时采用默认值:这些被称为选项。选项通常用于改变函数的运行方式,例如是否忽略“错误值”,或者在绘图中使用哪个符号。不过,如果你需要特定的设置,可以指定一个自定义值来替代默认值。

让我们试试一个可以接收多个参数的函数:round()。

R

round(3.14159)

输出

[1] 3

这里,我们仅传递了一个参数 3.14159 调用了 round(),它返回了值 3。这是因为默认行为是舍入到最接近的整数。如果我们需要更多位数,可以通过获取关于 round 函数的信息来了解如何实现。我们可以使用 args(round),或者使用 ?round 查看该函数的帮助信息。

R

args(round)

输出

function (x, digits = 0, ...)
NULL

R

?round

我们可以看到,如果想要不同的位数,可以输入 digits=2 或任何想要的位数。

R

round(3.14159, digits = 2)

输出

[1] 3.14

如果你按照定义顺序提供参数,则无需为其命名:

R

round(3.14159, 2)

输出

[1] 3.14

而且,如果你为参数命名,就可以调换它们的顺序:

R

round(digits = 2, x = 3.14159)

输出

[1] 3.14

在函数调用中,将必需参数(例如要舍入的数字)放在前面,并指定所有可选参数的名称,这是一个良好的实践。否则,阅读你代码的人可能需要查阅具有不熟悉参数的函数定义,才能理解你在做什么。通过指定参数名称,你还可以防范函数接口未来可能发生的变更,因为现有参数之间可能会增加新参数。

向量与数据类型


向量是 R 中最常见且最基础的数据类型,几乎是 R 的主力。向量由一系列值(如数字或字符)组成。我们可以使用 c() 函数将一系列值赋给向量。例如,我们可以创建一个动物体重向量,并将其赋给一个新对象 weight_g:

R

weight_g <- c(50, 60, 65, 82)
weight_g

输出

[1] 50 60 65 82

向量也可以包含字符:

R

molecules <- c("dna", "rna", "protein")
molecules

输出

[1] "dna"     "rna"     "protein"

这里 “dna”、“rna” 等字符串周围的引号至关重要。如果没有引号,R 会认为存在名为 dna、rna 和 protein 的对象。由于这些对象在 R 的内存中并不存在,因此会出现错误消息。

有许多函数可以让你检查向量的内容。length() 会告诉你某个特定向量中包含多少个元素:

R

length(weight_g)

输出

[1] 4

R

length(molecules)

输出

[1] 3

向量的一个重要特点是,所有元素都属于同一类型的数据。函数 class() 用于表示对象的类(元素类型):

R

class(weight_g)

输出

[1] "numeric"

R

class(molecules)

输出

[1] "character"

str() 函数可提供对对象及其元素结构的概览。在处理大型且复杂的对象时,这是一个非常有用的函数:

R

str(weight_g)

输出

 num [1:4] 50 60 65 82

R

str(molecules)

输出

 chr [1:3] "dna" "rna" "protein"

您可以使用 c() 函数向向量中添加其他元素:

R

weight_g <- c(weight_g, 90) # add to the end of the vector
weight_g <- c(30, weight_g) # add to the beginning of the vector
weight_g

输出

[1] 30 50 60 65 82 90

在第一行中,我们取原向量 weight_g,在其末尾添加值 90,并将结果保存回 weight_g。然后,我们在开头添加值 30,同样将结果保存回 weight_g。

我们可以重复此操作来扩展向量,或构建数据集。在编程过程中,这对于添加我们正在收集或计算的结果会很有用。

原子向量是最简单的 R 数据类型,是单一类型的线性向量。上面我们看到了 R 使用的 6 种主要 原子向量 类型中的 2 种:"character" 和 "numeric"(或 "double")。这些是所有 R 对象构建的基本构建块。另外 4 种 原子向量 类型为:

  • "logical" 用于 TRUE 和 FALSE(布尔数据类型)
  • "integer" 表示整数(例如 2L,其中 L 告知 R 这是一个整数)
  • "complex" 用于表示具有实部和虚部的复数(例如 1 + 4i),关于它们我们仅此而已。
  • "raw" 用于后续不讨论的位流

您可以使用 typeof() 函数并将向量作为参数输入,来检查向量的类型。

向量是 R 使用的众多数据结构之一。其他重要的数据结构还包括列表 (list)、矩阵 (matrix)、数据框 (data.frame)、因子 (factor) 和数组 (array)。

练习

挑战:

我们已经看到,原子向量可以是字符型、数值型(或 double 型)、整数型和逻辑型。但如果我们尝试在同一个向量中混合使用这些类型,会发生什么呢?

R 会隐式地将它们全部转换为同一种类型

练习

挑战:

在这些示例中会发生什么?(提示:使用 class() 检查对象的数据类型,并输入其名称以查看结果):

R

num_char <- c(1, 2, 3, "a")
num_logical <- c(1, 2, 3, TRUE, FALSE)
char_logical <- c("a", "b", "c", TRUE)
tricky <- c(1, 2, 3, "4")

R

class(num_char)

输出

[1] "character"

R

num_char

输出

[1] "1" "2" "3" "a"

R

class(num_logical)

输出

[1] "numeric"

R

num_logical

输出

[1] 1 2 3 1 0

R

class(char_logical)

输出

[1] "character"

R

char_logical

输出

[1] "a"    "b"    "c"    "TRUE"

R

class(tricky)

输出

[1] "character"

R

tricky

输出

[1] "1" "2" "3" "4"
练习

挑战:

你认为这是为什么会发生?

向量只能具有一种数据类型。R 会尝试转换(强制转换)该向量的内容,以找到一种不会丢失任何信息的共同类型。

练习

挑战:

在以下示例中,combined_logical 中有多少个值是 "TRUE"(作为字符):

R

num_logical <- c(1, 2, 3, TRUE)
char_logical <- c("a", "b", "c", TRUE)
combined_logical <- c(num_logical, char_logical)

只有一个。系统不记忆之前的数据类型,强制转换发生在向量首次求值时。因此,num_logical 中的 TRUE 在被 combined_logical 转换为 "1" 之前,会先被转换为 1。

R

combined_logical

输出

[1] "1"    "2"    "3"    "1"    "a"    "b"    "c"    "TRUE"
练习

挑战:

在 R 中,我们将对象从一个类转换为另一个类的过程称为强制转换。这些转换遵循一种层级结构,其中某些类型会优先被强制转换为其他类型。你能绘制一张图表来展示这些数据类型强制转换的层级关系吗?

逻辑 → 数值 → 字符 ← 逻辑

向量子集提取


如果我们想从向量中提取一个或多个值,必须在方括号内提供一个或多个索引。例如:

R

molecules <- c("dna", "rna", "peptide", "protein")
molecules[2]

输出

[1] "rna"

R

molecules[c(3, 2)]

输出

[1] "peptide" "rna"    

我们还可以重复索引,以创建一个比原对象包含更多元素的对象:

R

more_molecules <- molecules[c(1, 2, 3, 2, 1, 4)]
more_molecules

输出

[1] "dna"     "rna"     "peptide" "rna"     "dna"     "protein"

R 的索引从 1 开始。Fortran、MATLAB、Julia 和 R 等编程语言从 1 开始计数,因为这是人类的常规做法。C 语言家族(包括 C++、Java、Perl 和 Python)从 0 开始计数,因为这对计算机来说更简单。

最后,还可以使用负索引来获取向量中除某些指定元素之外的所有元素:

R

molecules ## all molecules

输出

[1] "dna"     "rna"     "peptide" "protein"

R

molecules[-1] ## all but the first one

输出

[1] "rna"     "peptide" "protein"

R

molecules[-c(1, 3)] ## all but 1st/3rd ones

输出

[1] "rna"     "protein"

R

molecules[c(-1, -3)] ## all but 1st/3rd ones

输出

[1] "rna"     "protein"

条件子集选取


另一种常见的子集选取方式是使用逻辑向量。TRUE 将选取索引相同的元素,而 FALSE 则不会:

R

weight_g <- c(21, 34, 39, 54, 55)
weight_g[c(TRUE, FALSE, TRUE, TRUE, FALSE)]

输出

[1] 21 39 54

通常,这些逻辑向量并非手动输入,而是其他函数或逻辑测试的输出结果。例如,如果你想仅选择大于 50 的值:

R

## will return logicals with TRUE for the indices that meet
## the condition
weight_g > 50

输出

[1] FALSE FALSE FALSE  TRUE  TRUE

R

## so we can use this to select only the values above 50
weight_g[weight_g > 50]

输出

[1] 54 55

您可以使用 &(两个条件均为真,AND)或 |(至少有一个条件为真,OR)来组合多个测试:

R

weight_g[weight_g < 30 | weight_g > 50]

输出

[1] 21 54 55

R

weight_g[weight_g >= 30 & weight_g == 21]

输出

numeric(0)

这里,< 表示“小于”,> 表示“大于”,>= 表示“大于或等于”,== 表示“等于”。双等号 == 用于测试左侧和右侧的数值是否相等,不应与单个 = 符号混淆,后者用于变量赋值(类似于 <-)。

一个常见的任务是在向量中搜索特定字符串。可以使用“或”运算符 | 来检查是否等于多个值,但这很快会变得繁琐。函数 %in% 允许你检查搜索向量的任何元素是否被找到:

R

molecules <- c("dna", "rna", "protein", "peptide")
molecules[molecules == "rna" | molecules == "dna"] # returns both rna and dna

输出

[1] "dna" "rna"

R

molecules %in% c("rna", "dna", "metabolite", "peptide", "glycerol")

输出

[1]  TRUE  TRUE FALSE  TRUE

R

molecules[molecules %in% c("rna", "dna", "metabolite", "peptide", "glycerol")]

输出

[1] "dna"     "rna"     "peptide"
练习

挑战:

你能想出为什么 "four" > "five" 会返回 TRUE 吗?

R

"four" > "five"

输出

[1] TRUE

在对字符串使用 > 或 < 时,R 会比较它们的字母顺序。这里,"four" 排在 "five" 之后,因此大于它。

名称


可以为向量的每个元素命名。下面的代码块展示了一个最初没有名称的向量,以及如何设置和检索名称。

R

x <- c(1, 5, 3, 5, 10)
names(x) ## no names

输出

NULL

R

names(x) <- c("A", "B", "C", "D", "E")
names(x) ## now we have names

输出

[1] "A" "B" "C" "D" "E"

当向量带有名称时,除了通过索引外,还可以通过名称访问其元素。

R

x[c(1, 3)]

输出

A C
1 3 

R

x[c("A", "C")]

输出

A C
1 3 

缺失数据


由于 R 旨在分析数据集,因此它包含了缺失数据的概念(这在其他编程语言中并不常见)。在向量中,缺失数据用 NA 表示。

在对数值进行运算时,如果处理的数据中包含缺失值,大多数函数都会返回 NA。这一特性使得人们更难以忽略处理缺失数据的情况。您可以添加参数 na.rm = TRUE,以在忽略缺失值的情况下计算结果。

R

heights <- c(2, 4, 4, NA, 6)
mean(heights)

输出

[1] NA

R

max(heights)

输出

[1] NA

R

mean(heights, na.rm = TRUE)

输出

[1] 4

R

max(heights, na.rm = TRUE)

输出

[1] 6

如果您的数据包含缺失值,您可能需要熟悉 is.na()、na.omit() 和 complete.cases() 这几个函数。示例请参阅下文。

R

## Extract those elements which are not missing values.
heights[!is.na(heights)]

输出

[1] 2 4 4 6

R

## Returns the object with incomplete cases removed.
## The returned object is an atomic vector of type `"numeric"`
## (or `"double"`).
na.omit(heights)

输出

[1] 2 4 4 6
attr(,"na.action")
[1] 4
attr(,"class")
[1] "omit"

R

## Extract those elements which are complete cases.
## The returned object is an atomic vector of type `"numeric"`
## (or `"double"`).
heights[complete.cases(heights)]

输出

[1] 2 4 4 6
练习

挑战:

  1. 使用这组以英寸为单位的身高向量,创建一个去除 NA 值的新向量。

R

heights <- c(63, 69, 60, 65, NA, 68, 61, 70, 61, 59, 64, 69, 63, 63, NA, 72, 65, 64, 70, 63, 65)
  1. 使用函数 median() 计算 heights 向量的中位数。
  2. 使用 R 计算该集合中身高超过 67 英寸的人数。

R

heights_no_na <- heights[!is.na(heights)]
## or
heights_no_na <- na.omit(heights)

R

median(heights, na.rm = TRUE)

输出

[1] 64

R

heights_above_67 <- heights_no_na[heights_no_na > 67]
length(heights_above_67)

输出

[1] 6

生成向量


构造函数

有一些函数可用于生成不同类型的向量。若要生成数值向量,可以使用 numeric() 构造函数,并将输出向量的长度作为参数提供。其值将被初始化为 0。

R

numeric(3)

输出

[1] 0 0 0

R

numeric(10)

输出

 [1] 0 0 0 0 0 0 0 0 0 0

请注意,如果我们请求一个长度为 0 的数值向量,我们将恰好得到该向量:

R

numeric(0)

输出

numeric(0)

针对字符和逻辑值也有类似的构造函数,分别命名为 character() 和 logical()。

练习

挑战:

字符向量和逻辑向量的默认值是什么?

R

character(2) ## the empty character

输出

[1] "" ""

R

logical(2)   ## FALSE

输出

[1] FALSE FALSE

复制元素

rep 函数可以将某个值重复指定次数。例如,如果我们想初始化一个长度为 5 且值为 -1 的数值向量,可以这样做:

R

rep(-1, 5)

输出

[1] -1 -1 -1 -1 -1

同样地,若要生成一个包含缺失值的向量(这通常是一个很好的起点,且无需对待采集的数据设定任何假设):

R

rep(NA, 5)

输出

[1] NA NA NA NA NA

rep 可以接收任意长度的向量作为输入(上述示例中使用了长度为 1 的向量),且支持任何类型。例如,如果我们想将值 1、2 和 3 重复五次,可以这样做:

R

rep(c(1, 2, 3), 5)

输出

 [1] 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
练习

挑战:

如果我们想将值 1、2 和 3 重复五次,但希望按此顺序获得五个 1、五个 2 和五个 3 呢?有两种可能 —— 请参阅 ?rep 或 ?sort 以获取帮助。

R

rep(c(1, 2, 3), each = 5)

输出

 [1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3

R

sort(rep(c(1, 2, 3), 5))

输出

 [1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3

序列生成

另一个非常有用的函数是 seq,用于生成数值序列。例如,若要生成一个从 1 到 20 且步长为 2 的整数序列,可以使用:

R

seq(from = 1, to = 20, by = 2)

输出

 [1]  1  3  5  7  9 11 13 15 17 19

by 的默认值为 1,且考虑到经常需要生成由一个值到另一个值、步长为 1 的序列,有一个快捷方法:

R

seq(1, 5, 1)

输出

[1] 1 2 3 4 5

R

seq(1, 5) ## default by

输出

[1] 1 2 3 4 5

R

1:5

输出

[1] 1 2 3 4 5

若要生成一个从 1 到 20 且最终长度为 3 的数列,可以使用:

R

seq(from = 1, to = 20, length.out = 3)

输出

[1]  1.0 10.5 20.0

随机样本与置换

最后一组实用函数是用于生成随机数据的函数。第一个函数 sample 用于生成另一个向量的随机排列。例如,若要为 10 名学生的口试抽取随机顺序,我首先为每名学生分配一个 1 到 10 的编号(例如根据姓名的字母顺序),然后:

R

sample(1:10)

输出

 [1]  9  4  7  1  2  5  3 10  6  8

如果不提供进一步参数,sample 将返回该向量所有元素的排列。如果我想获取特定大小的随机样本,我会将此值设为第二个参数。下面,我从预定义的 letters 向量中随机抽取 5 个字母:

R

sample(letters, 5)

输出

[1] "s" "a" "u" "x" "j"

如果我想要的输出比输入向量更大,或者能够多次抽取某些元素,我就需要将 replace 参数设置为 TRUE:

R

sample(1:5, 10, replace = TRUE)

输出

 [1] 2 1 5 5 1 1 5 5 2 2
练习

挑战:

在尝试上述函数时,你会发现样本确实是随机的,且不会两次获得相同的排列。为了能够复现这些随机抽取,可以在抽取随机样本之前,使用 set.seed() 手动设置随机数生成的种子。

与邻座一起测试此功能。首先独立抽取 1:10 的两个随机排列,并观察到结果不同。

现在使用例如 set.seed(123) 设置种子,并重复随机抽取。观察到现在得到的随机抽取结果相同。

通过设置不同的种子重复此操作。

不同的排列

R

sample(1:10)

输出

 [1]  9  1  4  3  6  2  5  8 10  7

R

sample(1:10)

输出

 [1]  4  9  7  6  1 10  8  3  2  5

种子为 123 时的相同置换

R

set.seed(123)
sample(1:10)

输出

 [1]  3 10  2  8  6  9  1  7  5  4

R

set.seed(123)
sample(1:10)

输出

 [1]  3 10  2  8  6  9  1  7  5  4

不同的种子

R

set.seed(1)
sample(1:10)

输出

 [1]  9  4  7  1  2  5  3 10  6  8

R

set.seed(1)
sample(1:10)

输出

 [1]  9  4  7  1  2  5  3 10  6  8

从正态分布中抽取样本

我们要看到的最后一个函数是 rnorm,它可以从正态分布中抽取随机样本。下面展示了两个均值为 0 和 100、标准差为 1 和 5 的正态分布,分别记作 N(0, 1) 和 N(100, 5)。

Two figures illustraing normal distributonis N(0,1) and N(100,5).
Two normal distributions: N(0, 1) on the left and N(100, 5) on the right.

这三个参数 n、mean 和 sd 分别定义了样本大小以及正态分布的参数,即均值及其标准差。后者的默认值为 0 和 1。

R

rnorm(5)

输出

[1]  0.69641761  0.05351568 -1.31028350 -2.12306606 -0.20807859

R

rnorm(5, 2, 2)

输出

[1]  1.3744268 -0.1164714  2.8344472  1.3690969  3.6510983

R

rnorm(5, 100, 5)

输出

[1] 106.45636  96.87448  95.62427 100.71678 107.12595

既然我们已经学会了如何编写脚本以及 R 数据结构的基础知识,现在可以开始处理更大规模的数据,并学习数据框了。

要点
  • 如何与 R 交互