I would like to create a bar and line chart using dygraphs, which seems like it should be possible based on the \"Bar & Line Chart\" dygraphs example here, and the
After a bit of research I think that this would be simplest. At least that's the way it seems for me.
You would need to download the "barseries.js" file available at http://dygraphs.com/tests/plotters.html
Then the code would look like so
library(dygraphs)
dyBarSeries <- function(dygraph, name, ...) {
file <- "plotters/barseries.js" #you need to link to the downloaded file
plotter_ <- paste0(readLines(file, skipNul = T), collapse = "\n")
dots <- list(...)
do.call('dySeries', c(list(dygraph = dygraph, name = name, plotter =
plotter_), dots))
}
lungDeaths <- cbind(ldeaths, mdeaths)
dygraph(lungDeaths) %>%
dyBarSeries("ldeaths") %>%
dySeries("mdeaths")
Yielding this result
enter image description here
I am not sure this is exactly what you want. What I propose, comes close to the combination of a bar plot and a line plot, without the need to create a separate function.
You can set the type of plot per series, with dySeries
. You can choose between lineplot (default), stepPlot
, and stemPlot
. In addition you may set to see the points with drawPoints
and pointSize
, you may also opt to fill the graph or not with fillGraph
. For other options type ?dySeries
The code looks as follows:
library(dygraphs)
lungDeaths <- cbind(ldeaths, mdeaths)
dygraph(lungDeaths, main = "Main Title") %>%
dySeries("ldeaths", drawPoints = FALSE) %>%
dySeries("mdeaths", stepPlot = TRUE, fillGraph = TRUE)
Yielding this plot:
Please, let me know whether this is what you want.
Sometimes you get lucky… I‘ve worked on the same thing a couple of weeks ago and I‘ve found that the documentation is not quite clear on how to do it. But you were pretty close yourself.
How to do it – step by step:
plotter
argument in the dyseries command does not take functions names. But it needs to be a javascript function as plain textBTW, setting the dyPlotter
command did not work because it sets the plotter globally for all dySeries in the plot. At least that‘s what I figure it does.
So without further ado, here‘s my code. I have added some more test data just to show all the functions.
Test data:
library(xts)
library(dygraphs)
test<-xts(matrix(rnorm(100*4), ncol=4, nrow=100), order.by=seq.POSIXt(as.POSIXct("2017-01-01 00:00", tz="UTC"),by=3600, length.out = 100))
colnames(test)<-c("Series_A","Series_B", "Series_C", "Series_D")
Functions:
dy_position<-function(data_final, plot_title, y2_names=NULL, y1_label, y2_label, y1_step=F, y2_step=F, stacked=T){
data_final<-reorder_xts(data_final, y2_names) #reorder necessary so that all y2 are at the right end of the xts. Needed for the multibar plot
dyg <- dygraphs::dygraph(data_final, main=plot_title)
dyg <- dygraphs::dyAxis(dyg, "x", rangePad=20)
dyg <- dygraphs::dyAxis(dyg, "y", label = y1_label,
axisLabelWidth = 90)
y1_names<-colnames(data_final)[!(colnames(data_final) %in%y2_names)]
if (length(y1_names)==1){
stacked<-T #in this case only stacking works
}
if (stacked){
dyg <- dygraphs::dyOptions(dyg,stepPlot=y1_step,stackedGraph = T)
for(i in seq_along(y1_names)) {
dyg <- dygraphs::dySeries(dyg, y1_names[i], axis = "y", strokeWidth = 1.5, stepPlot = y1_step, plotter=" function barChartPlotter(e) {
var ctx = e.drawingContext;
var points = e.points;
var y_bottom = e.dygraph.toDomYCoord(0);
ctx.fillStyle = e.color;
// Find the minimum separation between x-values.
// This determines the bar width.
var min_sep = Infinity;
for (var i = 1; i < points.length; i++) {
var sep = points[i].canvasx - points[i - 1].canvasx;
if (sep < min_sep) min_sep = sep;
}
var bar_width = Math.floor(2.0 / 3 * min_sep);
// Do the actual plotting.
for (var i = 0; i < points.length; i++) {
var p = points[i];
var center_x = p.canvasx;
ctx.fillRect(center_x - bar_width / 2, p.canvasy,
bar_width, y_bottom - p.canvasy);
ctx.strokeRect(center_x - bar_width / 2, p.canvasy,
bar_width, y_bottom - p.canvasy);
}
}")
}
} else {
dyg <- dygraphs::dyOptions(dyg,stepPlot=y1_step)
for(i in seq_along(y1_names)) {
#plotter in function
dyg <- dygraphs::dySeries(dyg, y1_names[i], axis = "y", strokeWidth = 1.5, stepPlot = y1_step, plotter =multibar_combi_plotter(length(y2_names)))
}
}
# put stuff on y2 axis
dyg <- dygraphs::dyAxis(dyg, "y2", label = y2_label, independentTicks = T)
for(i in seq_along(y2_names)) {
dyg <- dygraphs::dySeries(dyg, y2_names[i], axis = "y2", strokeWidth = 1.5, stepPlot = y2_step)
}
return(dyg)
}
#we need to take into account all values and then leave out the ones we do not like
multibar_combi_plotter<-function(num_values){
#plotter function
plotter_text<-"function multiColumnBarPlotter(e) {
// We need to handle all the series simultaneously.
if (e.seriesIndex !== 0) return;
var g = e.dygraph;
var ctx = e.drawingContext;
var sets = e.allSeriesPoints;
var y_bottom = e.dygraph.toDomYCoord(0);
// Find the minimum separation between x-values.
// This determines the bar width.
var min_sep = Infinity;
for (var j = 0; j < sets.length-%s; j++) {
var points = sets[j];
for (var i = 1; i < points.length; i++) {
var sep = points[i].canvasx - points[i - 1].canvasx;
if (sep < min_sep) min_sep = sep;
}
}
var bar_width = Math.floor(2.0 / 3 * min_sep);
var fillColors = [];
var strokeColors = g.getColors();
for (var i = 0; i < strokeColors.length; i++) {
fillColors.push(strokeColors[i]);
}
for (var j = 0; j < sets.length-%s; j++) {
ctx.fillStyle = fillColors[j];
ctx.strokeStyle = strokeColors[j];
for (var i = 0; i < sets[j].length; i++) {
var p = sets[j][i];
var center_x = p.canvasx;
var x_left = center_x - (bar_width / 2) * (1 - j/(sets.length-%s-1));
ctx.fillRect(x_left, p.canvasy,
bar_width/sets.length, y_bottom - p.canvasy);
ctx.strokeRect(x_left, p.canvasy,
bar_width/sets.length, y_bottom - p.canvasy);
}
}
}"
custom_plotter <- sprintf(plotter_text, num_values, num_values, num_values)
return(custom_plotter)
}
reorder_xts<-function(xts_series,line_names){
bar_names<-colnames(xts_series)[!(colnames(xts_series)%in%line_names)]
xts_series<-xts_series[,c(bar_names,line_names)]
return(xts_series)
}
Some Explanation:
dy_position
does all the plotting. It uses individual plotters per series axis.
reorder_xts
is needed to make sure that all lines plots are at the right end of the xts. This is needed for the multibar plot. Because the java script is looping over all series (sets) to determine the width of the bars and we need to make sure we are not looping over the series which are line plots. Otherwise we have additional bars.
multibar_combi_plotter
does exactly that. It takes a numeric parameter lines_names
and modifies the javascript string so that it loops over all plots except for the line_names (i.e. last series in the right part of the xts). Notice several little %s
in the string for the sprintf
command! Afterwards it returns the plotter as character
for the dySeries argument.
All the javascript code is taken directly from the examples in the dygraphs folder.
Here are some examples...
Examples:
dy_position(test,plot_title = "Test1", y2_names = c("Series_C","Series_D"),y1_label = "Axis1", y2_label = "Axis2", stacked=F)
dy_position(test,plot_title = "Test1", y2_names = c("Series_C","Series_D"),y1_label = "Axis1", y2_label = "Axis2", stacked=T)
dy_position(test,plot_title = "Test1", y2_names = c("Series_B","Series_C","Series_D"),y1_label = "Axis1", y2_label = "Axis2", stacked=T)
dy_position(test,plot_title = "Test1", y2_names = c("Series_D"),y1_label = "Axis1", y2_label = "Axis2", stacked=F)
dy_position(test,plot_title = "Test1", y2_names = c("Series_D"),y1_label = "Axis1", y2_label = "Axis2", stacked=T)
dy_position(test,plot_title = "Test1", y2_names = NULL ,y1_label = "Axis1", y2_label = "Axis2", stacked=F)
dy_position(test,plot_title = "Test1", y2_names = NULL ,y1_label = "Axis1", y2_label = "Axis2", stacked=T)