R语言:从数据思维到数据实战
上QQ阅读APP看书,第一时间看更新

2.1.1 基本数据类型

第1章介绍了R语言的概貌,下面将以一个简单的电影票房实际数据为例,介绍在实际数据处理中R语言的基本类型和基本操作。

1.热门电影数据集简介

在电影的宣传期,往往能看到其主演、导演频频现身各大头条,吸引看客眼球,最后的落脚点往往是“祝×××电影票房大卖”。虽然观影习惯已经开始慢慢养成,电影的方方面面也成为人们茶余饭后的谈资,但是否想过通过数据的形式统计一下电影的基本信息呢?比如,本月上映了几部电影,动作戏偏多还是喜剧为主,主演是不是当红花旦等,这些信息都可以通过简单的R语言操作来一一获得。这里收集了2016年1—5月间上映的19部热门电影共10个变量的基本信息,我们将以此为例说明如何在R语言中进行相关操作。

数据来源于中国电影发行放映协会(http://www.chinafilm.org.cn)、豆瓣电影(https://movie.douban.com)、百度指数(http://index.baidu.com)等网站(见图2-1),图2- 2和图2-3展示了电影《火锅英雄》的一些基本情况。

picture

图2-1 中国电影发行放映协会电影月度上映数据

picture

图2-2 电影《火锅英雄》豆瓣主页

picture

图2-3 《火锅英雄》主演百度指数

详细的数据变量说明如表2-1所示。

picture

表2-1 数据变量说明

部分数据示例如表2-2所示。

picture

表2-2 数据示例

当把表2-2这个数据集第一次读入R中时,它会以数据框(data.frame)的形式存储,我们把这个数据框命名为movie.。数据框是类似于excel里常见的表格一样的对象,具体的定义和内容后面介绍。下面就从这个简单的数据集出发,依次介绍R中的各种数据类型。

2.基本数据类型介绍

(1)数值型(numeric)。数值型变量很简单,统计教材中的定量数据就是R中的数值型数据。比如数据集中的doubanscore,boxoffice等就是这类数据。通常,当用符号“<-”或者“=”给一个变量赋予数字时,就默认生成数值型数据[1]

picture

这种数据类型虽然看似简单,但不可大意,有时会出现状况。比如,下面几个命令会输出什么结果呢?

picture

答案如下:

picture

出现这种结果,其实是因为数值类型中还包含几种特殊情况:正无穷(Inf)、负无穷(-Inf)以及NaN即非数值(Not a Number)。R会把所有超过电脑存储限制的数字当作正无穷,一般来说这个限制大约为1.8×1038。一旦算式中有正无穷或者负无穷的子项出现,结果就很可能是无穷或者NaN型的数。比如上面命令的第三项,两个指数相除并不会给出exp(10)的答案,而是NaN,这一点一定要多加留意。

(2)字符型(character)。字符型变量从字面上很好理解,就是用来储存文字的,比如数据集中的director,actor1就是这种类型,然而未必都是如此。首先,不是文字的也可能是字符型,比如:

picture

其次,文字也许并不是字符型。比如前面提到的电影数据集中,name,type等变量看起来都是文字,但如果导入R时不加特别设置,很可能会得到如下结果:

picture

以为是字符型的,其实它是因子(因子类型,后面详解)。

字符型数据到底是什么呢?首先,简单来说,用单引号或双引号定义的就是字符型(注意是英文格式的引号),所以,在不是文字的也可能是字符型的例子中由于2上面加了双引号,所以它就被R识别为字符型,而不是数值;其次,大多数情况下,文字就是字符型数据,但是当一串文字被放在一个数据框中读进R时,它就极易被自动转换为因子型数据。混淆数据类型会带来什么问题呢?它们的区别主要在数据运算、字符存储上。如感兴趣,不妨试试在R中输入"1"+"1",看看得到什么结果,体会一下其中的区别。所以,看到大数据表(数据框)中的文字时一定要注意,它很可能默默地欺骗了你的眼睛。

(3)逻辑型(logical)。逻辑型数据取值很有限,只有TRUE和FALSE两个值,但它的作用却不可小觑。它常常出现在各种条件设定语句中,比如if条件语句中,或是在选取某些符合条件的数值时,都暗含着逻辑型数据的产生。最简单的,当进行一个条件判断时,就会产生逻辑型的数据结果。另外,逻辑型的结果还可以进行加减运算,原因就是TRUE在R中对应数字1,FALSE对应数字0,因此TRUE和FALSE两个值便可类似数字进行加减计算。

picture

(4)因子型(factor)。

1)什么是因子型数据?因子型数据通常用命令factor( )来定义。

picture

这里,因子型数据转换为字符型数据分为两步:第一步区分字符有几类,形成类型到整数的映射;第二步将原字符按照整数形式存储。具体过程如图2-4所示。

picture

图2-4 字符型数据存储过程

或许你会问,这个因子存储的不就是名义型变量吗?那举一反三,可不可以存储有序型变量呢?答案是肯定的。有序型变量就是带有顺序的名义型变量,因此只需把factor( )中的ordered参数设置好就可以了。

picture

仔细观察有序型因子和普通因子类型的区别,可以发现levels的显示等级有顺序了,这时它内部的存储方式是1=Excellent,2=Improved,3=Poor。那么电脑怎么知道我们想要的排列逻辑是什么样的呢?没错,电脑并不知道,它只是按照默认的字母顺序创建,这里仅仅是因为首字母顺序恰好与我们的逻辑顺序相同而已。

2)如何改变因子型数据各水平的编码顺序?如果按照字母排的顺序不是我们想要的逻辑顺序怎么办呢?同样好解决,只要设置factor(   )的levels参数即可。仔细思考,什么时候会用到因子但又不满意因子水平的排列顺序呢?举个例子,画分组箱线图时,可能会发现几个箱子的排列顺序不对劲或者不满意,这其实就是因为因子的水平定义顺序不当,这时就可以通过levels参数改变因子水平编码方式,从而让分组箱线图按照设想排列好。

picture

3)如何将因子型和字符型数据互相转换?前面提到,当读入一个数据表格(数据框)时,如果不做任何处理,软件会自动把字符型变量变成因子型变量。如果我们需要自己操作,如何才能实现字符型和因子型数据的自由转换?转换后对象所需内存有何变化?又是在什么情况下需要把字符变成因子型呢?

首先是字符型和因子型数据的自由转换,秘诀是一类as.函数。as.factor(  )可以把其他类型数据转换成因子型; as.character(   )可以把其他类型数据转换成字符型。另外,is.类函数可以查看数据类型是不是你想要的那种,仔细研读以下代码:

picture

其次是切换后对象大小的变化。一般来说,如果字符串包含的水平较少(比如男、女),那么因子型数据会比字符本身更节省空间,但如果字符串包含的水平很多(比如电影名称),转换成因子型数据反而会占用更多空间(在数据量较大时尤其严重)。如感兴趣,可采用object.size( )函数进行观察。

最后是什么时候需要把字符型数据转换成因子型数据。前面提到,R中的“因子”实际对应的是定性和定序变量,因此如果需要这两种类型的变量出现,就可以考虑把字符型变成因子型。比如,在作图中需要对数据分组,用来分组的变量就应该变成因子型;需要做包含定性变量的回归模型,定性变量就要变成因子型进入模型……这些都是因子型数据的用武之地。

(5)时间型数据(Date/POSIXct/POSIXlt)。实际上,时间型数据并不是一种单独的数据类型,然而在很多实践项目中,时间型数据曝光率极高。

通常,时间型数据是以字符串形式输入R中的,因此首先需要把这些字符转换成R可以识别的时间型数据。 R语言的基础包中提供了两种类型:一类是Date日期数据,它不包括时间和时区信息;另一类是POSIXct/ POSIXlt类型数据,其中包括日期、时间和时区信息。下面分别介绍这两类数据如何从字符转换过来以及后续可进行的操作。

1)将字符转换成Date日期格式。所谓Date日期数据,就是精确到日的时间形式。一般来说,用as.Date(   )函数转换时需要通过参数format指定输入字符的格式(包括年月日排列的顺序及表达方式),该函数默认可自动识别以斜杠(2017/12/23)和短横线(2017-12-23)相连接的年月日格式,并统一转换为以短横线连接的输出形式。例如对前面给出的数据集movie中的“showtime”进行转换,代码如下:

picture

如果对于特殊形式不指定format或者指定错误,R就会报错。表2-3 是format对应法则和一个小示例。

picture

表2-3 日期格式示意表
picture

小功课:如何将一个数字变成日期型数据?可参阅help(as.Date( )).

2)将字符转换成POSIXct/POSIXlt时间格式。所谓POSIXct/POSIXlt时间格式,其实就是精确到秒级的时间戳。当我们周围智能化设备、传感器越来越多时,很多数据都可以精确记录到秒级。一个典型的例子就是车载记录仪,它会每秒实时记录你开车行驶的速度、方向等信息,这样的数据就是每秒采集上传并记录的。那么对于这样的数据,该怎样转换呢?可以使用另一个类似的函数:as.POSIXct(  )。

这个函数的使用方法和as.Date(  )类似,同样需要定义好被转换字符的format才能被正确识别转换。与as.Date(  )相同的是,默认可以转换的格式仍然是2017/12/23 01:20:34或者2017-12-23 01:20:34这两种,其他格式都需要自行对照表2-3来具体指定格式,否则R语言就会“罢工”。

picture

3)将时间数据转换成你想要的形式。从前面内容可知,as.Date(  )和as.POSIXct(  )函数中的参数format并不能任意设置,只有输入与字符显示相匹配的格式才能有效识别转换。

如果想要其他格式输出的时间数据该如何操作呢?函数format(  ) 就可以用来更改时间数据的输出格式,甚至还可以提取你想要的一个部分。比如,如果想知道电影是什么月份哪一周上映,应该如何提取呢?下面分别以前面提到的movie数据集中的电影上映时间showtime、系统时间两种类型为例,见证一下format(  ) 的神奇力量。

picture

4)一款处理时间数据的专用包:lubridate。以上介绍的都是base基础包中自带的函数,下面再来介绍一款专门高效处理时间数据的包lubridG ate。这是一个实践中口碑极佳的数据预处理包。

lubridate包主要有两类函数:一类处理时点数据;另一类处理时段数据。它不仅功能强大,而且相应函数也很直观易懂,比如把字符转换成时间类型,根本不需要输入匹配的format参数;再如提取时间数据细节,也只是一个函数即可,不附带任何参数。下面的代码就是一个典型的例子,更多内容可见lubridate的帮助文档。

picture

5)时间型数据的操作。在字符型数据被转换成“正统”的时间型后,便可以进行后续的操作和建模了。以下介绍两类常见的基本操作。

①做差。如果想看两个日期之间相差多久,可以直接把两个数据做减法,也可以用difftime(  ) 函数提取。

picture

②排序。由于时间型数据本质上是用数值形式存储,因此它可以按类似数值方式进行排序。比如想看一下按照上映时间先后顺序排列的影片分别如何,可以参考对“单列时间数据”和“依照时间对整个数据表”进行排序的示范。

picture