recharts
是从Yihui Xie fork而来。它基于百度Echarts2的最后一个稳定发布版(v2.2.7)开发。本文档始终反映recharts最新的特性(Github)。基于Echarts3的recharts2包仍在开发中。
安装方法:
if (!require(devtools)) library(devtools)
install_github("madlogos/recharts")
recharts是一个用于可视化的R加载包,它提供了一套面向JavaScript库ECharts2的接口。此包的目的是让R用户即便不精通HTML或JavaScript,也能用很少的代码做出Echarts交互图——当然,懂一点JavaScript的话会更如虎添翼。下面这个散点图展示了本包的基本语法:
library(recharts)
echartr(iris, Sepal.Length, Sepal.Width, series = Species)
通过
browseVignettes("recharts")
可以离线查看本手册。
recharts建基于htmlwidgets包之上,这样做的优点是极大地节省了开发者管理JavaScript依赖包和处理不同类型的输出文档(如R Markdown和Shiny)的时间。你只需要创建一幅图,而如何输出这幅图(无论R Markdown, Shiny, 还是R控制台/ RStudio)则交由htmlwidgets来处理。
此包的主函数是echartr()
和S3通用函数echart()
。在设计宗旨上,我们希望它们能自动处理不同类型的R数据。比如,当把一个数据框传入echart()
,而x
/y
变量均为数值型,它们会自动适配散点图,并自动生成对应的坐标轴。当然,你也可以覆盖自动适配的结果。
试着按下面的步骤制作你的第一幅Echarts交互图。
制图总是始于数据本身。我们用datasets
包自带的mtcars
数据集。你可以输入命令?mtcars
查看数据集的结构。
head(mtcars)
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
我们想知道wt
(weight)和mpg
(miles per gallon)的关联,那么散点图是一个不错的选择。这要求x和y都是数值型。
用echartr
构建基础图形。
echartr(mtcars, wt, mpg)
echartr
的语法是:
## function (data, x = NULL, y = NULL, series = NULL, weight = NULL,
## facet = NULL, t = NULL, lat = NULL, lng = NULL, type = "auto",
## subtype = NULL, elementId = NULL, ...)
参数 | 解释 |
---|---|
data |
源数据,必须是数据框 |
x |
自变量。
|
y |
应变量, |
series |
分组变量, |
weight |
权重变量,在气泡图、线图、柱图中与图形大小关联。 |
facet |
分面变量, |
t |
时间轴变量。一旦指定 |
lat |
纬度,用于地图/热力图 |
lng |
经度,用于地图/热力图 |
type |
图类型,默认为’auto’。 |
subtype |
图亚类,默认为NULL。 |
echartr
目前支持下列图类型 (’name’列的正则样式,不分大小写)。’type’列则是目前Echarts2所支持的图类型。
knitr::kable(recharts:::validChartTypes[,c(1:3,5)])
id | name | type | misc |
---|---|---|---|
1 | ^(scatter|point)$ | scatter | |
2 | ^(bubble)$ | scatter | bubble |
3 | ^(bar|hbar)$ | bar | flip |
4 | ^(vbar|column)$ | bar | |
5 | ^(histogram|hist)$ | hist | |
6 | ^(line)$ | line | |
7 | ^(curve)$ | line | smooth |
8 | ^(area)$ | line | fill |
9 | ^(wave)$ | line | fill_smooth |
10 | ^(map_world|world_map)$ | map | world |
11 | ^(map_china|china_map)$ | map | china |
12 | ^(map_world_multi|world_map_multi)$ | map | world_multi |
13 | ^(map_china_multi|china_map_multi)$ | map | china_multi |
14 | ^(map)$ | map | geojson |
15 | ^(k|candlestick)$ | k | |
16 | ^(pie)$ | pie | |
17 | ^(ring)$ | pie | ring |
18 | ^(rose)$ | pie | rose |
19 | ^(chord)$ | chord | |
20 | ^(force|force_curve)$ | force | |
21 | ^(force_line)$ | force | line |
22 | ^(funnel)$ | funnel | |
23 | ^(pyramid)$ | funnel | ascending |
24 | ^(tree|vtree|tree_vertical)$ | tree | |
25 | ^(htree|tree_horizontal)$ | tree | horizontal |
26 | ^(tree_inv|vtree_inv|tree_vertical_inv)$ | tree | inv |
27 | ^(htree_inv|tree_horizontal_inv)$ | tree | horizontal_inv |
28 | ^(treemap)$ | treemap | |
29 | ^(wordcloud)$ | wordCloud | |
30 | ^(heatmap)$ | heatmap | |
31 | ^(radar|spider|star)$ | radar | |
32 | ^(gauge|dashboard)$ | gauge | |
33 | ^(eventriver)$ | eventRiver | |
34 | ^(venn)$ | venn |
指定data
以及x
、y
,echartr
就会自动调用recharts:::series_scatter
函数处理数据。
如果指定series
,就会产生一幅分组散点图。
echartr(mtcars, wt, mpg, factor(am, labels=c('Automatic', 'Manual')))
如果指定weight
,并设type
为bubble
,就生成一幅气泡图。
echartr(mtcars, wt, mpg, am, weight=gear, type='bubble')
## Warning in split_indices(.group, .n): '.Random.seed' is not an integer
## vector but of type 'NULL', so ignored
输入参数列表的格式包括
wt
、mpg
甚至复杂如factor(am, labels=c('Automatic', 'Mannual'))
'wt'
、'mpg'
~wt
、~mpg
数据必须以数据框形式传入。数据系列和时间轴会以series
和t
变量的原始顺序排序。所以作图前,必须先对数据框进行相应的排序,以确保作出的图里,图例和时间轴以正确的顺序显示。
用到时间轴(t
)时,务必格外小心。x
、y
、series
、weight
变量一旦用到,就要确保每个t
水平上,这些变量都完备地排列组合了。否则你会发现各组图形在时间轴上莫名其妙地重叠呈现。
recharts
在某些情况下支持混合作图,比如柱图和线图。
目前recharts
只支持下列混合 - 直角坐标系图类:散点/气泡、柱/条图、线/面积图 - 极坐标图类:漏斗图、饼/环图 - 其他:力导向图、和弦图
d <- data.table::dcast(mtcars, carb+gear~., mean, value.var='mpg')
names(d)[3] <- 'mean.mpg'
d$carb <- as.character(d$carb)
echartr(d, carb, "mean.mpg", gear, type=c('vbar', 'vbar', 'line')) %>%
setSymbols('emptycircle')
也可以混合亚类。参见recharts:::validChartTypes
的subtype
列。你可以用’+‘、’_‘或’|’组合多个亚类,比如’stack+smooth’可以用来生成堆积平滑线图。
echartr(d, carb, mean.mpg, gear, type='line',
subtype=c('stack + smooth', 'stack + dotted', 'smooth + dashed')) %>%
setSymbols('emptycircle')
上面的例子显示了如何将有类/亚类映射到数据系列(series)。如果你想将类/亚类映射到分面,需要将它们写成列表(list),而列表中的每个向量,则依次映射到数据系列(series)。即list[[1]], list[[2]], …映射分面1、2, …,而list[[1]]的1、2、…向量则映射系列1、2、…。
如facet为2个水平,series为3个水平,意味着type和subtype应为list(c(s1,s2,s3), c(s1,s2,s3))结构。recharts会尝试将传入参数type和subtype进行补齐或截断,以达到一一映射。
系列/分面 | 分面1 | 分面2 | … | 分面i |
---|---|---|---|---|
系列_1_ | list[[1]][1] | list[[2]][1] | … | list[[i]][1] |
系列_2_ | list[[1]][2] | list[[2]][2] | … | list[[i]][2] |
… | … | … | … | … |
系列_j_ | list[[1]][j] | list[[2]][j] | … | list[[i]][j] |
d1 <- data.frame(x=rep(LETTERS[1:6], 4), y=abs(rnorm(24)),
f=c(rep('i', 12), rep('ii', 12)),
s=rep(c(rep('I', 6), rep('II', 6)), 2))
echartr(d1, x, y, s, facet=f, type='radar',
subtype=list(c('fill', ''), c('', 'fill')))
你可以将基本图形存为一个对象,然后不断修改它,并把这些修改用%>%
串联起来。
g = echartr(mtcars, wt, mpg, factor(am, labels=c('Automatic', 'Manual')))
上述命令创建了一个Echarts对象g
,包含两个数据系列:‘Automatic’和’Manual’。g
作为一个列表,其数据结构为:
- x
|-- series
|--- list 1
|---- name: 'Automatic'
|---- data: ...
|---- type: 'scatter'
|--- list 2
|---- name: 'Manual'
|---- data: ...
|---- type: 'scatter'
|--- ...
|-- legend
|-- xAxis
|-- yAxis
|-- grid
|-- tooltip
|-- ...
如果你想直接修改数据系列的定义,可以调用低级函数setSeries
。
我们把’Manual’系列(索引号为2)的symbolSize改为8,symbolRotate改为30。
g %>% setSeries(series=2, symbolSize=8, symbolRotate=30)
给两个数据系列分别添加各自的均数标注线。
g %>% addMarkLine(data=data.frame(type='average', name1='Avg'))
给第一个数据系列(‘Automatic’)标出最大值的点。
g %>% addMarkPoint(series=1, data=data.frame(type='max', name='Max'))
该命令也可以写作
g %>% addMarkPoint(series='Automatic', data=data.frame(type='max', name='Max'))
添加标题(红色)和副标题(超级链接到 https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html)。
link <- 'https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html'
g %>% setTitle('wt vs mpg', paste0('[Motor Trend](', link, ')'),
textStyle=list(color='red'))
修改图例(青柠色),初始化时只选中第一系列(‘Automatic’)。
g %>% setLegend(selected='Automatic', textStyle=list(color='lime'))
修改工具箱显示语言为英文,并置于交互图右上角,垂直显示。
关于
pos
变量如何定义,请参考vecPos
函数。
g %>% setToolbox(lang='en', pos=2)
添加缩放漫游控件(初始时不显示).
g %>% setDataZoom()
调整坐标轴,使x-和y-坐标交叉于零点。
g %>% setXAxis(min=0) %>% setYAxis(min=0)
使用’dark’主题。可以选择的自带主题包括“macarons”, “infographic”, “blue”, “dark”, “gray”, “green”, “helianthus”, “macarons2”, “mint”, “red”, “roma”, “sakura”, “shine”, 和 “vintage”。
拖曳重算(Calculable)是Echarts特有的交互方式。在某些图(如饼图)中,效果比较好。
g %>% setTheme('dark', calculable=TRUE)
把第1系列(‘Automatic’)的图标改为’heart’,第2系列(‘Manual’)的图标改为’star6’。
g %>% setSymbols(c('heart', 'star6'))
你可以把上述步骤用%>%合起来。如果你对JavaScript很熟悉,你可以把JavaScript片段包在JS()
函数中,以获得更好的效果。
g %>% setSeries(series=2, symbolSize=8, symbolRotate=30) %>%
addMarkLine(data=data.frame(type='average', name1='Avg')) %>%
addMarkPoint(series=1, data=data.frame(type='max', name='Max')) %>%
setTitle('wt vs mpg', paste0('[Motor Trend](', link, ')'),
textStyle=list(color='red')) %>%
setLegend(selected='Automatic', textStyle=list(color='lime')) %>%
setToolbox(lang='en', pos=2) %>% setDataZoom() %>%
setTheme('dark', calculable=TRUE) %>% setSymbols(c('heart', 'star6'))
虽然ECharts支持很多种图,但要娴熟地用echartr()
创建它们仍可能花不少时间。 所以recharts也为list提供了一个低级的S3方法实现。由于ECharts的主要用法是传入一个JavaScript对象给.setOption()
方法,而在R中,这样一个对象是用list构建的。所以echart.list()
这个低级方法能让用户任意创建自定义图形。下面是一个简单的和弦图的例子,取材于http://echarts.baidu.com/echarts2/doc/example/chord1.html:
chordEx1 = list(
title = list(
text = '测试数据',
subtext = 'From d3.js',
x = 'right',
y = 'bottom'
),
tooltip = list(
trigger = 'item',
formatter = JS('function(params) {
if (params.indicator2) { // is edge
return params.value.weight;
} else {// is node
return params.name
}
}')
),
toolbox = list(
show = TRUE,
feature = list(
restore = list(show = TRUE),
magicType = list(show = TRUE, type = c('force', 'chord')),
saveAsImage = list(show = TRUE)
)
),
legend = list(
x = 'left',
data = c('group1', 'group2', 'group3', 'group4')
),
series = list(
list(
type = 'chord',
sort = 'ascending',
sortSub = 'descending',
showScale = TRUE,
showScaleText = TRUE,
data = list(
list(name = 'group1'),
list(name = 'group2'),
list(name = 'group3'),
list(name = 'group4')
),
itemStyle = list(
normal = list(
label = list(show = FALSE)
)
),
matrix = rbind(
c(11975, 5871, 8916, 2868),
c( 1951, 10048, 2060, 6171),
c( 8010, 16145, 8090, 8045),
c( 1013, 990, 940, 6907)
)
)
)
)
echart(chordEx1)
显然,上述例子就是把原例子中的JavaScript对象翻译成了R对象。注意,我们是用htmlwidgets中的JS()
函数翻译了tooltip.fomatter
函数。其他所有对象都可以自然地映射到R。
这个例子等价于
mat <- as.data.frame(rbind(
c(11975, 5871, 8916, 2868),
c( 1951, 10048, 2060, 6171),
c( 8010, 16145, 8090, 8045),
c( 1013, 990, 940, 6907)
))
names(mat) <- c("group1", "group2", "group3", "group4")
mat$name <- names(mat)
echartr(mat, x=name, y=c(group1, group2, group3, group4), type="chord",
subtype='ribbon + asc + descsub + hidelab + scaletext') %>%
setTitle("测试数据", subtitle="From d3.js", pos=5)
如果熟悉Echarts的数据结构,我们也可以用echartr
创建Echarts对象,然后直接修改它,就如同修改其他S3 list一样。
下面是Echarts对象g
的数据结构。g
是通过命令echartr(mtcars, wt, mpg, factor(am, labels=c('Automatic', 'Mannual')))
创建出来的。
enquote(g)
## quote(list(x = list(series = list(list(name = "Automatic", type = "scatter",
## data = list(list(3.215, 21.4), list(3.44, 18.7), list(3.46,
## 18.1), list(3.57, 14.3), list(3.19, 24.4), list(3.15,
## 22.8), list(3.44, 19.2), list(3.44, 17.8), list(4.07,
## 16.4), list(3.73, 17.3), list(3.78, 15.2), list(5.25,
## 10.4), list(5.424, 10.4), list(5.345, 14.7), list(2.465,
## 21.5), list(3.52, 15.5), list(3.435, 15.2), list(3.84,
## 13.3), list(3.845, 19.2))), list(name = "Manual", type = "scatter",
## data = list(list(2.62, 21), list(2.875, 21), list(2.32, 22.8),
## list(2.2, 32.4), list(1.615, 30.4), list(1.835, 33.9),
## list(1.935, 27.3), list(2.14, 26), list(1.513, 30.4),
## list(3.17, 15.8), list(2.77, 19.7), list(3.57, 15), list(
## 2.78, 21.4)))), legend = list(show = TRUE, data = list(
## "Automatic", "Manual"), x = "left", y = "top", orient = "horizontal"),
## yAxis = list(list(type = "value", show = TRUE, position = "left",
## name = "mpg", nameLocation = "end", boundaryGap = c(0,
## 0), scale = TRUE, axisLine = list(show = TRUE, onZero = FALSE),
## axisTick = list(show = FALSE), axisLabel = list(show = TRUE),
## splitLine = list(show = TRUE), splitArea = list(show = FALSE))),
## xAxis = list(list(type = "value", show = TRUE, position = "bottom",
## name = "wt", nameLocation = "end", boundaryGap = c(0,
## 0), scale = TRUE, axisLine = list(show = TRUE, onZero = FALSE),
## axisTick = list(show = FALSE), axisLabel = list(show = TRUE),
## splitLine = list(show = TRUE), splitArea = list(show = FALSE))),
## tooltip = list(show = TRUE, trigger = "axis", axisPointer = list(
## type = "cross", crossStyle = list(type = "dashed"), lineStyle = list(
## type = "solid", width = 1), shadowStyle = list(color = "rgba(150,150,150,0.3)",
## width = "auto", type = "default")), textStyle = list(
## color = "#fff"), formatter = "function (params) {\n var i;\n var text = params.value[0];\n if (params.seriesName === null || params.seriesName === \"\"){\n if (params.value.length > 1) {\n for (i = 1; i < params.value.length; i++){\n text += \" , \" + params.value[i];\n }\n return text;\n } else {\n return params.name + \" : \" + text;\n }\n } else {\n if (params.value.length > 1) {\n text = params.seriesName + \" :<br/>\" + text;\n for (i = 1; i < params.value.length; i++) {\n text += \" , \" + params.value[i];\n }\n return text;\n } else {\n return params.seriesName + \" :<br/>\"\n + params.name + \" : \" + text;\n }\n }\n }",
## islandFormatter = "{a} < br/>{b} : {c}", enterable = FALSE,
## showDelay = 20, hideDelay = 100, transitionDuration = 0.4,
## backgroundColor = "rgba(0,0,0,0.7)", borderColor = "#333",
## borderWidth = 0, borderRadius = 4), toolbox = list(show = TRUE,
## feature = list(mark = list(show = TRUE), dataZoom = list(
## show = TRUE), dataView = list(show = TRUE, readOnly = FALSE),
## magicType = list(show = FALSE), restore = list(show = TRUE),
## saveAsImage = list(show = TRUE)), x = "right", y = "top",
## orient = "horizontal")), width = NULL, height = NULL,
## sizingPolicy = list(defaultWidth = NULL, defaultHeight = NULL,
## padding = NULL, viewer = list(defaultWidth = NULL, defaultHeight = NULL,
## padding = NULL, fill = TRUE, suppress = FALSE, paneHeight = NULL),
## browser = list(defaultWidth = NULL, defaultHeight = NULL,
## padding = NULL, fill = FALSE), knitr = list(defaultWidth = NULL,
## defaultHeight = NULL, figure = TRUE)), dependencies = NULL,
## elementId = NULL, preRenderHook = NULL, jsHooks = list()))
Echarts本质上是一个htmlwidgets对象。所以只要调用htmlwidgets包的saveWidget函数即可将其保存为HTML文件。
也可以将Echarts用于Shiny。具体可参考renderEchart
函数。
library(recharts)
library(shiny)
app = shinyApp(
ui = fluidPage(eChartOutput('myChart')),
server = function(input, output) {
chart = echartr(data.frame(x = rnorm(100), y = rnorm(100)), x, y)
output$myChart = renderEChart(chart)
}
)
if (interactive()) print(app)
通常我们不希望R用户写那么长的选项列表。R用户一般更熟悉(也更常用到)诸如数据框、矩阵、表格等数据结构,理想状态下,我们希望用户只要传入一些熟悉的数据对象,recharts自动地配置出ECharts对象来。欢迎反馈您的体验和看法,也欢迎用Github pull requests贡献您的代码。