R 语言入门 —— 基础语法
前言
R
是一种用于统计分析、图形展示和报告的编程语言。由 Ross Ihaka
和 Robert Gentleman
在 1990
年代早期在新西兰奥克兰大学开发,设计之初就作为统计数据分析和图形表示的工具。R
语言的灵感来自于 S
语言,后者是在贝尔实验室开发的一种先进的统计编程语言。R
继承了 S
语言的多种特性,并且以其开源的特点,在全球范围内迅速获得了广泛的应用和发展。
特点
- 强大的统计分析能力:
R
内置了广泛的统计分析功能,包括线性和非线性建模、统计测试、时间序列分析、分类、聚类等,并且还在不断扩展中。 - 丰富的图形和可视化工具:
R
提供了强大的图形绘制功能,能够生成高质量的图表和数据可视化,非常适合进行复杂数据的图形展示和分析报告。 - 扩展性和灵活性:
R
拥有一个庞大的社区,社区成员贡献了数以万计的额外软件包,用于各种专门的统计处理、图形工具和数据处理方法,几乎涵盖了所有的统计分析领域。 - 跨平台兼容性:
R
可以在UNIX
、Windows
和MacOS
等多种操作系统上运行,具有很好的可移植性。
应用领域
R
语言在学术界和业界都有广泛的应用,尤其是在以下领域:
- 统计分析:作为统计分析的强大工具,
R
被广泛用于数据挖掘、统计推断、概率分析等领域。 - 生物信息学:
R
在生物信息学和计算生物学中非常受欢迎,用于基因组数据分析、疾病模式识别等。 - 数据科学:随着数据科学的兴起,
R
成为机器学习、预测建模和数据可视化的重要工具。 - 金融分析:
R
也被用于金融领域,包括量化分析、风险管理、股票市场分析等。
学习和使用 R
R
语言的学习曲线相对平缓,对于初学者而言,入门相对容易,特别是对于那些已经具有统计学背景的用户。
R
语言的一个挑战在于需要掌握大量的额外软件包来充分利用其强大的功能。不过,得益于其活跃的社区和丰富的在线资源,用户可以较容易地找到学习材料和解决问题的帮助。
总的来说,R
语言以其在统计分析和图形表示方面的卓越性能,以及强大的用户社区支持,成为了数据分析师、统计学家和研究人员的重要工具。
基础语法
变量的概念和数学中常说的变量是类似的,即可以使用某一字符来代表任意且未知的值,可以通过变量名来访问和赋值。通常变量的值是可变的,其值可以是一个数、一个符号、一个对象。
变量命名规范
R
语言的变量名必须符合如下命名规则:
- 只能包含字母、数字、下划线(
_
) 及点(.
),并且只能以字母或.
开头,以.
开头的变量其后不能紧跟数字 - 大小写敏感,即
A
和a
是两个不同的变量 - 变量名最好不要与保留字一样,使用
?reserved
可以查看所有的保留字,所谓的保留字就是语言本身定义的用于表示特定功能名称,如if
、for
等
注意:如果变量名称不合法,可以使用点号(`)将非法变量名包裹起来,例如
`a-b` <- 123
`a-b`
# [1] 123
`1.aaa` <- 222
(`a b c` <- `1.aaa` - `a-b`)
# [1] 99
虽然不推荐这样使用,但是有时候读取数据时难免会遇到一些奇怪的命名方式,需要记住
仅仅只是定义符合命名规则的变量是不够的,变量的命名应该尽可能见名知意,不同类型的变量的命名方式要有所区别,更易分辨。虽不是强制,但为了方便他人阅读和自己 debug
,可以选择使用如下命名规范:
- 匈牙利命名法:以一个或者多个小写字母开头作为前缀,指示变量的类型;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途,如
fn_ShowTable()
- 驼峰命名法:混合大小写字母来命名变量和函数的名字,如
printPaychecks()
、print_Paychecks()
- 帕斯卡命名法:类似于驼峰法,只是首字母大写,如
UserInfo
当然可以根据自己的习惯,稍作修改和组合,好的命名方法胜过一大串注释。
注释
R
语言中,以符号 #
开头表示这行是注释,注释不会被编译器解析,且不影响代码的执行。
# 列出环境中的所有变量
ls()
如果要使用多行注释,可以使用多个 #
,也可以使用字符串的形式
"
第一行注释
第二行注释
"
ls()
为什么要有注释呢?注释的目的是为了让人更好的理解代码的逻辑、功能、使用方式等。适当的添加注释,能让代码更加清晰,便于阅读。
基本运算
R
中提供了很多基本运算符,这些运算符本质上也是一个个函数,都有对应的函数版本,该函数名称为使用反引号包裹的操作符,例如 ops
对应于 `ops`
-
赋值运算
运算符 描述 =
、<-
、<<-
向左侧变量赋值,需要赋值的变量放在左侧 ->
、->>
向右侧变量赋值,需要赋值的变量放在右侧
R
中的变量赋值方式有点特立独行,使用的是 <-
符号,类似箭头的符号,表示将右边的值交给左边的变量,当然使用 ->
符号也是允许的,只是变量名和数值的位置交换了。
a <- 1
2 -> b
c = 3
d <<- 4
assign('x', 1) # 赋值函数
`<-`('a', 2) # 使用反引号包裹操作符,可以当做函数来用
x
# [1] 1
a
# [1] 2
这三个赋值方式的区别我们后面会讲到
-
数学运算
运算符 描述 +
、-
、*
、/
加、减、乘、除 ^
乘方 %%
取余 %/%
整除 %*%
矩阵乘法 :
创建序列
1 + 2 * 3 / 2
# [1] 4
3 / 4
# [1] 0.75
3 %/% 4
# [1] 0
3 %% 2
# [1] 1
1 + 3 ^ (2 * 4 %/% 3)
# [1] 10
1:10
# [1] 1 2 3 4 5 6 7 8 9 10
`:`(1, 10)
# [1] 1 2 3 4 5 6 7 8 9 10
10:1
# [1] 10 9 8 7 6 5 4 3 2 1
-
关系运算
运算符 描述 >
左侧变量值是否大于右侧,如果是则返回 TRUE
,否则返回FALSE
<
左侧变量值是否小于右侧,如果是则返回 TRUE
,否则返回FALSE
>=
左侧变量值是否大于等于右侧,如果是则返回 TRUE
,否则返回FALSE
<=
左侧变量值是否小于等于右侧,如果是则返回 TRUE
,否则返回FALSE
==
判断左右两侧变量的值是否相等,如果是则返回 TRUE
,否则返回FALSE
!=
判断左右两侧变量的值是否不相等,如果是则返回 TRUE
,否则返回FALSE
R
中的关系运算符可直接比较两个向量对应元素大小关系
a <- 5:8
b <- 9:6
a > b
# [1] FALSE FALSE FALSE TRUE
a == b
# [1] FALSE FALSE TRUE FALSE
a <= b
# [1] TRUE TRUE TRUE FALSE
`<=`(a, b)
# [1] TRUE TRUE TRUE FALSE
-
逻辑运算
运算符 描述 &
依次对两个向量中对应元素进行与判断,两个都为真则结果为真,否则为假 ` ` &&
类似于 &
,但是只比较向量的第一个元素` !
对向量的每个元素进行取反操作 %in%
判断一个向量的元素是否在另一个向量中
a <- c(1, 0, TRUE, 1+3i)
b <- c(0, 0, 4, 3+4i)
a & b
# [1] FALSE FALSE TRUE TRUE
`&`(a, b)
# [1] FALSE FALSE TRUE TRUE
a | b
# [1] TRUE FALSE TRUE TRUE
!a
# [1] FALSE TRUE FALSE FALSE
`!`(a)
# [1] FALSE TRUE FALSE FALSE
a && b
# [1] FALSE
a || b
# [1] TRUE
1:3 %in% a
# [1] TRUE FALSE FALSE
`%in%`(1, 1:3)
# [1] TRUE
代码块
R
是一种表达式语言,每条代码语句都可以看作是一个表达式,一般每个表达式单独放在一行,如果要在一行内添加多个表达式,可以使用 ;
分隔。多个表达式可以使用 {}
组合在一起,作为一个代码块,代码块中的代码都是一起执行,并返回最后一个表达式的值。
{
a <- 2
b <- 3
a * b
}
# [1] 6
值得注意的是,当一个表达式也可以跨越多行,例如
a *
+ b
# [1] 6
分支判断
当我们观测的事件不可预测,但所有情况已知时,可以使用条件判断来分情况考虑。例如,明天如果下雨,我就待在家里读书;如果明天是大晴天,那我就去踢球。
R
提供了三种判断语句:
if
:单分支。例如,如果明天下雨,就带伞;
a <- 10
b <- 3
if (a > b) {
print(a)
}
# [1] 10
a <- 1
if (a > b) {
print(a)
}
# 无输出
if...else
:双分支,如果明天下雨,我就带伞,否则不带;
a <- 10
b <- 3
if (a > b) {
print(a)
} else {
print(b)
}
# [1] 10
a <- 1
if (a > b) {
print(a)
} else {
print(b)
}
# [1] 3
R
提供了一个更简单好用的判断函数:ifelse
ifelse(-5 > 2, "Yes", "NO")
# [1] "NO"
ifelse(-5:5 > 0, 0, -5:5)
# [1] -5 -4 -3 -2 -1 0 0 0 0 0 0
ifelse(-5:5 < 0, 0, -5:5)
# [1] 0 0 0 0 0 0 1 2 3 4 5
- 多级
if...else
: 多个if...else
联用可实现多分支功能
a <- 10
if (a < 0) {
print("负数")
} else if (a == 0) {
print("零")
} else {
print("正数")
}
# [1] "正数"
switch
:多分支,如果明天下雨、或阴天、或晴天、或下雪怎么办?
a <- 2
switch (a,
"First",
"Double",
"Triple"
)
# [1] "Double"
link <- "google"
switch (link,
google = "www.google.com",
taobao = "www.taobao.com",
weibo = "www.weibo.com"
)
# [1] "www.google.com"
循环控制
循环,就是要重复执行类似的操作,例如,计算一个向量内所有值的和,需要遍历向量的每个值。
R
提供了三种循环类型
for
:遍历向量while
:条件为真时一直执行repeat
:一直执行,直到跳出循环被触发
以及两种循环控制
next
:跳过本次循环break
:跳出循环
for
循环
count <- 0
for (i in 1:10) {
count <- count + i
if (i == 5) {
next
}
if (count > 20) {
break
}
}
i;
# [1] 6
# [1] 21
while
循环
while (count > 0) {
count <- count - 5
}
count
# [1] -4
repeat
循环
repeat {
count <- count + 1
if (count > 0) {
break
}
}
count
# [1] 1
repeat
就相当于 while (TRUE)
定义函数
函数是具有特定功能的代码块,我们通过将完成某一功能,或需要重复执行的代码封装起来,并为这些代码取一个函数名,函数包含函数名、输入参数、函数体以及返回值。
R
中的函数可以显式的使用 return
函数来返回值,默认会返回最后一个表达式的执行结果。
函数使用 function
关键字来定义
FunctionName <- function (arg1, arg2, ...) {
expr
return(value)
}
下面我们来定义几个函数
func1 <- function() {
print("Hello World!")
}
func2 <- function(a, b) {
a + b
}
# 默认参数
func3 <- function(a, b = 2, c = 3) {
return(a + b * c)
}
其中,参数可以设置默认值,而未设置默认值的参数在调用函数的时候必须要传递值。函数的参数默认按照顺序分配值,只有在以键值对的形式传递参数值时,才可以忽略参数的顺序。
# 调用函数
func1()
# [1] "Hello World!"
func2(1, 2)
# [1] 3
func3(1)
# [1] 7
func3(1, 4)
# [1] 13
func3(1, c = 4) # 为指定参数赋值
# [1] 9
惰性求值
惰性求值是 R
的一个关键特性,只有在程序调用该变量时才会对变量取值,例如
f <- function(a, b) {
cat("a:", a)
}
f(1)
# a: 1
f()
# Error in cat("a:", a): 缺少参数"a",也没有缺省值
可变参数
R
支持传入可变数量的参数,使用 ...
来表示输入的可变参数列表,可以直接使用 ..1
到 ..9
来获取可变参数的值,其中数值表示第几个参数。但最好还是将可变参数列表转换为 list
变量,并通过变量来访问。
func5 <- function(a, ...) {
args <- list(...)
print(paste("Args:", a))
for (i in args) {
print(i)
}
print(..2)
}
func5(1, m = 5, a = 10)
# [1] "Args: 10"
# [1] 1
# [1] 5
# [1] 5
匿名函数
顾名思义,匿名函数就是没有为函数定义名字,通常与一些将函数作为参数的函数联用,如 apply
函数族。
func4 <- function(a, f) {
# f 为函数
f(a)
}
func4(1:10, function(x){x * 2 + 1})
# [1] 3 5 7 9 11 13 15 17 19 21
(function(x) { x + 10 })(1:10)
# [1] 11 12 13 14 15 16 17 18 19 20
sapply(1:10, function (x) { x^2 })
# [1] 1 4 9 16 25 36 49 64 81 100
变量作用域
在一个程序中,并不是所有变量都可以访问的,有些变量是有访问权限的,即变量作用域。作用域主要分为
- 全局作用域
- 局部作用域
在 R
中,这些其实都是通过环境空间来管理的,针对初学者,我们只需了解一些简单的概念即可。我们更常见的是函数内的局部作用域,函数外的全局作用域,函数内部可以访问外部定义的变量,而函数内部定义的变量通常是无法在函数外访问的(->>
定义的变量除外)。
赋值方式的区别
前面我们提到了 R
中的三种赋值方式,在大部分情况下都是推荐使用 <-
来进行赋值,那 =
是不是在 R
中没用了呢?其实 =
与 ->
的效果基本上是一样的,它们之间的区别主要在于作用域的不同,看下面一个例子
rm(list = ls()) # 删除环境中的所有变量
mean(x = 1:10)
# [1] 5.5
x
# 错误: 找不到对象'x'
mean(x <- 1:10)
# [1] 5.5
x
# [1] 1 2 3 4 5 6 7 8 9 10
mean(x <- 1:10)
类似于下面的代码,但是略有不同
x = 1:10
mean(x)
我们定义一个简单的函数
say_hello <- function(x) {
print("Hello!")
}
say_hello(x <- "Tom")
# x: [1] 1 2 3 4 5 6 7 8 9 10
say_hello <- function(x) {
print(paste("Hello", x))
}
say_hello(x <- "Tom")
# x: [1] "Tom"
可以看到,<-
只有在用到的时候才会执行,当然,一般很少在参数中使用这种方式来赋值。
R
中第三种赋值方式 <<-
(或 ->>
),这种赋值方式是为了向上一层环境中写入变量,一般来说,函数是一个闭合的区域,在该区域内的变量只会在函数内发挥作用,在函数外部是不可见的,例如
rm(list = ls())
f <- function(x) {
a <- 1
b <<- 2
g <- function() {
d <<- 3
a <<- 10
b <- 12
}
g()
x <- x + 1
print(paste("a:", a, ", b:", b, ", d:", d))
}
f(x <- 10)
# [1] "a: 10 , b: 2 , d: 3"
ls()
# [1] "b" "d" "f" "x"
可以看到,变量 b
和 d
被写入最上层环境了,而 a
则只写入到了 f
函数层,a
和 d
的区别说明 <<-
会向上层寻找变量,如果没有找到会继续往上层查找,直到顶层全局环境。而函数内部使用 <-
无法改变全局变量的值(x
)或嵌套函数中上一层局部变量的值(b),只能在当前作用域发挥作用。
formula
R
语言中的 formula
对象可以理解为抽象的数学公式,通过 ~
符号分割自变量和因变量,通常应用于统计分析中,在绘图和矩阵数据处理时也会用到,有必要在这里介绍一下 formula
的一些概念,其基本操作符包括
操作符 | 描述 |
---|---|
~ | 分隔符号,自变量在左侧,因变量在右侧,例如线性回归模型:y ~ x + z + w |
+ | 分隔自变量 |
: | 表示交互项,即两个变量组合为一个新的变量,共同影响因变量,例如:y ~ x + z + x:z |
* | 表示所有可能的交互项,例如:y ~ x * z * w 表示 y ~ x + z + w + x:z + x:w + z:w + x:z:w |
^ | 指定交互项的度,即多少个变量之间的交互项,例如:y ~ (x + z + w)^2 表示 y ~ x + z + w + x:z + x:w + z:w |
. | 表示除因变量之外的所有变量,例如:一个包含 x 、y 、z 和 w 四个变量的数据框,使用 y ~ . 表示 y ~ x + z + w |
- | 表示从公式中移除某一变量,例如: y ~ (x + z + w)^2 - x:w 表示 y ~ x + z + w + x:z + z:w |
-1 | 删除截距,即去掉公式中的常数项,默认会自动添加常数项 |
I() | 将括号内的操作符解释为正常的数学公式,例如:y ~ x + I((z + w)^2) 将表示为 x + h ,h 为 z 和 w 的平方和 |
function | 对变量应用函数,例如 log(y) ~ x + z + w 表示用 x 、z 和 w 来预测 log(y) |
创建 formula
y ~ x + z
# 使用字符串创建
formula("y ~ x + z")
as.formula("y ~ x + z")
# y ~ x + z
f <- y ~ x + z
class(f)
# [1] "formula"
查看 formula
> all.vars(f) # 查看所有变量
[1] "y" "x" "z"
> terms(f) # 查看所有信息
y ~ x + z
attr(,"variables")
list(y, x, z)
attr(,"factors")
x z
y 0 0
x 1 0
z 0 1
attr(,"term.labels")
[1] "x" "z"
attr(,"order")
[1] 1 1
attr(,"intercept")
[1] 1
attr(,"response")
[1] 1
attr(,".Environment")
<environment: R_GlobalEnv>
修改 formula
update(f, ~ . + w)
# y ~ x + z + w
模块安装与导入
仅仅使用 R 语言自带的函数包是远远无法满足我们的需求的,我们通常需要从网站上下载安装第三方包,下面介绍几种安装包的方式
CRAN
安装
# CRAN 上的包只要传入包名即可下载
install.packages("packageName")
Bioconductor
安装
# 先安装 BiocManager,并使用该包的 install 函数来下载 Bioconductor 包
install.packages("BiocManager")
BiocManager::install("packageName")
GitHub
安装
# 先安装 devtools 包,并使用该包的 install_github 函数来下载 GitHub 上的包
install.packages("devtools")
devtools::install_github('git_path')
- 本地和
URL
安装
# 如果网络不好,可以下载包到本地进行安装
install.packages("path/packageName.tar.gz", repos = NULL)
# 或者,指定包的 URL 路径来下载安装
install.packages(packageurl, repos=NULL, type="source")
安装完第三方包之后,需要导入相应的包才能使用包中的函数,到如包的方式有两种
library
:如果导入的包不存在,会引发一个异常
library(ggplot2)
# 包不存在
library(ggplot3)
# Error in library(ggplot3) : 不存在叫‘ggplot3’这个名字的程辑包
require
:成功导入包会返回TRUE
,否则返回FALSE
,如果包不存在会产生一个警告
require(ggplot2)
#
require(ggplot3) == FALSE
# 载入需要的程辑包:ggplot3
# [1] TRUE
# Warning message:
# In library(package, lib.loc = lib.loc, character.only = TRUE, logical.return = TRUE, :
# 不存在叫‘ggplot3’这个名字的程辑包