从这一期开始,小编打算从程序语言的角度来认真审视和研究一下R。系列名称就叫做R语言编程特性,从最基本的数据结构开始讲起,包括向量、矩阵、数组、列表、数据框和因子这六大数据类型,R的编程结构和面向对象编程、IO编程、R的性能以及并行计算等内容。
R虽是统计学家们开发和发展的,但其终究还是一门编程语言。既然是编程语言,那么R就有自身的语法风格和特性。作为R中最基本的数据类型,向量(vector)在R编程架构里占据了最底层也是最核心的位置。并且从广泛意义上的数据类型而言,R中的矩阵和数组甚至是列表都是向量。本文就以向量为主题,探索R向量的循环补齐、索引与取数以及向量化运算等特性。
在R中一切都是向量,这可能是有点夸张的说法了,但并不妨碍向量成为最基础和最值得我们重视的一种数据结构。对于一切都是向量的说法,我们可以这么理解:R中不存在标量(单个数值),所谓标量是以向量形式存在的,并且R中的矩阵、数组甚至是列表都可以看成是某种意义上的向量。
标量以向量的形式存在:
x <- 1
y <- c(1)
identical(x, y)
[1] TRUE
矩阵的向量本质:数据虽然以矩阵的形式存在,但本质仍然是向量,只不过是多了行和列这样的属性。
z <- matrix(1:6, nrow=3)
z + 3:8
[,1] [,2]
[1,] 4 10
[2,] 6 12
[3,] 8 14
在R中,我们可以使用简单的 c()
函数来创建一个向量,c 的本意即为concatenate(连接)。下面的代码中小编创建了一个包含五个元素的向量,并将其赋值给变量 x
,而 x
的本质是一个指针,通过这个指针可以对向量元素进行增删或者更改的操作来进行重赋值。
x <- c(18,34,49,52,81)
x
[1] 18 34 49 52 81
除了 c()
函数之外,我们还可以使用 :
运算符和 seq()
函数来创建向量:
1:8
[1] 1 2 3 4 5 6 7 8
:
运算符在for循环中可能会特别好用:
for (i in 1:length(x)){}
seq()
函通过生成序列来创建向量:
seq(10,20,2)
[1] 10 12 14 16 18 20
和大多数编译语言不同的是,我们在R中创建这个向量并将其赋值给 x
的过程中,并不需要对其进行事先声明,这是R和Python等脚本语言的一大特性。
Matlab:
>> syms a,b,x;
>> int(x^2,a,b)
C:
int x;
int y[1]
由于R没有需要事先声明的限制,在对创建出的向量进行绑定赋值时,其类型是不受限制的。我们可以在将 x
绑定到一个数值向量之后,继续再将其绑定到一个字符型向量上去。 这再次提醒我们:变量 x
只是一个指针,在不同的时间可以指向不同类型的变量。
x <- c(18,34,49,52,81)
x
[1] 18 34 49 52 81
x <- "louwill"
x
[1] "louwill"
在对两个长度不等的向量执行运算的时候,R会自动对长度较短的向量进行循环补齐,即对长度较短的向量元素进行重复,直至其与要进行运算的另一向量长度相匹配。
c(1,3,5) + c(2,4,6,8,10)
[1] 3 7 11 9 13
x <- c(8,2,3,9)
y <- c(2,7)
"*"(x,y)
[1] 16 14 6 63
下面用矩阵的计算例子来展示向量的循环补齐特性以及印证矩阵的向量本质。
x <- matrix(1:8, nrow=2)
x + c(1,2)
[,1] [,2] [,3] [,4]
[1,] 2 4 6 8
[2,] 4 6 8 10
在上面的代码中,我们可以看到矩阵的本质其实是一个长向量,将4x2的矩阵看成是一个包含八个元素的长向量,在R中按列存储后就有了矩阵的形态。在执行上述向量加法运算时,R会自动将 c(1,2)
进行循环补齐操作,直到将其补成同样是4x2的矩阵为止。
c(1,2,3,4,5,6,7,8)
与Python索引从0开始不同的是,R的索引从1开始,当然这并不碍事。R通过 []
符号和向量索引来访问向量中的元素:
x <- c(rnorm(3),runif(2))
x[c(1,3,5)]
[1] 0.4888147 -1.1989531 0.6171411
x[2:4]
[1] 1.9060878 -1.1989531 0.5635335
y <- 4:5
x[y]
[1] 0.5635335 0.6171411
也可以用负数下标来删除对应元素:
x <- c(2,5,8)
x[-3]
[1] 2 5
x[-1:-2]
[1] 8
R作为一门像电脑要内存的编程语言,从某种程度上而言是R饱受诟病的原因之一。因此坊间有个约定就是尽量少在R中使用循环,尤其是在数据量较大的情形下,据说效率的低下程度令人发指。改变并提升代码效率的有效途径便是向量化(vectorize),即将运算符或者函数作用在向量的每一个元素上的一种理念。
向量化运算符
x <- c(2,6,0)
y <- c(1,5,8)
x > y
[1] TRUE TRUE FALSE
z <- rnorm(5)
round(z)
[1] 0 -1 1 0 3
上面的代码例子是以向量形式作为结果输出的,下面再看一例运用向量化运算符并以矩阵形式输出结果:
xy <- function(x) return(c(x, x^2))
sapply(-3:3, xy)
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] -3 -2 -1 0 1 2 3
[2,] 9 4 1 0 1 4 9
向量化函数 在R中执行向量化运算的核心函数就是apply函数族,其中包括以 apply()
函数为基础,以 lapply()
和 sapply()
为核心函数的等8个向量化计算函数,运算例子如下:
e <- list(alpha=3:7,beta=exp(2:8),gamma=c(TRUE,FALSE,FALSE,TRUE))
lapply(e, fivenum)
$alpha
[1] 3 4 5 6 7
$beta
[1] 7.389056 37.341843 148.413159 750.030976 2980.957987
$gamma
[1] 0.0 0.0 0.5 1.0 1.0
sapply(e, fivenum,simplify=TRUE)
alpha beta gamma
[1,] 3 7.389056 0.0
[2,] 4 37.341843 0.0
[3,] 5 148.413159 0.5
[4,] 6 750.030976 1.0
[5,] 7 2980.957987 1.0
具体函数用法含义和区别可参见
参考资料:
R语言编程艺术
欢迎大家关注微信公众号:数萃大数据
深度学习培训班【上海站】
时间:2017年12月23-24日
地点:上海创梦云实训创新中心
更多详情,请扫描下面二维码