在web上数据通常采用xml或json两种格式存放,因为这两类文件都使用了人类可以理解的数据格式,从程序开发的角度而言也非常容易处理,同时也适合处理任意类型的层次化数据结构,而不像csv文件一样仅能处理简单的表格数据。
鉴于json非常容易理解,因此可以在编译之前简单地浏览一下数据格式。下面可以使用rjson包将树状列表导入到r会话中:
这和之前我们曾经看见过的用逗号分隔的文件略有不同!再仔细分析一下文档,可以发现api终端返回的是元数据文件而非我们之前在csv文件中看到的原始表格数据。现在我们在浏览器中打开相关url来查看前5行中id为25ei-6bcr的数据内容:
当然在json结果链表中的结构已经发生了变化。下面,让我们将水平链表读入到r中:
还可以利用诸如视图、列等一些更详细的元数据信息来获取数据,这里面可能有一些我们现在不感兴趣的内容,因为fromjson返回的是一个list对象。从现在开始,我们可以直接删掉这些元数据,然后仅处理data行:
此时结果还是一个list对象,可以将它转换为data.frame类型。该list对象拥有5个元素,每个元素包括了19个嵌入的子节点。我们首先观察其中第13个子元素,是一个5-5的向量列表。这意味着树状list对象无法直接转换成表格数据,更不用说我们还发现其中某些向量的值是未经处理的json格式。因此,为了简单起见,同时也为了证明我们的观点,现在去掉与地址相关的值,然后将剩下的数据转换成data.frame格式:
我们应用了一个简单的函数去掉了表中每个元素的地址信息(移除了每个x的第13个元素),然后自动将其简化为matrix(通过使用sapply而非lapply对list中每个元素迭代处理),完成调换(通过t),再将结果强制转换为data.frame。
也可以使用之前介绍的一些方法,通过一些辅助函数而不用手动转换所有list元素。plyr包(请参考本书第3章和第4章获得更多细节)就提供了一些非常有用能够实现数据划分和组合的函数:
现在结果看起来熟悉一些了吧,尽管省略了变量的名字,而且所有值都被转换为字符向量或因子——尽管日期类型是以unix时间戳类型存放的。借助提供的元数据(res$meta),能够非常容易地解决这些难题。例如,可以通过抽取(通过[operator命令)除了已经被删掉的地址信息列13列之外的所有列的名字域:
也可以借助已经提供的元数据来确定对象的类别。例如,可以从域rendertypename着手,然后使用as.numeric处理数值型,使用as.posixct处理所有的calendar_date,就可以解决之前谈到的绝大部分问题了。
那么,你听过80%的数据分析时间是用在数据预处理过程上这一说法吗?
对json和xml进行解析和重构到data.frame对象会占用大量的时间,特别是在处理层次表时尤为突出。包jsonlite试图实现r对象到常规的json数据格式之间的转换而非原始处理来节约时间。从实际工作的角度来看,这意味着如果可能的话,从jsonlite::fromjson就能够得到data.frame结果而不是一堆原始list对象,实现了更好的无缝数据转换。不幸的是,我们并不是总能将list对象转换为表格式,此时,可以通过rlist包来加速list对象的转换。更多实现细节请参考本书第14章。
如图所示,api的xml输出与之前我们看到的json格式不太相同,它的输出仅包含我们感兴趣的行。这样,就能很简单地完成xml文档的分析,并且从中抽取出我们感兴趣的行并将其转换为data.frame:
可以通过修改传递给xmltodataframe函数中参数colclasses的值来手动确定变量的类型,就像在read.table函数里那样做一样,也可以通过一个快速的helper函数来解决这个问题:
当helper函数返回为true时,就可以验证我们对某个列仅包含数字的猜想,并将其转换为数值类型。请注意我们在将因子类型转换为数字时先要将其转换为字符类型,因为直接将因子转换为数值会返回因子的顺序而不是实际的数值。我们还可以通过type.convert函数来解决这个问题,read.table默认会采用这一方法。
现在,我们已经能够基于各种不同类型的数据下载格式完成数据处理,不过鉴于我们还必须掌握一些其他的数据源操作,我建议读者们继续往下接着学习。