[R语言] 《R语言编程艺术》 第2章 向量

向量的三大核心内容:

  1. 循环补齐
  2. 筛选
  3. 向量化

向量及其延伸

- 向量

单个数值(标量)没有单独的数据类型,是向量的一种特例

变量类型可以用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

你可能感兴趣的:([R语言] 《R语言编程艺术》 第2章 向量)