4.2 向量

现在,你已经试过用冒号运算符来创建从某个数到另一个数的数字序列,以及用c函数来拼接数值和向量,以创建更长的向量。总结如下:

  1. 8.5:4.5 #从8.5到4.5的数字序列
  2. ## [1] 8.5 7.5 6.5 5.5 4.5
  3. c(1, 1:3, c(5, 8), 13) #不同值被拼接成单一向量
  4. ## [1] 1 1 2 3 5 8 13

vector函数能创建一个指定类型和长度的矢量。其结果中的值可为零、FALSE、空字符串,或任何相当于“什么都没有”(nothing)的类型:

  1. vector("numeric", 5)
  2. ## [1] 0 0 0 0 0
  3. vector("complex", 5)
  4. ## [1] 0+0i 0+0i 0+0i 0+0i 0+0i
  5. vector("logical", 5)
  6. ## [1] FALSE FALSE FALSE FALSE FALSE
  7. vector("character", 5)
  8. ## [1] "" "" "" "" ""
  9. vector("list", 5)
  10. ## [[1]]
  11. ## NULL
  12. ##
  13. ## [[2]]
  14. ## NULL
  15. ##
  16. ## [[3]]
  17. ## NULL
  18. ##
  19. ## [[4]]
  20. ## NULL
  21. ##
  22. ## [[5]]
  23. ## NULL

在上例中,NULL是一个特殊的“空”值(不要与代表缺失值的NA混淆)。第5章会详细讨论NULL。为方便起见,用每个类型的包装(wrapper)函数来创建矢量以节省打字时间。下列命令与上面代码中的命令是等价的:

  1. numeric(5)
  2. ## [1] 0 0 0 0 0
  3. complex(5)
  4. ## [1] 0+0i 0+0i 0+0i 0+0i 0+0i
  5. logical(5)
  6. ## [1] FALSE FALSE FALSE FALSE FALSE
  7. character(5)
  8. ## [1] "" "" "" "" ""

注意

在下一章中我们将看到,list函数的工作方式与它们不一样。list(5)创建的内容有所不同。

4.2.1 序列

除了冒号运算符之外,还有几个其他函数能创建更为通用的序列。其中,seq函数最常见,能以许多不同的方式指定序列。然而在实际中,你不需要调用它,因为有三个专门的序列函数,运行更快且更易用,满足了特定场合的使用。

seq.int可创建一个序列,序列的范围由两个数字指定。只需两个参数,原理与冒号运算符完全相同:

  1. seq.int(3, 12) #与3:12相同
  2. ## [1] 3 4 5 6 7 8 9 10 11 12

seq.int稍微比更通用些,因为它可以指定步长:

  1. seq.int(3, 12, 2)
  2. ## [1] 3 5 7 9 11
  3. seq.int(0.1, 0.01, -0.01)
  4. ## [1] 0.10 0.09 0.08 0.07 0.06 0.05 0.04 0.03 0.02 0.01

因为seq_len函数将创建一个从1到它的输入值的序列,所以用seq_len(5)来表达1:5有些笨拙。不过,当输入值为零时,该函数非常有用:

  1. n <- 0
  2. 1:n #和你预期的可能不一样!
  3. ## [1] 1 0
  4. seq_len(n)
  5. ## integer(0)

seq_along创建一个从1开始、长度为其输入值的序列:

  1. pp <- c("Peter", "Piper", "picked", "a", "peck", "of", "pickled", "peppers")
  2. for(i in seq_along(pp)) print(pp[i])
  3. ## [1] "Peter"
  4. ## [1] "Piper"
  5. ## [1] "picked"
  6. ## [1] "a"
  7. ## [1] "peck"
  8. ## [1] "of"
  9. ## [1] "pickled"
  10. ## [1] "peppers"

在以上的每一个例子中,你都可以简单地用seq替换seq.intseq_lenseq_along,得到的结果相同,虽然这样做并无必要。

4.2.2 长度

我们刚才已经悄悄地引入了一个关于向量的新概念:所有的向量都有一个长度,它告诉我们向量包含多少个元素。这是一个非负整数1(是的,向量的长度可以为零),你可以通过length函数查到这个值。缺失值也会被计入长度:

1 在32位的系统以及3.0.0之前版本的R中,长度被限制为2^31-1个元素。

  1. length(1:5)
  2. ## [1] 5
  3. length(c(TRUE, FALSE, NA))
  4. ## [1] 3

容易造成混淆的地方是字符向量。它们的长度为字符串的数目,而非每个字符串中字符数的长度。对此,我们使用nchar

  1. sn <- c("Sheena", "leads", "Sheila", "needs")
  2. length(sn)
  3. ## [1] 4
  4. nchar(sn)
  5. ## [1] 6 5 6 5

我们也可以为向量重新分配一个长度,不过很少这么做,且几近意味着糟糕的代码。例如,如果向量的长度缩短,那么后面的值将会被删除;而如果长度增加,则缺失值会添加到最后:

  1. poincare <- c(1, 0, 0, 0, 2, 0, 2, 0) #请参见:http://oeis.org/A051629
  2. length(poincare) <- 3
  3. poincare
  4. ## [1] 1 0 0
  5. length(poincare) <- 8
  6. poincare
  7. ## [1] 1 0 0 NA NA NA NA NA

4.2.3 命名

R向量的一大特性是能给每个元素命名。通常,标记元素可使代码可读性更强。你可以使用name = value的形式在创建向量时为其指定名称。如果元素的名称是有效的,则无需被引号括起来。你也可以只命名向量中的某些元素而忽略其他元素:

  1. c(apple = 1, banana = 2, "kiwi fruit" = 3, 4)
  2. ## apple banana kiwi fruit
  3. ## 1 2 3 4

你也可以在向量创建后用names函数为元素添加名字:

  1. x <- 1:4
  2. names(x) <- c("apple", "bananas", "kiwi fruit", "")
  3. x
  4. ## apple bananas kiwi fruit
  5. ## 1 2 3 4

name函数也可用于取得向量的名称:

  1. names(x)
  2. ## [1] "apple" "bananas" "kiwi fruit" ""

如果向量中没有一个元素有名字,则names函数返回NULL

  1. names(1:4)
  2. ## NULL

4.2.4 索引向量

通常,我们只要访问向量中的部分或个别元素。这就是所谓的索引,它用方括号[]来实现。(有人也称之为子集下标切片,这些术语所指相同。)R系统非常灵活,提供如下多种索引方法。

  • 给向量传入正数,它会返回此位置上的向量元素切片。它的第一个位置是1 (而不像其他某些语言一样是0) 。
  • 给向量传入负数,它会返回一个向量切片,它将包含除了这些位置以外的所有元素。
  • 给向量传入一个逻辑向量,它会返回一个向量切片,里面只包含索引为TRUE的元素。
  • 对于已命名的向量,给向量传入命名的字符向量,将会返回向量中包含这些名字的元素切片。

请看以下向量:

  1. x <- (1:5) ^ 2
  2. ## [1] 1 4 9 16 25

以下三个索引方法都将返回相同的值:

  1. x[c(1, 3, 5)]
  2. x[c(-2, -4)]
  3. x[c(TRUE, FALSE, TRUE, FALSE, TRUE)]
  4. ## [1] 1 9 25

如果给每个元素命名,以下方法也将返回相同的值:

  1. names(x) <- c("one", "four", "nine", "sixteen", "twenty five")
  2. x[c("one", "nine", "twenty five")]
  3. ## one nine twenty five
  4. ## 1 9 25

混合使用正负值是不允许的,会抛出一个错误:

  1. x[c(1, -1)] #这说不通!
  2. ## Error: only 0's may be mixed with negative subscripts

如果你使用正数或逻辑值作为下标,那么缺失索引所对应的值同样也是缺失值:

  1. x[c(1, NA, 5)]
  2. ## one <NA> twenty five
  3. ## 1 NA 25
  4. x[c(TRUE, FALSE, NA, FALSE, TRUE)]
  5. ## one <NA> twenty five
  6. ## 1 NA 25

对于负的下标值来说,不允许存在缺失值,会产生错误:

  1. x[c(-2, NA)] #这也讲不通!
  2. ## Error: only 0's may be mixed with negative subscripts

超出范围的下标值(即超出了矢量的长度)不会导致错误,而是返回缺失值NA。在实际中,最好确保你的下标值都在使用范围内:

  1. x[6]
  2. ## <NA>
  3. ## NA

非整数下标会默认向零舍入。这是另一个R被认为过于宽松的证例。如果你传入的下标是分数,那么很可能你正在写一些糟糕的代码:

  1. x[1.9] #1.9 舍入为 1
  2. ## one
  3. ## 1
  4. x[-1.9] #-1.9 舍入为-1
  5. ## four nine sixteen twenty five
  6. ## 4 9 16 25

不传递任何下标值将返回整个向量。不过再次提醒,如果你没有传递任何索引值,那么很可能你正在做一些不靠谱的事情:

  1. x[]
  2. ## one four nine sixteen twenty five
  3. ## 1 4 9 16 25

which函数将返回逻辑向量中为TRUE的位置。如果要将逻辑索引切换到整数索引中,这个函数很有用:

  1. which(x > 10)
  2. ## sixteen twenty five
  3. ## 4 5

which.minwhich.max分别是which(min(x))which(max(x))的简写:

  1. which.min(x)
  2. ## one
  3. ## 1
  4. which.max(x)
  5. ## twenty five
  6. ## 5

4.2.5 向量循环和重复

到目前为止,所有相加在一起的向量都具有相同的长度。你可能会问:“当我对不同长度的向量做运算,结果会如何?”

如果我们把一个单独的数字与向量相加,则向量的每个元素都会与该数字相加:

  1. 1:5 + 1
  2. ## [1] 2 3 4 5 6
  3. 1 + 1:5
  4. ## [1] 2 3 4 5 6

将两个向量相加时,R将会循环较短向量中的元素以配合较长的那个:

  1. 1:5 + 1:15
  2. ## [1] 2 4 6 8 10 7 9 11 13 15 12 14 16 18 20

如果长向量的长度不是短向量长度的倍数,将出现一个警告:

  1. 1:5 + 1:7
  2. ## Warning: longer object length is not a multiple of shorter object length
  3. ## [1] 2 4 6 8 10 7 9

必须强调的是,虽然我们可以在不同长度的向量之间做运算,但这并不意味着应该这样做。为向量添加一个标量值没有问题,但我们会在其他方面把自己搞晕。更好的做法是明确地创建同等长度的向量,然后再对它们进行操作。

rep函数非常适合此类任务,它允许我们重复使用元素来创建矢量:

  1. rep(1:5, 3)
  2. ## [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
  3. rep(1:5, each = 3)
  4. ## [1] 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
  5. rep(1:5, times = 1:5)
  6. ## [1] 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
  7. rep(1:5, length.out = 7)
  8. ## [1] 1 2 3 4 5 1 2

正如seq函数一样,rep有一个更为简单和快速的变体rep.int

  1. rep.int(1:5, 3) #与rep(1:5, 3)一样
  2. ## [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5

类似seq_len,在最近的R版本(从v3.0.0开始)中也有rep_len函数,可指定输出向量的长度:

  1. rep_len(1:5, 13)
  2. ## [1] 1 2 3 4 5 1 2 3 4 5 1 2 3