forked from FrissAnalytics/shinyJsTutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial_02.Rmd
More file actions
729 lines (494 loc) · 29.6 KB
/
Copy pathtutorial_02.Rmd
File metadata and controls
729 lines (494 loc) · 29.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
---
title: "Shiny JavaScript Tutorials"
author:
- "Herman Sontrop"
- "Erwin Schuijtvlot"
date: "`r Sys.Date()`"
output:
html_document:
highlight: kate
mathjax: null
theme: flatly
toc: yes
toc_depth: 4
vignette: >
%\VignetteIndexEntry{Introduction}
%\VignetteEncoding{UTF-8}
%\VignetteEngine{knitr::rmarkdown}
---
## Introduction
In the [previous](http://shiny.rstudio.com/tutorial/js-lesson1/) tutorial, we created a simple [C3](http://c3js.org/) based gauge widget. In this tutorial, we expand the widget to create smooth transitions like those in the [showcase dashboard app](https://js-tutorial.shinyapps.io/dashboardApp/). We also provide code and examples for the other charts types shown in the dashboard: a pie chart, a line + bar chart and a stacked area chart. In this tutorial, we focus on pairing charts and data in such a way that C3 can animate the transition between old and new data.
<iframe width='100%' height='300' src='https://jsfiddle.net/FrissAnalytics/tx38gstp/9/embedded/result' allowfullscreen='allowfullscreen' frameborder='0'></iframe>
<div style = "text-align:center; margin-bottom:25px" >example of a gauge with animated transitions between old and new data</div>
Before we do all that, let's take a better look at the `HTMLWidgets.widget` function we saw in the [first tutorial](http://shiny.rstudio.com/tutorial/js-lesson1/), which is related to the concept of factory methods and closures.
### Factory methods and closures
In the previous tutorial, we saw that the skeleton of our widget initially looks like this
```javascript
HTMLWidgets.widget({
name: 'C3Gauge',
type: 'output',
factory: function(el, width, height) {
// TODO: define shared variables for this instance
return {
renderValue: function(x) {
// TODO: code to render the widget, e.g.
el.innerText = x.message;
},
resize: function(width, height) {
// TODO: code to re-render the widget with a new size
}
};
}
});
```
If you are new to JavaScript, this code may look a bit strange. We see that `factory` is a [method](http://www.w3schools.com/js/js_object_methods.asp), but the return type is not a simple value. What's with that?
The construction above is a design pattern that software engineers call a [factory](http://www.oodesign.com/factory-method-pattern.html) method. This pattern contains a method that returns an object instance. That may sound complicated, so let's dig a little deeper to see what it means.
In more general terms, a factory method like this:
```javascript
factory: function () {return {
a: function() { ... },
b: function() { ... }
}};
```
is equivalent to:
```javascript
// factory is a method of some object
factory: function () {
// define a new object
var obj = new Object();
// create a method a for it
obj.a = function() { ... };
// create a method b for it
obj.b = function() { ... };
// return the object instance
return obj;
}
```
Hence, in our case, the `factory` method in HTMLWidgets.widget returns an `object` of the widget instance. This object itself has two methods i.e. `renderValue`, which draws our widget, and `resize`, which resizes the widget (if necessary).
In the implementation above, `renderValue` and `resize` are both implemented as <a href='http://www.w3schools.com/js/js_function_closures.asp'>closures</a>, which means that the functions remember the scope they were created in and have access to the variables defined in that scope. A more in depth discussion on JavaScript closures is offered [here](https://developer.mozilla.org/en/docs/Web/JavaScript/Closures). Of note, the concept of closures also exists in R, see [here](http://adv-r.had.co.nz/Functional-programming.html#closures).
The implication of `renderValue` and `resize` being closures is that they have access to the `el`, `width` and `height` parameters passed to `factory`. Furthermore, we can initialize variables in the body of `factory` (above the definitions of `renderValue` and `resize`) that we want to use during the lifetime of our widget.
Now that we have a better understanding of what our `HTMLWidgets.widget` function is doing, let's add some transitions to our gauge widget!
## Adding transitions to a C3 gauge
Even though the gauge widget from the first tutorial works, we can improve it. The current widget always creates a completely new gauge if we send it a value via shiny (using the `renderC3Gauge` function, see [tutorial 1](http://shiny.rstudio.com/tutorial/js-lesson1/)). It would be nicer if the widget used an animation to smoothly transition from its old state to its new state, as in the example at the top of the page. Luckily, C3 can create such transitions automatically. To take advantage of this, we will have to modify the code of our factory method.
In pseudo code, our new factory method will looks something like this:
```javascript
factory: function(el, width, height){
// we can define any variables we wish to use to keep track of the state of the widget here
// lets create an empty chart
var chart = null;
return {
renderValue: function(x) {
// check if the chart exists
if(chart === null){
// the chart did not exist and we want to create a new chart
chart = createChart(x);
// store the chart on el so we can get it later
el.chart = chart;
}
// at this stage the chart always exists
// get the chart stored in el and update it
el.chart.updateChart(x);
}
}
}
```
To understand the pseudo code, it is helpful to realize that there are two basic stages in the lifetime of a chart:
1) the chart does not yet exist
2) the chart already exists and we want to pass in new data, set options or send messages to it
The factory method has three arguments: `el`, `width` and `height`. Here `el` is the container element created by htmlwidgets in which we will house our chart. On top, we create a variable called `chart`, which we initially set to null, indicating that the chart does not yet exist.
Inside the `renderValue` method, we first check if the chart exists. If the chart does not exist, we create a chart via the toy function createChart, which in this case uses information in `x`. Subsequently, we store the chart on `el`, so we can access it later.
Technically, the closure allows us to render the chart even in the first line, i.e. at the line `var chart = null;`. In C3, however, we often need information stored in `x` to create a chart. Note that `x` contains the data and chart options passed via shiny from R to JavaScript (using jsonlite, see [tutorial 1](http://shiny.rstudio.com/tutorial/js-lesson1/)). As C3 often needs information in `x` during initialization, here we initialize the chart inside the `renderValue` method and subsequently check if the chart was already created. This is slightly inelegant, however, it is quite easy and the overhead is minimal.
Note that the chart will exist in all subsequent calls to `renderValue`. In those instances, we first retrieve the chart via `el`, which we can access because `renderValue` is a closure. Second, we call an update method on the chart, which will again use information stored in `x`.
Let's convert the previous pseudo code into actual code for our gauge!
The next JavaScript code block shows how we can load new data into a C3 based chart created via [c3.generate](http://c3js.org/gettingstarted.html#generate).
```javascript
var chart = c3.generate({
// chart code here
...
});
// some data
var newData = 50;
// load data into the chart
chart.load({
json: newData
});
```
If we combine this code snippet with the previous factory pseudo code we end up with the code we're looking for:
```javascript
HTMLWidgets.widget({
name: 'C3Gauge',
type: 'output',
factory: function(el, width, height) {
// create an empty chart
var chart = null;
return {
renderValue: function(x) {
// check if the chart exists
if(chart === null){
// the chart did not exist and we want to create a new chart via c3.generate
chart = c3.generate({
bindto: el,
data: {
json: x,
type: 'gauge',
},
gauge: {
label:{
format: function(value, ratio){ return value;}
},
min: 0,
max: 100,
width: 15,
units: 'value'
}
});
// store the chart on el so we can get it later
el.chart = chart;
}
// at this stage the chart always exists
// get the chart stored in el and update it
el.chart.load({json: x});
}
};
}
});
```
Of note, for brevity here we omitted the `resize` method (which is optional).
### Code repository and demo app
You can find the complete code for the updated gauge widget, as well as the code for the widgets below at [this](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3) repo, which contains a C3 package. The C3 package contains updated code for the gauge widget, a pie chart widget, a combination bar + line chart and a stacked area chart. The code of the latter three widgets is discussed in detail below. The R code of these widgets can be found [here](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3/R). The JavaScript and YAML files for each widget are located [here](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3/inst/htmlwidgets).
You can install the updated C3 package by running the following command in R:
```r
devtools::install_github("FrissAnalytics/shinyJsTutorials/tutorials/materials2/C3")
```
In order to help you understand the various C3 widgets in this tutorial, we've built a shiny C3 demo app that shows how you can use these widgets in a shiny context. You can download this dashboard [here](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3_demo). To see the app in action press the screenshot below! In the next tutorials we will extend the capabilities of these widgets even further when we take a closer look at JavaScript [events](http://www.w3schools.com/js/js_events.asp).
<p style = "margin: 40px 0px">
<a href = "https://js-tutorial.shinyapps.io/C3_demo/" target = "_blank"><img src = "C3_dashboard.png" width="100%"></img></a>
<div style = "text-align:center">demo app with animated C3 widgets. Click on the image to launch the app!</div>
</p>
We show you how to create the widgets below, which you can subsequently render with `C3Gauge`,`C3Pie`,`C3LineBarChart`, and `C3StackedAreaChart`.
In a shiny app (in ui.r), these functions are paired with the output functions: `C3GaugeOutput`, `C3PieOutput`, `C3LineBarChartOutput`, `C3StackedAreaChartOutput`, and (in server.R) with the render functions: `renderC3Gauge`, `renderC3Pie`, `renderC3LineBarChart`, `renderC3StackedAreaChart`.
Note that you can also use the widgets in a Markdown document as well!
You can also experiment with code changes right in the browser with an online htmlwidget fiddle app, as discussed in the next section.
### Htmlwidget fiddle app
It can become a bit cumbersome to build, install and load a package each time you want to test something, especially if you just want to play around with an example and experiment to find out how something works. For the purpose of this tutorial series, we created a small helper online app similar to a JavaScript fiddle. You can use the app to change code and immediately see the impact. You can find the online app [here](https://js-tutorial.shinyapps.io/WidgetFiddle/).
<p style = "margin:25px 0px">
<a href = "https://js-tutorial.shinyapps.io/WidgetFiddle/" target = "_blank"><img src = "htmlwidgetsFiddle_app.png" width="100%"></img></a>
<div style = "text-align:center">htmlwidget fiddle app. Play with C3 code & create a package right from the browser!</div>
</p>
#### About the app
All widgets in this tutorial series are available in the app. You can select a widget in the drop-down list after which its JavaScript code, R code, and YAML contents are loaded in the corresponding tabs. You can then play around with code changes and see the effect by pressing the 'render widget' button. In the Widgetcall tab, you can add your own data and provide the widget render call.
You can also export the code as a zipped R package, which can be installed by opening the .Rproj file in RStudio and running `devtools::install()`. Currently, the functionality of this app is limited to rendering C3 based charts. We plan to update the functionality of this app during the course of the tutorial series, i.e. by allowing you to add additional JavaScript and CSS files.
Now we're all set, let's make some more widgets!
## C3 data update pattern
The gauge is by far the most simple visualization C3 offers. More complex charts require more complex data objects. In C3, each data series is associated with a unique `key`. These keys help us to keep track of which data series are in the chart, which data parts need an update and which data we want to remove.
To be more specific, when C3 loads a new data set via the `load` method, it will overwrite data if the key you provide is already present. However, when the key is not already present, the data you provide for that key will be added to the data it already has! As a consequence, if you want to remove data, you'll have to explicitly remove it for a given key. To illustrate this process, consider the following example.
Assume we have the following data: `{key1 = 10, key2 = 25}`, and in a next step we want to use: `{key1 = 20, key3 = 30}`. Notice that in the new data key2 is *not* present. Without unloading, loading the new data set will result in the data `{key1 = 20, key2 = 25, key3 = 30}`. In this case C3 updates the data for key1, retains the data for key2, and adds the data for key3. In order to make the correct update, we need to remove the data for key2, which we can do via an `unload` statement (see below).
## C3 pie chart
With the gauge chart fully handled, let's create a C3 based pie chart with animated transitions! Just like in our [first tutorial](http://shiny.rstudio.com/tutorial/js-lesson1/), let's first take a look at the HTML and JavaScript code via a JSfiddle.
<iframe width='100%' height='450' style = 'padding-bottom:25px' src='https://jsfiddle.net/FrissAnalytics/umu7Lnrv/12/embedded/result,js,html' allowfullscreen='allowfullscreen' frameborder='0'></iframe>
The steps for creating other C3 widgets are very similar to the process described for the gauge widget. In the remainder of the tutorial, we will therefore focus on complete code segments for the R and JavaScript parts, in which various parts are explained via code comments inside the code blocks.
In the charts below, we use various C3 specific chart options. A complete overview of all these options (including additional classes & methods) can be found in the [C3 reference](http://c3js.org/reference.html). In addition, the complete R, YAML and JavaScript code for all the widgets constructed in this tutorial can be found [here](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3).
Similar to the [first tutorial](http://shiny.rstudio.com/tutorial/js-lesson1/), we can create the template code for a C3 pie widget via the R command:
```R
htmlwidgets::scaffoldWidget("C3Pie", edit = FALSE)
```
This command creates a C3Pie.js and C3Pie.yaml file in the inst/htmlwidgets folder and a C3Pie.R file in the R folder of your package (see [tutorial 1](http://shiny.rstudio.com/tutorial/js-lesson1/)).
The R part of our code looks like this:
```R
C3Pie <- function(values, legendPosition = "bottom", width = NULL, height = NULL) {
# forward options using x
x = list(
values = values,
legendPosition = legendPosition
)
# create widget
htmlwidgets::createWidget(
name = 'C3Pie',
x,
width = width,
height = height,
package = 'C3'
)
}
```
Note that we added two arguments i.e. `values`, which contains the individual pie values and `legendPosition`, which takes as values 'bottom', 'right', and 'inset' (see [here](http://c3js.org/reference.html#legend-position)).
As before, all data is passed from R by the variable `x`, which is a list. Subsequently, shiny will tacitly pass this object via the R package [jsonlite](https://cran.r-project.org/web/packages/jsonlite/vignettes/json-aaquickstart.html), to JavaScript. Remember, after this step we are talking about a JavaScript object, which is very different to the R object you started with. As noted before, this step is one of the hardest (and often most confusing) steps in creating htmlwidgets.
<p style = "margin:25px">
<a href = "https://cran.r-project.org/web/packages/jsonlite/vignettes/json-aaquickstart.html" target = "_blank"><img src = "jsonlite.svg" width="100%"></img></a>
<div style = "text-align:center">all objects that are send from R to JavaScript, or from JavaScript to R, are tacitly passed and processed by shiny via jsonlite</div>
</p>
Back to our widget, in JavaScript, we can subsequently access the two variables from above via `x.value` and `x.legendPosition`, respectively. In the R code above, `values` is a list object. For instance, we can specify the pie data as `list('data1'=30,'data2'=120)`.
As noted in the [tutorial 1](http://shiny.rstudio.com/tutorial/js-lesson1/#step-3), it is worthwhile to experiment with [jsonlite](https://cran.r-project.org/web/packages/jsonlite/vignettes/json-aaquickstart.html) to see how R structures are converted from R to JavaScript.
For instance, in R run:
```{r}
jsonlite::toJSON(list('data1'=30,'data2'=120))
```
The output results in `{"data1":[30],"data2":[120]}`. Aside from the extra brackets, the output looks like the structure we would like to have. The extra brackets imply an array with a single value, which for this purpose is the same as a single number.
The JavaScript code for our pie widget looks like this:
```javascript
HTMLWidgets.widget({
name: 'C3Pie',
type: 'output',
factory: function(el, width, height) {
// create an empty chart
var chart = null;
return {
renderValue: function(x) {
// if the chart does not exist, create it via c3.generate
if(chart === null){
// create a pie chart, see http://c3js.org/samples/chart_pie.html
chart = c3.generate({
bindto: el,
data: {
json : [],
type : 'pie'
},
legend: {
position: x.legendPosition
}
});
}
// at this stage the chart always exists
// get difference in keys
var old_keys = _.keys(chart.x());
var new_keys = _.keys(x.values);
var diff = _.difference(old_keys,new_keys);
// load the new data (stored in x.values)
chart.load({
json:
x.values,
// unload data that we don't want anymore
unload: diff
});
}
};
}
});
```
In the JavaScript code above, just before the `chart.load` command, we compute the difference between keys in the old (current) data and the new data. In R, computing set differences is easy via `setdiff`. However, this function is not available in JavaScript, so how can we compute this difference?
Luckily, in JavaScript there are many tool-belt helper libraries that offer handy functions for manipulating data. A popular library worth checking out is [underscore](http://underscorejs.org/), which we use to help us work with data keys. Here we used the underscore functions [_.keys](http://underscorejs.org/#keys) and [_.difference](http://underscorejs.org/#difference). Via `_.keys` we extract the key information out of the appropriate objects and via `_.difference` we compute the set difference:
``` javascript
var old_keys = _.keys(chart.x());
var new_keys = _.keys(x.values);
var diff = _.difference(old_keys,new_keys);
```
Notice that all of the functions in underscore start with a `_`, hence the name. You don't need a complete understanding of the last code snipped to follow the rest of this tutorial, but we highly recommend you check out what underscore can do, as being able to work with JavaScript objects and arrays is really helpful in building widgets!
For the remaining two charts (discussed below) we use the same concept, but the objects we put in `_.keys` are slightly different.
Finally, the YAML file for our pie widget looks like this:
```javascript
dependencies:
- name: d3
version: 3.5.0
src: htmlwidgets/lib/d3
script: ./d3.min.js
- name: c3
version: 0.4.10
src: htmlwidgets/lib/c3
script: ./c3.min.js
style: ./c3.min.css
- name: underscore
version: 1.8.3
src: htmlwidgets/lib/underscore
script: ./underscore-min.js
```
Note that we added a reference to underscore.
You can test drive the C3 pie widget via:
```R
C3Pie(values=list(data1=200,data2=300,data3=100))
```
## C3 combination chart
Instead of creating a single chart, like a line chart or a bar chart, C3 allows you to create [combination charts](http://c3js.org/samples/chart_combination.html) that combine several chart types into a single chart.
<iframe width='100%' height='300' style = "padding-bottom:25px" src='https://fiddle.jshell.net/FrissAnalytics/gzx4sxqn/embedded/result,js,html' allowfullscreen='allowfullscreen' frameborder='0'></iframe>
In this segment, we will combine a bar chart with a set of line charts. In addition, we'll add a sub-chart to it which we'll use in a later tutorial to create a time based filter.
<iframe width='100%' height='400' src='https://fiddle.jshell.net/FrissAnalytics/hu5j1yan/embedded/result,js,html' allowfullscreen='allowfullscreen' frameborder='0'></iframe>
Looking at the fiddle, you can see we have a lot more options to deal with. To keep things simple, we're not going to parameterize all of them. In the widget below, the user provides a single dataframe called `dataset`, with a time column (used for the x-axis) and a set of additional columns (used as y-values) for the bar and line series. Furthermore, the user must specify a `colors` variable that holds the display colors for the bar and line charts.
Here's the complete R code for our C3LineBarChart widget:
```R
C3LineBarChart <- function(dataset, colors, width = NULL, height = NULL) {
# forward options using x
x = list(
dataset = dataset,
colors = colors
)
# create widget
htmlwidgets::createWidget(
name = 'C3LineBarChart',
x,
width = width,
height = height,
package = 'C3'
)
}
```
From the JSfiddle we can see C3 want the data in this format:
``` javascipt
var dataSet = [
{"Time":"2016-01-01","GREEN":1,"RED":2,"Total":3},
{"Time":"2016-01-02","GREEN":4,"RED":5,"Total":6},
{"Time":"2016-01-03","GREEN":7,"RED":9,"Total":9}
];
```
From R, we can obtain this format by providing the data as a dataframe. Let's test this assertion with jsonlite:
``` {r}
dataset <- data.frame(Time = c("2016-01-01","2016-01-02","2016-01-03"),
GREEN = c(1,4,7),
RED = c(2,5,9),
Total = c(3,6,9) )
jsonlite::toJSON(dataset)
```
Nice, precisely what we need!
The complete JavaScript code for our C3LineBarChart looks like this:
```javascript
HTMLWidgets.widget({
name: 'C3LineBarChart',
type: 'output',
factory: function(el, width, height) {
// create an empty chart
var chart = null;
return {
renderValue: function(x) {
// if the chart does not exist, create it via c3.generate
if(chart===null){
keys = _.keys(x.dataset);
chart = c3.generate({
// specify the container element we want the chart to render in
bindto: el,
data: {
// intialize with an empty array
json: [],
keys: {
// use Time for x-axis
x: "Time",
// use the remaining data for y-values
value: keys,
},
// set chart types
types: {
// default is line, we want totals to be displayed as bars
Total: 'bar'
},
axes: {
// extra y-axis
Total: 'y2'
}
},
axis: {
x: {
// x axis as timeseries
type: "timeseries",
// tick format x-axis
tick: {
format: "%Y-%m-%d"
}
},
y2: {
// we want a second y-axis
show: true
}
},
// display a subchart - this will be used for brushing in a later stage
subchart: {
show: true
}
});
}
// at this stage the chart always exists
// get difference in keys
var old_keys = _.keys(chart.x());
var new_keys = _.keys(x.dataset);
var diff = _.difference(old_keys,new_keys);
// update the data and colors
chart.load({
json : x.dataset,
colors: x.colors,
// unload data that we don't need anymore
unload: diff
});
}
};
}
});
```
As you can see, this code is a bit more involved. However, for the most part, we are only specifying specific C3 options. An overview of the complete set of available options can be found in the [C3 reference](http://c3js.org/reference.html).
The concept of rendering this chart is the same as for the pie chart. We make sure we hand the data to JavaScript in the correct format, we initialize a chart with an empty array, we compute any differences in keys relevant to unloading specific series and then we load and unload data.
You can build your practical experience by using our [htmlwidgets fiddle app](https://js-tutorial.shinyapps.io/WidgetFiddle/) to experiment with creating and setting options for C3 charts. When you do this, its probably best to keep a separate tab open with the [C3 reference](http://c3js.org/reference.html).
Of note, we will use the sub-chart we created here in a later part of the tutorial series to pick up brush events and to send information back to shiny so we can create a brush-able time based filter.
Finally, the YAML file for this widget is the same as for our pie chart.
When we install our [C3 package](https://github.com/FrissAnalytics/shinyJsTutorials/tree/master/tutorials/materials2/C3), we can create a C3LineBarChart via:
```R
dataset <- data.frame(Time=c("2016-01-05","2016-01-12","2016-01-24"),
GREEN=c(0.1,0,0.2),
RED=c(0.9,1,0.8),
Total=c(320,210,190))
colors <- list(Total="gray",GREEN="Green",RED="Red")
C3LineBarChart(dataset = dataset,colors=colors)
```
## C3 stacked area chart
Our last chart is very similar to the combination chart above. Here we create a C3 [stacked area chart](http://c3js.org/samples/chart_area_stacked.html). As all of the concepts for this widget are repetitions from the previous charts, we keep the description of this widget brief. Here's a fiddle showing how to code a C3 stacked area chart:
<iframe width='100%' height='420' style = "padding-bottom:25px" src='https://fiddle.jshell.net/FrissAnalytics/b71gsdy9/embedded/result,js,html' allowfullscreen='allowfullscreen' frameborder='0'></iframe>
The corresponding R code for our stacked area chart widget looks like this:
```R
HTMLWidgets.widget({
name: 'C3StackedAreaChart',
type: 'output',
factory: function(el, width, height) {
// create an empty chart
var chart = null;
return {
renderValue: function(x) {
// if the chart does not exist, create it via c3.generate
if(chart===null){
var keys = _.keys(x.dataset);
chart = c3.generate({
bindto: el,
data: {
json: [],
keys: {
x: "Time",
value: keys,
}
},
axis: {
x: {
type: "timeseries",
tick: {
format: "%Y-%m-%d"
}
}
}
});
}
// at this stage the chart always exists
// get difference in keys
var old_keys = _.keys(chart.x());
var new_keys = _.keys(x.dataset);
var diff = _.difference(old_keys,new_keys);
//chart.groups([x.groups]);
// update the data
chart.load({
// new data
json : x.dataset,
// new colors
colors: x.colors,
// remove data we no longer need (if any)
unload: diff,
// set types
types : x.types
});
}
};
}});
```
Finally, like before, the YAML file is similar to the one for the line bar chart and pie chart.
In R, the stacked area chart widget can be rendered via the following snippet:
``` r
dataset <- data.frame(
Time = c("2016-01-05","2016-01-12","2016-01-24"),
GREEN = c(0.5,0.2,0.3),
RED = c(0.5,0.8,0.7))
colors <- list(GREEN = "green", RED = "red")
C3StackedAreaChart(dataset,colors)
```
Note that we create a named list to set the colors such that the data keys line up with the display colors we want to use.
## Next tutorial
At this stage we have created code for all four widgets in the [showcase dashboard app](https://js-tutorial.shinyapps.io/dashboardApp/)! In the next tutorial, we see how we can send messages from R to our widgets and back. The messages allow us to manipulate charts from R and to create linked visualizations in which one widget reacts to another widget.