3.1 原子型向量
原子型向量就是最简单的包含数据的向量。其实,你已经见过它了,比如项目1所生成的die对象。在R中,可以将一组数据用c函数组合在一起,形成一个原子型向量。
die <- c(1, 2, 3, 4, 5, 6)
die
## 1 2 3 4 5 6
is.vector(die) ➊
## TRUE
➊ is.vector用来测试某个对象是否为原子型向量。如果是,is.vector将返回TRUE,如果不是则返回FALSE。
也可以生成只包含一个值的原子型向量。在这种情况下,R将这个单值存储为一个长度为1的原子型向量。
five <-5 five ## 5 is.vector(five) ## TRUE length(five) ## 1 length(die) ## 6
length
length函数返回原子型向量的长度。
每一个原子型向量都将其值存储在一个一维向量中,并且只能是一种类型的数据。可以使用R中其他不同类型的原子型向量存储不同类型的数据。概括来说,R可以识别六种基本类型的原子型向量,分别是:双整型(double)、整型(integer)、字符型(character)、逻辑型(logical)、复数类型(complex)以及原始类型(raw)。
为了生成一整副牌,你需要使用不同类型的原子型向量存储不同类型的信息(比如文字信息和数字信息)。这可以通过在键入数据时使用简单的R规范来实现。比如说,输入整数值后键入大写字母L就表示输入一个整型数值。如果要输入一个字符,只需给相应的字符加上双引号即可。
int <-1L text <- "ace"
每种原子型向量都有自己的书写规范(下面会介绍)。R会根据相应的规范识别你的输入,并将其定义为对应类型的原子型向量。如果想生成一个包含多个元素的原子型向量,可以利用第2章介绍过的c函数把元素组合起来。在组合时,要注意书写规范的一致性。
int <- c(1L, 5L) text <- c("ace", "hearts")
你可能会问:R为什么要使用好几种不同类型的向量呢?这是因为,每一种类型都帮助R定义了一个特定的对象,而不会有意外的情况发生。例如,针对包含数值的原子型向量,R可以进行运算,而针对包含字符型的原子型向量进行运算则是不合逻辑的。
sum(int)
## 6
sum(text)
## Error in sum(text) : invalid 'type' (character) of argument
接下来将介绍R中这六种原子型向量。
3.1.1 双整型
双整型向量用来存储普通的数值型数据。数值可正可负,可大可小,可包含小数部分,也可不包含。总之,你在R中键入的任何一个数值都会默认以双整型存储。例如,你在项目1中生成的die就是一个双整型向量。
die <- c(1, 2, 3, 4, 5, 6) die ## 1 2 3 4 5 6
知道自己在使用何种类型的R对象并不难(因为有些是显而易见的),但有一个直接的方法,就是使用typeof函数查看某个对象到底是什么类型,如下所示。
typeof(die)
## "double"
有一些函数把双整型称作数值型(numeric),在之后的章节中我也会用这个代称。双整型是来源于计算机科学的术语。它指的是计算机在存储一个数值时所设定的字节数,但是我个人觉得在数据科学中,“数值型”更符合我们的逻辑。
3.1.2 整型
整型向量用来存储整型的数据。顾名思义,整型数据的数值不需要小数点成分。作为数据科学家,你并不需要经常与整型打交道,因为整型数据也可以被存储为双整型。在R中,明确设定整型的方法是在该数值之后加上大写字母L,如下所示。
int <- c(-1L, 2L, 4L) int ## -1 2 4 typeof(int) ## "integer"
记住,如果不明确加上L, R并不会将一个数值设定为整型。未加L的整数将被存储为双整型。4和4L之间的唯一区别就在于它们的存储方式。在计算机内存中,整型的定义方式要比双整型更加精确(除非该整数非常大或者非常小)。
那么为什么要把数据存储为整型而不是双整型呢?因为有些时候,精度上的微小差距会产生令人意想不到的效果。在R程序中,计算机会给双整型对象分配64字节的内存。这使得计算机可以非常精确地表示数值,但是有些数值不能用64字节精确表示,也就是不能用64个1和0来表达。例如,圆周率π是无限不循环小数。你的计算机必须将圆周率舍入到小数点后某一位以便交给内存存储,还要保证舍进后的值与真实的圆周率非常接近。很多类似的小数都有这个问题。
这就导致每一个双整型的数值都只会大约精确到小数点后16位,这便会带来一点小误差。虽然在很多场合,这种舍入误差都不会被察觉到。但是,有时却会造成令人意想不到的后果。例如,你可能认为下面示例代码中的返回结果一定是0,其实不然。
sqrt(2)^2-2
## 4.440892e-16
根号2不能用16字节精确表示。因此,R必须进行一定量的舍入。上面表达式得到的结果非常接近零,但一定不等于零。
这样的舍入误差也叫作浮点(floating-point)误差,在这种情况下的运算称作浮点运算。浮点运算是计算机编程的一个特点,并非R所独有。通常来说,浮点运算并不会搅浑一锅好粥。但是应该记住,如果运算时出现了令人意外的结果,有可能与浮点运算有关。
为了避免浮点误差,可以限定只使用整型而不使用带有小数点的双整型。但是,这对于很多数据科学的应用场景来说不太现实。很多情况下,要想表达某个运算过程,仅仅使用整数是不够的。然而幸运的是,由浮点运算所导致的误差往往并不是非常显著(如果非常显著,也很容易看出来)。因此,从数据科学家的角度来看,大多数情况下你都会使用双整型,而不是整型。
3.1.3 字符型
字符型向量存储一小段文本。在R中,字符要加双引号,再组合起来构成一个字符型向量。
text <- c("Hello", "World") text ## "Hello" "World" typeof(text) ## "character" typeof("Hello") ## "character"
字符型向量中的单个元素称作字符串(string)。请注意,字符串不仅可以包含英文字母,也可以由数字或者符号组成。
练习
你能看出一个字符串与一个数值之间的区别吗?小测验:1、"1"和"one"中哪个是字符串,哪个是数值?
"1"和"one"都是字符串。字符串也可以包含数值型的字符,但这些数值字符并不是真正意义上的数值。它们本质上是字符串,只是字符串中包含的是一个数值元素而已。你可以区分字符串和数值,因为字符串总是被一对双引号所包围。在R中,任何加双引号的对象都会被当作字符串,无论双引号内是什么元素。
R中的字符串很容易与R对象混淆。为什么呢?因为在R代码中,字符串和R对象名看起来都只是一小段文本。举例来说,x是某个R对象“x”的名称,而"x"是某个包含字符“x”的字符串。这里,一个是包含原始数据的对象,而另一个则是原始数据本身。
在字符串键入和操作的时候,如果忘记键入双引号,就很容易导致程序出错。如果不加双引号,R很可能会去寻找一个不存在的R对象。
3.1.4 逻辑型
逻辑型向量用来存储TRUE(真)和FALSE(假),这是R中布尔数据的表现形式。在比对数据时,逻辑型会非常有用。
3 > 4 ## FALSE
只要在R中键入全部大写的TRUE或FALSE(不加双引号),就会被当作逻辑型数据。R也会默认把T和F分别当作TRUE和FALSE的简写。
logic <- c(TRUE, FALSE, TRUE) logic ## TRUE FALSE TRUE typeof(logic)
## "logical" typeof(F) ## "logical"
3.1.5 复数类型和原始类型
双整型、整型、字符型和逻辑型是原子型向量中最常见的类型,但是R其实还可以识别另外两种类型的原子型向量,它们是:复数类型和原始类型。在分析数据时,基本不会用到这两个类型,但是为了论述的完整性,我们还是在这里介绍一下。
复数类型向量用来存储复数。要生成一个复数类型向量,只需要将某个数字与带i的虚数项相加即可。
comp <- c(1 + 1i, 1 + 2i, 1 + 3i)
comp
## 1+1i 1+2i 1+3i
typeof(comp)
## "complex"
原始类型向量用来存储数据的原始字节。原始类型向量的生成较为复杂,但是如果要生成一个长度为n的空原始类型向量,可以用raw(n)。如果你正在处理这种类型的数据,不妨看一下raw函数的帮助页面,那里有更详尽的信息。
raw(3)
## 00 00 00
typeof(raw(3))
## "raw"
练习
生成一个原子型向量来存储皇家同花顺的牌面,例如,一张黑桃A、一张黑桃K、一张黑桃Q、一张黑桃J和一张黑桃10。在这一手牌当中,黑桃A的牌面是A(ace),花色是黑桃(spades)。
你会使用哪种类型的向量来存储这些名称信息呢?
字符型向量对于存储扑克牌的牌面名称信息应该最合适不过了。每张牌的牌面名称加双引号,再用c函数组合成一个向量,代码如下。
hand <- c("ace", "king", "queen", "jack", "ten")
hand
## "ace" "king" "queen" "jack" "ten"
typeof(hand)
## "character"
这样便生成了一组扑克牌的牌面名称,并将它们放在了一个一维向量中,干得漂亮!接下来,我们再生成一个结构更加复杂的数据:一个二维的表格型数据,其中既有扑克牌牌面的信息,也有花色的信息。要想生成这样结构更为复杂的数据对象,我们将以原子型向量为基础,赋予它一些属性(attribute)和类(class)。