美文网首页The Art for R Programming
[R语言] 《R语言编程艺术》 第2章 向量

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

作者: 半为花间酒 | 来源:发表于2020-04-21 09:16 被阅读0次

    向量的三大核心内容:

    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章 向量

        本文链接:https://www.haomeiwen.com/subject/ptubbhtx.html