3.4 其他通用类

除了我们已知的三个数字类和逻辑类,向量还有其他三个类,它们分别是:用于存储文本的字符character,存储类别数据的因子factor,以及较罕见的存储二进制数据的原始值raw

在下例中,正如之前创建数字向量一样,我们使用c运算符创建了一个字符向量。字符向量的类是character

  1. class(c("she", "sells", "seashells", "on", "the", "sea", "shore"))
  2. ## [1] "character"

请注意,和某些语言不同,R不区分整个字符串和单个字符——只包含一个字符的字符串与其他字符串的处理相同。与一些低级语言不同,你无需用空字符(\0)来终止字符串。事实上,把这样一个字符加进字符串中也是错误的。

在许多编程语言中,类别数据用整数表示。例如,gender中用1来代表female,2代表male。稍好的办法是把gender当作带有“female”和“male”选项的字符变量。然而,因为类别数据与传统的纯文本是不同的概念,所以从语义上看这仍然不妥。R找到了一种更有效的方法,把这两种方法整合到一个语义正确的类里面——因子(factor),即拥有标签的整数:

  1. (gender <- factor(c("male", "female", "female", "male", "female")))
  2. ## [1] male female female male female
  3. ## Levels: female male

因子的内容看起来与它们所对应的字符一样——这样每个值都能得到一个可读性很好的标签。这些标签被限制在称为因子水平(levels of the factor)的特定值中(本例中为“female”和“male”):

  1. levels(gender)
  2. ## [1] "female" "male"
  3. nlevels(gender)
  4. ## [1] 2

请注意,即使“male”是gender中的第一个值,第一个水平仍是“female”。默认情况下,因子水平按字母顺序分配。

在底层,因子的值被存储为整数而非字符。你可通过调用as.integer清楚地看到:

  1. as.integer(gender)
  2. ## [1] 2 1 1 2 1

采取整数而非字符文本的存储方式,令内存的使用非常高效,尤其当出现大量重复字符串时。如果我们再夸张一点,生成10 000个随机的gender值(使用sample函数对字符串“female”和“male”随机采样10 000次并使用replace选项),可以看到,一个因子包含的值比同等字符占用更少的内存。在以下代码中,sample返回一个字符向量(这是使用as.factor转换而成的),而object.size则返回每个对象的内存分配大小:

  1. gender_char <- sample(c("female", "male"), 10000, replace = TRUE)
  2. gender_fac <- as.factor(gender_char)
  3. object.size(gender_char)
  4. ## 80136 bytes
  5. object.size(gender_fac)
  6. ## 40512 bytes

注意

32位和64位系统中变量所占用的内存数量是不一样的,所以在不同情况下object.size将返回不同的值。

当操作因子水平的内容时(常见的例子是:清理命名,把所有的男性字符统一为“male”而非“Male”),最好先把因子转换成字符串后再处理,以便充分利用字符串操作函数。可以使用as.character函数完成转换:

  1. as.character(gender)
  2. ## [1] "male" "female" "female" "male" "female"

更多有关字符向量和因子的内容将在第7章中深入探讨。

原始类raw存储向量的“原始”字节2。每个字节由一个两位的十六进制值表示。它们主要用于保存输入的二进制文件的内容,因而比较少见。使用as.raw函数可把0到255之间的整数转换为原始值(raw)。此范围之外的数字将全部视为0,分数和虚部也被丢弃。对于字符串,as.raw则不起作用,而须使用charToRaw函数:

2 不确定是什么字节。

  1. as.raw(1:17)
  2. ## [1] 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11
  3. as.raw(c(pi, 1 + 1i, -1, 256))
  4. ## Warning: imaginary parts discarded in coercion
  5. ## Warning: out-of-range values treated as 0 in coercion to raw
  6. ## [1] 03 01 00 00
  7. (sushi <- charToRaw("Fish!"))
  8. ## [1] 46 69 73 68 21
  9. class(sushi)
  10. ## [1] "raw"

除了目前我们已了解到的向量类,还有许多其他类型的变量。我们将在接下来的章节中继续讨论它们。

数组包含多维数据,矩阵(通过matrix类)是特殊的二维数组,这些将在第4章中讨论。

目前,所有这些变量类型都要包含相同类型的值。例如,字符(character)向量或数组里须包含字符串,逻辑(logical)向量或数组须只能包含逻辑值。但列表(list)则不一样,它比较灵活,列表里的每一项都可以是不同的类型,甚至能包含其他列表。数据框(data frame)像是矩阵和列表的共同产物。它既像矩阵一样是矩形的,又像列表一样,每一列都可以有不同的类型。它们非常适合于存储类似电子表格这样的数据。第5章将继续讨论列表和数据框。

前面的类都用于存储数据。环境(environment)中会存储那些能保存数据的变量。很显然,除了保存数据之外,我们也需要函数来和它一起工作,如之前提过的函数sinexp。事实上,像+这样的操作符也是隐藏的函数!第6章将进一步谈及环境和函数。

第7章将深入讨论字符串和因子,以及用于存储日期和时间的一些选项。

R中还有一些稍难理解的类型,留待后文讨论。第15章将讨论公式(formuale);16.5节将讨论调用(call)和表达式(expression);16.6节则将进一步探讨类。