4

介绍

现代化数据科学中的 DataFrame 概念源起R语言,而 Python Pandas 和 Spark DateFrame 都是参考R设计的。不过在实际的网络数据通讯中,类似DateFrame这样的格式却并不是主流,真正主流的方式其实是JSON(JavaScript Object Notation),所以讨论如何处理非结构化数据就变得非常有意义了。加之,近年来 Redis、MongoDB、ELK等非结构化数据库的繁荣,MySQL 5.7之后也已经添加了对JSON格式的原生支持(之前可以用blob、longtext等格式存储),非结构化数据更是在数据处理中变得流行。

本文将从 非结构化数据的转化、处理以及可视化三个方面讨论如何在R中操作非结构化数据。

数据清洗:JSON、List、DataFrame的三国杀

DataFrame 是R中的结构化数据结构,List 是R中的非结构化数据。JSON、List、DataFrame三者之间的互相转化是数据科学中非常频繁的一类操作。

在R中有一个非常有意思的现象,那就是处理json时,我们有三个选择,jsonlite、rjson以及RJSONIO,三者各有特点,有时为了处理一些问题还必须得混合使用。在实际处理字符串中,一定要注意的就是R中字符串的转义问题。比如\\表示\\"表示"等等。我曾经因为Python和R中的双层JSON解析多次遇到转义符号的问题。具体可以参看官方手册

jsonlite

jsonlite 是我最常用的一个json处理包,因为jsonlite可以一步将 json 转成 dataframe 再 从dataframe 转到 json,在数据处理中可以轻松解决常见的 json转化问题。此外,jsonlite 还完美支持utf-8,在 json 字符串错误时会有明显的错误提示。

jsonlite 的劣势是当出现双层 json 时,jsonlite 会将json转成dataframe格式的 list,这直接导致我们在用 length() 或者 dim() 求内层 JSON 的维度会出现错误。

jsonlite::fromJSON("{\"x\":\"量化投资\",\"y\":\"harryzhu\"}")
$x
[1] "量化投资"

$y
[1] "harryzhu"

rjson

rjson 和 jsonlite最大不同之处在于,rjson将json转化为一个list,而list是R语言中非结构化数据的事实标准,类似 python 中的 dict,或者 matlab 中的 cell。

值得注意的是,rjson在json转化中直接保持所有的浮点型数据,而jsonlite和RJSONIO则可以通过参数控制保留若干位小数的精度。

rjson::fromJSON("{\"x\":\"1\",\"y\":\"2\"}")
rjson::toJSON(pi)
$x
[1] "1"

$y
[1] 2

[1] "3.14159265358979"
jsonlite::fromJSON(pi,digit=4)
[1] "[ 3.142 ]"

RJSONIO

RJSONIO 允许传入没有转义符号的 JSON 字符串,并且支持将缺失值(NA)直接转成 Null,需要小心的是RJSONIO只支持unicode,如果传入utf-8则会酿成悲剧。

RJSONIO::fromJSON(RJSONIO::toJSON(c(1,2,NA,4)))
"[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
NULL

[[4]]
[1] 4"

更多细节可以参考 A biased comparsion of JSON packages in R

数据处理:List 处理

谈到list的处理就不得不谈一谈 rlist包。rlist包是任坤老师贡献到CRAN上的,任坤老师既是一个多产的R Developer (pipeR、formattable作者)也是一名量化投资者,目前在做私募方面的创业。想要学习rlist,我们可以参考一下任坤老师的演讲:跳出数据框,拥抱非结构化数据官方教程

rlist与高阶函数

rlist 是支持高阶函数表达式的,借鉴了Python、Scala等语言中的MapReduce模型,rlist也为list提供了map、filter、reduce、group、join、search、sort等高级数据操作,熟悉这些操作以后上手sparkR的RDD操作非常有帮助。

示例一:

利用 GitHub API,我们可以知道Hadley 的原创R语言开源项目中讨论议题数量最多的10个项目是哪些。

library(rlist)
library(pipeR)
# 分页读取数据
repos <-
"https://api.github.com/users/hadley/repos?per_page=100&page=%d" %>>%
sprintf(1:2) %>>%
list.load("json") %>>%
list.ungroup

repos %>>%
list.filter(has_issues, !fork, language == "R") %>>%
list.names(name) %>>%
list.mapv(open_issues) %>>%
list.sort(-.) %>>%
list.take(10)
# dplyr ggplot2 lubridate devtools staticdocs plyr
# 88 72 54 33 32 28
# stringr roxygen3 scales gtable
# 24 22 22 21

示例二:

批量读取非空 csv 文件并且合并成一个 data frame:

"data/" %>>%
list.files("\\.csv", full.names = TRUE) %>>%
list.filter(file.info(.)$size > 0) %>>%
list.map(read.csv(., header = TRUE, stringsAsFactors = FALSE)) %>>%
list.stack

rlist扩展包充分利用了R语言中list对象的特性,定义了一整套函数来帮助用户灵活快速地按要求处理各种非结构化数据,同时结合pipeR包中管道操作符的使用,使R程序更加具有可读性,应用更加人性化。

更多操作

下面是rlist中提供的操作:

分类 函数
映射(Mapping) list.map, list.mapv, list.select, list.iter, list.maps
筛选(Filtering) list.filter, list.find, list.findi, list.first, list.last,list.take, list.skip,list.takeWhile, list.skipWhile, list.is,list.which, list.all, list.any, list.count, list.match
更新(Updating) list.update
排序(Sorting) list.order, list.sort
分组(Grouping) list.group, list.ungroup, list.cases, list.class, list.common,list.table
合并(Joining) list.join, list.merge
搜索(Searching) list.search
数据读写(I/O) list.parse, list.load, list.save, list.serialize,list.unserialize
数据变换 list.append, list.prepend, list.reverse, list.zip, list.rbind,list.cbind, list.stack, list.flatten, list.names, list.sample

非结构化数据可视化

为了方便在R中可视化JSON数据,jsonview将js中的jsonviewer库引入到R中。

我们可以传入list或者json字符串做非结构化数据的可视化。

library(xmlview)
library(jsonlite)
devtools::install_github("hrbrmstr/jsonview")
jsonview::json_tree_view(fromJSON("https://collector.torproject.org/index.json"))

下面是timelyportfolio提供的一个结合shiny和jsonview的json编辑器的例子:

install.packages("shiny")
devtools::install_github("timelyportfolio/listviewer")
library(shiny)
library(listviewer)

# 数据准备
data(mtcars)

ui <- shinyUI(
  fluidPage(
    jsoneditOutput( "jsed" )
  )
)

server <- function(input,output){
  output$jsed <- renderJsonedit({
    jsonedit(
      as.list( .GlobalEnv )
      ,"change" = htmlwidgets::JS('function(){
        console.log( event.currentTarget.parentNode.editor.get() )
      }')
    )

  })
}

runApp( list( ui = ui, server = server ) )

接着,在浏览器中的对应端口可以打开这个json编辑器应用。

尾注

除了JSON之外,和NoSQL数据库的交互在大数据时代也成为了主流,混合使用Redis、Hive、MongoDB等数据库也成了家常便饭,具体操作可以翻看张丹老师的R利剑NoSQL系列文章

参考资料

作为分享主义者(sharism),本人所有互联网发布的图文均遵从CC版权,转载请保留作者信息并注明作者 Harry Zhu 的 FinanceR 专栏:https://segmentfault.com/blog...,如果涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio
商业使用请联系作者。

HarryZhu
2.2k 声望2.2k 粉丝