《R的极客理想—工具篇》—— 1.6 R和JSON的傻瓜式编程

本节书摘来自华章出版社《R的极客理想—工具篇》一 书中的第1章,第1.6节,作者:张丹,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.6 R和JSON的傻瓜式编程

问题
如何让R语言的数据类型转换成JSON数据类型?
screenshot

引言
JSON作为一种轻量级数据格式,被大量地应用在各种程序环境中。JSON(JavaScript Object Notation)是JavaScript的内嵌的标准对象,同时也是MongoDB的表结构存储类型。JSON是半结构化的,可以表达出丰富的文档含义。JSON文档比XML文档要少很多,更适合于网络传输。早期R语言编程很少会用到JSON,但随着R语言的壮大,R也在伸向各种领域,JSON就是与其他领域的一个交点。如何让R语言傻瓜式转型JSON呢?请看下文介绍。

1.6.1 rjson包介绍

rjson是一个R语言与JSON进行转换的包,非常简单,支持R自身语言转型和基于C类库的转型两种方式。rjson包提供的函数只有3个,fromJSON(), newJSONParser(), toJSON()。 后面我们将介绍如何使用rjson包。本节使用的系统环境是:
Windows 7: x86_64-w64-mingw32/x64 (64-bit)
R: version 3.0.1
注 rjson同时支持Windows 7环境和Linux环境。

  1. 安装并加载rjson
> install.packages("rjson")  # 安装rjson
> library(rjson)  # 加载rjson

接下来,我们进行测试。新建JSON文件fin0.json:

~ vi fin0.json

{
    "table1": {
        "time": "130911",
        "data": {
            "code": [
                "TF1312",
                "TF1403",
                "TF1406"
            ],
            "rt_time": [
                130911,
                130911,
                130911
            ]
        }
    },
    "table2": {
        "time": "130911",
        "data": {
            "contract": [
                "TF1312",
                "TF1312",
                "TF1403"
            ],
            "jtid": [
                99,
                65,
                21
            ]
        }
    }
}
  1. 调用函数fromJSON(): 从JSON到R
    从fin0.json文件中,读取JSON并解析成R语言对象。我们通常把字节或者文字格式转型为程序对象的过程叫反序列化过程,与之相反的过程,叫做序列化过程。
> json_data <- fromJSON(paste(readLines("fin0.json"), collapse=""))
> json_data
$table1
$table1$time
[1] "130911"

$table1$data
$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

$table1$data$rt_time
[1] 130911 130911 130911

$table2
$table2$time
[1] "130911"

$table2$data
$table2$data$contract
[1] "TF1312" "TF1312" "TF1403"

$table2$data$jtid
[1] 99 65 21
检查转型后,对应R语言的数据类型:
> class(json_data)
[1] "list"
> class(json_data$table2)
[1] "list"
> class(json_data$table2$data)
[1] "list"
> class(json_data$table2$data$jtid)
[1] "numeric"
> class(json_data$table1$data$code)
[1] "character"

我们看到原JSON对象转型后,除最内层外,其他都被解析为R的列表(list)类型,最内层则是基本(numeric,character)类型。在R对象结构中取JSON数据的一个叶子节点,JSON的索引路径为 json.table1.data.code[0]。

> json_data$table1$data$code
[1] "TF1312" "TF1403" "TF1406"
> json_data$table1$data$code[1]
[1] "TF1312"
  1. toJSON() 从R到JSON
    把R对象转型为JSON串,这个过程叫做序列化过程。还以刚才的json_data为例。
> json_str<-toJSON(json_data)

> print(json_str)
[1] "{\"table1\":{\"time\":\"130911\",\"data\":{\"code\":[\"TF1312\",\"TF1403\",
\"TF1406\"],\"rt_time\":[130911,130911,130911]}},\"table2\":{\"time\":\"130911\"
,\"data\":{\"contract\":[\"TF1312\",\"TF1312\",\"TF1403\"],\"jt
id\":[99,65,21]}}}"

> cat(json_str)
{"table1":{"time":"130911","data":{"code":["TF1312","TF1403","TF1406"],"rt_time"
:[130911,130911,130911]}},"table2":{"time":"130911","data":{"contract":["TF1312",
"TF1312","TF1403"],"jtid":[99,65,21]}}}

我们只要使用toJSON()函数,就可以实现R对象向JSON的转型。如果用print()函数输出,结果就是带转义的输出(");如果直接用cat函数输出,结果就是标准的JSON串格式。把JSON输出到文件fin0_out.json, 有2种方法,即writeLines()和sink()。

> writeLines(json_str, "fin0_out1.json")  # writeLines方法
> sink("fin0_out2.json")  # sink方法
> cat(json_str)
> sink()

虽然写法不同,但是输出结果是一个样的,writeLines最后新建一个空行。

{"table1":{"time":"130911","data":{"code":["TF1312","TF1403","TF1406"],"rt_time":
[130911,130911,130911]}},"table2":{"time":"130911","data":{"contract":["TF1312",
"TF1312","TF1403"],"jtid":[99,65,21]}}}
  1. C语言库和R语言库转型,并进行性能测试

我们对fromJSON进行性能测试:

system.time( y <- fromJSON(json_str,method="C") )

用户 系统 流逝
0 0 0

system.time( y2 <- fromJSON(json_str,method = "R") )

用户 系统 流逝
0.02 0.00 0.02

system.time( y3 <- fromJSON(json_str) )

用户 系统 流逝
0 0 0


我们可以看到,基于C语言库的操作比基于R语言库的要快,因为数据量很小,所以0.02并不明显。当JSON串很大的时候,这个差距就会变得相当大了。fromJSON默认使用C语言库的方法,所以我们平时处理不用加method='C'的参数。下面做toJSON的性能测试。

system.time( y <- toJSON(json_data,method="C") )

用户 系统 流逝
0 0 0

system.time( y2 <- toJSON(json_data,method = "R") )

用户 系统 流逝
0.02 0.00 0.01

system.time( y3 <- toJSON(json_data) )

用户 系统 流逝
0 0 0

解释同前。
####1.6.2 RJSONIO包介绍
RJSONIO包,提供了2个主要的操作,把JSON串反序列化成R对象,把R对象序列化成JSON串。RJSONIO包的两个主要函数是fromJSON(), toJSON(),它还包括几个辅助函数,即asJSVars(), basicJSONHandler(), Bob(), isValidJSON(), readJSONStream()。RJSONIO包解决了rjson包序列化大对象慢的问题。RJSONIO依赖于底层的C语言类库libjson。
  1. 安装并加载RJSONIO

install.packages("RJSONIO")
library(RJSONIO)

  1. fromJSON() 从JSON到R
    同rjson一样,测试fromJSON()函数。

json_data <- fromJSON(paste(readLines("fin0.json"), collapse=""))
json_data

$table1
$table1$time
[1] "130911"

$table1$data
$table1$data$code
[1] "TF1312" "TF1403" "TF1406"

$table1$data$rt_time
[1] 130911 130911 130911
$table2
$table2$time
[1] "130911"
$table2$data
$table2$data$contract
[1] "TF1312" "TF1312" "TF1403"
$table2$data$jtid
[1] 99 65 21


我们发现与 rjson的结果是一样,R对象除最内层外,其他都是列表(list)类型。下面取叶子节点:

json_data$table1$data$code

[1] "TF1312" "TF1403" "TF1406"

json_data$table1$data$code[1]

[1] "TF1312"


3. toJSON() 从R到JSON
做toJSON的性能测试:

json_str<-toJSON(json_data)
print(json_str)

[1] "{n "table1": {n "time": "130911",n"data": {n "code":
["TF1312", "TF1403", "TF1406" ],n"rt_time": [ 1.3091e+05, 1.3091e+05,
1.3091e+05 ] n} n},n"table2": {n "time": "130911",n"data": {n
"contract": [ "TF1312", "TF1312", "TF1403" ],n"jtid": [99,
65,21 ] n} n} n}"

cat(json_str)

{
"table1": {
"time": "130911",
"data": {
"code": [ "TF1312", "TF1403", "TF1406" ],
"rt_time": [ 1.3091e+05, 1.3091e+05, 1.3091e+05 ]
}
},
"table2": {
"time": "130911",
"data": {
"contract": [ "TF1312", "TF1312", "TF1403" ],
"jtid": [99,65,21 ]
}
}
}


toJSON的函数输出与rjson是不一样的,这个输出是格式化的。下面输出到文件:

writeLines(json_str, "fin0_io.json")

文件结果:
{
"table1": {
"time": "130911",
"data": {
"code": [ "TF1312", "TF1403", "TF1406" ],
"rt_time": [ 1.3091e+05, 1.3091e+05, 1.3091e+05 ]
}
},
"table2": {
"time": "130911",
"data": {
"contract": [ "TF1312", "TF1312", "TF1403" ],
"jtid": [ 99, 65, 21 ]
}
}
}


4. isValidJSON() 验证JSON是否合法
验证JSON的格式是否合法。

isValidJSON(json_str)

Error in file(con, "r") : cannot open the connection

isValidJSON(json_str,TRUE) #合法JSON

[1] TRUE

isValidJSON(I('{"foo" : "bar"}')) #合法JSON

[1] TRUE

isValidJSON(I('{foo : "bar"}')) #不合法JSON

[1] FALSE

  1. asJSVars() 转换为JavaScript变量格式

cat(asJSVars( a = 1:10, myMatrix = matrix(1:15, 3, 5)))
a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ;

myMatrix = [ [ 1, 4, 7, 10, 13 ],

       [ 2, 5, 8, 11, 14 ],
       [ 3, 6, 9, 12, 15 ] ] ;

得到两个JavaScript变量,即数组a和二维数组myMatrix.
####1.6.3 自定义JSON的实现
在R语言中,我们最常用的类型是data.frame,接下来我们自己实现data.frame类型和JSON的转换。首先,把R的data.frame对象转成我们定义的JSON格式。
定义输出JSON的格式:

[

{
    "code": "TF1312",
    "rt_time": "152929",
    "rt_latest": 93.76,
    "rt_bid1": 93.76,
    "rt_ask1": 90.76,
    "rt_bsize1": 2,
    "rt_asize1": 100,
    "optionValue": -0.4,
    "diffValue": 0.6
}

]


定义R语言data.frame类型对象:

df<-data.frame(
code=c('TF1312','TF1310','TF1313'),

rt_time=c("152929","152929","152929"),
rt_latest=c(93.76,93.76,93.76),
rt_bid1=c(93.76,93.76,93.76),
rt_ask1=c(90.76,90.76,90.76),
rt_bsize1=c(2,3,1),
rt_asize1=c(100,1,11),
optionValue=c(-0.4,0.2,-0.1),
diffValue=c(0.6,0.6,0.5)
)

df

code rt_time rt_latest rt_bid1 rt_ask1 rt_bsize1 rt_asize1 optionValue diffValue

1 TF1312 152929 93.76 93.76 90.76 2 100 -0.4 0.6

2 TF1310 152929 93.76 93.76 90.76 3 1 0.2 0.6
3 TF1313 152929 93.76 93.76 90.76 1 11 -0.1 0.5


直接使用toJSON,输出的JSON串按列组合成了数组,并不是我们想要的。

cat(toJSON(df))

{
"code": ["TF1312", "TF1310", "TF1313"],
"rt_time": ["152929", "152929", "152929"],
"rt_latest": [93.76, 93.76, 93.76],
"rt_bid1": [93.76, 93.76, 93.76],
"rt_ask1": [90.76, 90.76, 90.76],
"rt_bsize1": [2, 3, 1],
"rt_asize1": [100, 1, 11],
"optionValue": [-0.4, 0.2, -0.1],
"diffValue": [0.6, 0.6, 0.5]
}


我们要对data.frame类型进行数据处理:

library(plyr) #用plyr进行数据转换
cat(toJSON(unname(alply(df, 1, identity))))

[
{
"code": "TF1312",
"rt_time": "152929",
"rt_latest": 93.76,
"rt_bid1": 93.76,
"rt_ask1": 90.76,
"rt_bsize1": 2,
"rt_asize1": 100,
"optionValue": -0.4,
"diffValue": 0.6
},
{
"code": "TF1310",
"rt_time": "152929",
"rt_latest": 93.76,
"rt_bid1": 93.76,
"rt_ask1": 90.76,
"rt_bsize1": 3,
"rt_asize1": 1,
"optionValue": 0.2,
"diffValue": 0.6
},
{
"code": "TF1313",
"rt_time": "152929",
"rt_latest": 93.76,
"rt_bid1": 93.76,
"rt_ask1": 90.76,
"rt_bsize1": 1,
"rt_asize1": 11,
"optionValue":-0.1,
"diffValue": 0.5
}
]


输出的结果已经通过alply()函数做了数据变换,这正是我希望的按行输出的结果。
####1.6.4 JSON性能比较
性能比较我们做2组测试,一组是rjson和RJSONIO 对大对象进行序列化(toJSON)测试,另一组是RJSONIO包序列化(toJSON) 列式输出和行式输出的测试。
1. rjson和RJSONIO 对大对象进行序列化(toJSON)测试
创建一个rjson测试脚本,在命令行运行。

library(rjson)
df<-data.frame(

  • a=rep(letters,10000),
  • b=rnorm(260000),
  • c=as.factor(Sys.Date()-rep(1:260000))
  • )

system.time(rjson::toJSON(df))
1.01 0.02 1.03
system.time(rjson::toJSON(df))

1.01 0.03 1.04

system.time(rjson::toJSON(df))

0.98 0.05 1.03


同样,再创建一个RJSONIO测试脚本,在命令行运行。

library(RJSONIO)
df<-data.frame(

  • a=rep(letters,10000),
  • b=rnorm(260000),
  • c=as.factor(Sys.Date()-rep(1:260000))
  • )

system.time(RJSONIO::toJSON(df))
2.23 0.02 2.24
system.time(RJSONIO::toJSON(df))

2.30 0.00 2.29

system.time(RJSONIO::toJSON(df))

2.25 0.01 2.26


对比结果发现,rjson的性能优于RJSONIO。
2. rjson和RJSONIO,序列化(toJSON)列式输出和行式输出的测试
创建一个rjson测试脚本,在命令行运行。

library(rjson)
library(plyr)
df<-data.frame(

  • a=rep(letters,100),
  • b=rnorm(2600),
  • c=as.factor(Sys.Date()-rep(1:2600))
  • )

system.time(rjson::toJSON(df))
0.01 0.00 0.02
system.time(rjson::toJSON(df))

0.01 0.00 0.02

system.time(rjson::toJSON(unname(alply(df, 1, identity))))

1.55 0.02 1.56

system.time(rjson::toJSON(unname(alply(df, 1, identity))))

0.83 0.00 0.83


同样,再创建一个RJSONIO测试脚本,在命令行运行。

library(RJSONIO)
library(plyr)
df<-data.frame(

  • a=rep(letters,100),
  • b=rnorm(2600),
  • c=as.factor(Sys.Date()-rep(1:2600))
  • )

system.time(RJSONIO::toJSON(df))
0.03 0.00 0.03
system.time(RJSONIO::toJSON(df))

0.04 0.00 0.03

system.time(RJSONIO::toJSON(unname(alply(df, 1, identity))))

2.82 0.02 2.84

system.time(RJSONIO::toJSON(unname(alply(df, 1, identity))))

2.06 0.00 2.06

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值