向量的三大核心内容:
- 循环补齐
- 筛选
- 向量化
向量及其延伸
- 向量
单个数值(标量)没有单独的数据类型,是向量的一种特例
变量类型可以用typeof()
查询
typeof(c(1,2,3,4))
# [1] "double"
typeof(TRUE)
# [1] "logical"
typeof('hello world')
# [1] "character"
- 一个添加向量的例子
x <- c(1,3,4,6)
x <- c(x[1:3],5,x[4])
x
# [1] 1 3 4 5 6
这种方法实际上是创建了新的向量并存储到x,某些情况下,这种方式会限制R快速执行的能力
x本质是一个指针,重赋值是通过将x指向新向量的方式实现的
- 获取向量长度
x <- c(1,2,4)
length(x)
# [1] 3
- 矩阵和数组
二者包括列表本质上都是向量,只不过它们有额外的类属性
m <- matrix(c(1,2,3,4),nrow = 2); m
# [,1] [,2]
# [1,] 1 3
# [2,] 2 4
m + 11:14
# [,1] [,2]
# [1,] 12 16
# [2,] 14 18
声明
- 一元向量无需声明
z <- 3
- 多元向量创建不完全,必须先声明
y <- vector(length=2)
y[1] <- 5
y[2] <- 7
y
# [1] 5 7
- 多元向量可一次声明完成
y <- c(5,7)
实际就是先创建一个新向量然后bind给变量y
y可以先后绑定到不同模式的向量上,因为y是一个指针
循环补齐
矩阵是一个长向量
x <- matrix(c(1,2,3,4,5,6),nrow = 3);x
# 创建的时候是按列填充
# [,1] [,2]
# [1,] 1 4
# [2,] 2 5
# [3,] 3 6
x + c(1,2)
# 等价于x + 1:2,注意也是按列填补
# [,1] [,2]
# [1,] 2 6
# [2,] 4 6
# [3,] 4 8
向量运算
R是一种函数式语言,每一个运算符都是函数
2 + 3
# [1] 5
'+'(2,3)
# [1] 5
- 关于乘法
# 向量的内积,对应位置相乘
c(5,4,3) * c(1,2,3)
# [1] 5 8 9
# 向量的外积,线性代数
c(5,4,3) %*% c(1,2,3)
# [,1]
# [1,] 22
- 索引
- 索引重复是允许的
x <- c(6,8,4,5)
x[c(1,3,2,2)]
# [1] 6 4 8 8
- 负数索引是剔除
x[-length(x)]
# 等价于
x[1:length(x)-1]
- 用: 创建向量
要注意和()的优先级
i <- 3
1:i - 1
# [1] 0 1 2
1:(i - 1)
# [1] 1 2
# 查看优先级文档
?Syntax
- 用seq创建向量
seq(from=2,to=20,by=3)
# [1] 2 5 8 11 14 17 20
seq(from=2,to=20,length=10)
# [1] 2 4 6 8 10 12 14 16 18 20
# 不指定关键字时默认第三个参数是by
seq(2,20,3)
# [1] 2 5 8 11 14 17 20
# 只有一个参数默认是to,而from=1,by=1
seq(10)
# [1] 1 2 3 4 5 6 7 8 9 10
seq(0) # 特殊形式 依然from=1
# [1] 1 0
seq(NULL)
# integer(0)
# 如果为空值则无结果参数,在for (i in seq(x))中迭代0次
对迭代的进一步理解
for (i in seq(x))
当x非空时,1:length(x)和seq(x)是等价的
x <- c(2,20,3,4)
1:length(x)
# [1] 1 2 3 4
seq(x)
# [1] 1 2 3 4
- 用rep创建向量
x <- c(5,12,13)
rep(x,times=2) # 默认跟的是times参数
# [1] 5 12 13 5 12 13
rep(x,each=2)
# [1] 5 5 12 12 13 13
any()和all()
x <- 1:10
any(x > 8)
# [1] TRUE
all(x > 8)
# [1] FALSE
拓展案例一
—— 寻找连续出现1的游程
findruns <- function(x,k){
n <- length(x)
runs <- NULL
for (i in 1:(n-k+1)){
if (all(x[i:(i+k-1)])==1) runs <- c(runs,i)
}
return(runs)
}
x <- c(1,0,0,1,1,1,0,0,1,1)
findruns(x,2)
# [1] 4 5 9
findruns(x,6)
# NULL
上述代码调用c(runs,i)时给新的向量分配了内存空间,每次运行会减慢运行速度
优化:预先分配内存空间
findruns_new <- function(x,k){
n <- length(x)
runs <- vector(length=n)
count <- 0
for (i in 1:(n-k+1)){
if (all(x[i:(i+k-1)])==1){
count <- count + 1
runs[count] <- i
}
}
# 需要判断和切片
if (count > 0){
runs <- runs[1:count]
} else runs <- NULL
return(runs)
}
拓展案例二
—— 预测离散值时间序列
preda <- function(x,k){
n <- length(x)
# 过半数原则
k2 <- k/2
# 从第k+1天开始才能预测,故总长度是n-k
pred <- vector(length=n-k)
for (i in 1:(n-k)){
if (sum(x[i:(i+(k-1))]) >= k2) pred[i] <- 1 else pred[i] <- 0
}
# 计算预测值和真值的偏差
return(mean(abs(pred-x[(k+1):n])))
}
上述代码sum()的计算很没有效率,每次实际上只相差两个元素
优化:总和采取迭代更新策略而不是从头计算
predb <- function(x,k){
n <- length(x)
k2 <- k/2
pred <- vector(length=n-k)
sm <- sum(n[1:k])
# 需要对n-k=1的情况单独判断避免后续出错
if (sm >= k2) pred[1] <- 1 else pred[1] <- 0
if (n-k >= 2){
for (i in 2:(n-k)){
sm <- sm + x[i+k-1] - x[i-1]
if (sm >= k2) pred[i] <- 1 else pred[i] <- 0
}
}
return(mean(abs(pred-x[(k+1):n])))
}
优化:用窗口函数进一步优化代码
predc <- function(x,k){
n <- length(x)
k2 <- k/2
pred <- vector(length=n-k)
# 垫个0,后续循环不需要再判断
csx <- c(0,cumsum(x))
for (i in 1:(n-k)){
# 只需要相减一次
if (csx[i+k] - csx[i] >= k2) pred[i] <- 1 else pred[i] <- 0
}
return(mean(abs(pred-x[(k+1):n])))
}
向量化
- 向量输入,向量输出
- 如果一个函数使用了向量化的运算符,那么它也是向量化的
w <- function(x) return(x+1)
w(c(1,2,3))
# [1] 2 3 4
- 向量化过程中循环补齐起到关键作用
如果要限制c为标量:
test <- function(x,c){
if (length(c) != 1) stop('vector c not allow')
return((x+c)^2)
}
test(1:3,2)
# [1] 9 16 25
test(1:3,1:2)
# Error in test(1:3, 1:2) : vector c not allow
- 向量输入,矩阵输出
try(1:8)
# [1] 1 2 3 4 5 6 7 8 1 4 9 16 25 36 49 64
# 纵向填充
matrix(try(1:8),ncol=2)
# [,1] [,2]
# [1,] 1 1
# [2,] 2 4
# [3,] 3 9
# [4,] 4 16
# [5,] 5 25
# [6,] 6 36
# [7,] 7 49
# [8,] 8 64
sapply(1:8,try)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
# [1,] 1 2 3 4 5 6 7 8
# [2,] 1 4 9 16 25 36 49 64
sapply(x,func)
可以对x的每一个元素使用函数func,并将结果转化为矩阵,且从结果上看非纵向
NA和NULL
- NA
- NA 是缺失值,未知数
- NULL是控制,非数
x <- c(4,6,7,NA,10)
mean(x)
# [1] NA
mean(x,na.rm = T)
# [1] 6.75
x <- c(4,6,7,NULL,10)
mean(x)
# [1] 6.75
NA的mode跟随向量和其他元素
y1 <- c(1,3,NA)
y2 <- c('hello','world',NA)
mode(y1[2])
# [1] "numeric"
mode(y1[3]) # NA
# [1] "numeric"
mode(y2[2])
# [1] "character"
mode(y2[3]) # NA
# [1] "character"
- NULL
NULL是不存在的数
,不会被计数和参与计算
- NULL可以用作迭代创建向量
z <- NULL
for (i in 1:10) if (i %% 2 == 0) z <- c(z,i)
z
# [1] 2 4 6 8 10
# 如果是NA则会产生多余的NA
z <- NA
for (i in 1:10) if (i %% 2 == 0) z <- c(z,i)
z
# [1] NA 2 4 6 8 10
另一个例子印证NULL不存在
x1 <- NULL
length(x1)
# [1] 0
x2 <- NA
length(x2)
# [1] 1
筛选
- 利用索引
# 利用bool筛选
z <- c(2,4,-3,8)
y <- c(1,2,30,5)
y[z^2 > 8]
# [1] 2 30 5
# 赋值
x <- c(1,3,8,2,20)
x[x > 3] <- 0
x
# [1] 1 3 0 2 0
- 利用subset
x <- c(6,1:3,NA,12)
x
# [1] 6 1 2 3 NA 12
x[x>5]
# [1] 6 NA 12
subset(x,x>5)
# [1] 6 12
subset筛选会滤除NA,而索引筛选会保留NA
- 利用which
which 函数返回满足条件的元素位置
x
# [1] 6 1 2 3 NA 12
which(x^2 > 5)
# [1] 1 4 6
x[which(x^2 > 5)]
# [1] 6 3 12
- 找出向量中第1个
1
的位置
first1 <- function(x){
for (i in seq(x)){
if (x[i] == 1) break
}
return(i)
}
# 也可以用which解决
first1a <- function(x) return(which(x == 1)[1])
# 但这种方法会找出x所有的1再返回第1个,实际上可能要慢
ifelse函数
本质是一个向量化语句,运行速度快
x <- 1:10
ifelse(x > 5,'high','low')
# [1] "low" "low" "low" "low" "low" "high" "high" "high" "high" "high"
# 获得形参的名字和功能
args(ifelse)
# function (test, yes, no)
# NULL
拓展案例三
—— 度量相关性
findud <- function(v){
vud <- v[-1] - v[-length(v)]
# 错位相减 也可以写成 (x - dplyr::lag(x))[-1]
# 更简单的就是用diff(x)
return(ifelse(vud > 0, 1, -1))
}
udcorr <- function(x,y){
ud <- lapply(list(x,y), findud)
return(mean(ud[[1]]==ud[[2]]))
}
优化:简化两个函数
# sign函数可以用于判断符号
udcorr <- function(x,y) mean(sign(diff(x)) == sign(diff(y)))
拓展案例四
—— 对鲍鱼数据集重新编码
abalone <- c('M','F','F','I','M','M','F')
# I为infant,指无性幼虫
- 嵌套ifelse
ifelse(abalone == 'M',1,ifelse(abalone == 'F',2,3))
# [1] 1 2 2 3 1 1 2
- 将结果存到list
grps <- list()
# 注意列表的存储方式
for (sex in c('M','F','I')) grps[[sex]] <- which(abalone == sex)
grps
# $M
# [1] 1 5 6
#
# $F
# [1] 2 3 7
#
# $I
# [1] 4
# 也可以用apply
try <- function(x) return(which(abalone == x))
lapply(c('M','F','I'), try)
# [[1]]
# [1] 1 5 6
#
# [[2]]
# [1] 2 3 7
#
# [[3]]
# [1] 4
比较向量相同
== 是一个向量化的函数,不能用于比较向量二者相同
c(1,3,4) == 1:3
# [1] TRUE FALSE FALSE
- 比较相同一种方法是利用==向量化的本质结合all
all(c(1,3,4) == 1:3)
# [1] FALSE
- 利用identical
identical(c(1,3,4),1:3)
# [1] FALSE
identical是极度严格的比较,需要完全相同
x <- 1:3
y <- c(1,2,3)
x
# [1] 1 2 3
y
# [1] 1 2 3
all(x==y)
# [1] TRUE
identical(x,y)
# [1] FALSE
typeof(x)
# [1] "integer"
typeof(y)
# [1] "double"
直接由:创建的数值向量是整型
由c()创建的非:数值向量是浮点型
由c()创建的含:数值向量是整型含: 的数值向量默认是整型
向量命名
x <- 1:3
x
# [1] 1 2 3
names(x) <- c('a','b','c')
x
# a b c
# 1 2 3
# 命名后可以用名字取子集
x['a']
# a
# 1
x[c('a','c')]
# a c
# 1 3
# 置空向量命名
names(x) <- NULL
x
# [1] 1 2 3