April 12, 2016

Histogram + line combo charts

Recently I have faced a problem of creating histogram + line combo chart. My first attempt was using google-charts but the problem is that according to the documentation it is not possible to use histogram type in a combo charts. the same problem I had with Plotly.js library.  Fortunately these libraries are very flexible so I tried to use vertical bar (column) chart instead of the histogram.

Fig.1. Bar chart
Fig.2. Histogram + line combo chart


                                     






Nevertheless  bar chart x-axis has ticks (labels) for every bar (see Fig.1) instead of the “from-to” range as in normal histogram or line chart (Fig.2). So the post is about how to fix that for both libraries. The main trick is in shift of the plot to the right. lets see how it’s done in codepen for google-chart and for plotly.js

code for google-chart is below:

google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawVisualization);
 
function drawVisualization() {
  var config = [{color: 'lightblue', label: 'Select'}, {color: 'blue', label: 'CTX'}, {color: 'green', label: '100%'}];
   
  var data = new google.visualization.DataTable();
  data.addColumn('number', 'Amount');
  data.addColumn({type: 'string', role: 'tooltip', 'p': {'html': true}});
  data.addColumn('number', config[0].label); 
  data.addColumn('number', config[1].label);
  data.addColumn('number', config[2].label);
  data.addRows([
     [0.5, null, 50, 80, 111],
     [1.5, null, 30, 170, 200],
     [2.5, null, 300, 300, 300],
     [3.5, null, 250, 310, 370],
     [4.5, null, 150, 240, 250]
  ]);
 
  // setting tooltip
  for (var i = 0, rows = data.getNumberOfRows(), month; i < rows; i++) {
      data.setValue(i, 1,`<div class="gc-tooltip">
           <div style="color:${config[0].color;">
                Select:<span class="gc-right">${data.getValue(i,2)}</span>
           </div>
           <div style="color:${config[1].color;">
                CTX:<span class="gc-right">${data.getValue(i,3)}</span>
           </div>
           <div style="color:${config[2].color;">
                100%:<span class="gc-right">${data.getValue(i,4)}</span>
           </div>
           </div>`);
  }
 
  var options = {
    vAxis: {
        title: 'Trace Frequency',
      gridlines: {color: 'gainsboro'}
    },
    hAxis: {
      title: 'Amount', format: '#K',
      ticks: [0, 1, 2 , 3, 4, 5],
      gridlines: {color: 'white'}
    },
    series: {
      0: {type: 'bars', color: config[0].color},
        1: {type: 'line', color: config[1].color},
        2: {type: 'line', color: config[2].color}
    },
    legend: "none",
    bar: {groupWidth: "98%"},
    pointSize: 8,
    focusTarget: 'category',
    tooltip: {isHtml: true},
    width: 600,
    height: 400,
  };
 
  var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
  chart.draw(data, options);
}
code for plotly.js is below:
var trace0 = {
  
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [111,200,300,370,250], 
  type: 'scatter',
  marker: {
    color: 'green'
  },
  hoverinfo: 'y'
};
 
var trace1 = {
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [80, 170, 300, 310, 240], 
  type: 'scatter',
  marker: {
    color: 'blue'
  },
  hoverinfo: 'y'
};
 
var trace2 = {
  x: [0.5, 1.5, 2.5, 3.5, 4.5], 
  y: [50,30,300,250,150],
  type: 'bar',
  marker: {
    color: 'lightblue'
  },
  hoverinfo: 'y'
};
 
var layout = {
  xaxis: {
        zeroline: false,
        title: 'Amount',
        range: [0, 5],
        type: 'linear',
        dtick: 1,
        ticksuffix: 'K'
  },
  yaxis: {
        title: 'Trace Frequency',
        range: [0, 400],
        type: 'linear'
  },
  bargap :0.01,
  margin: {
    pad: 10
  },
  showlegend: false,
  hovermode: 'x',
}
 
Plotly.newPlot('myDiv', [trace0, trace1, trace2], layout);

Bootstrap 3 carousel with multiple items.

As you probably know bootstrap 3 carousel shows only one item at a time.
But what if I want it to show 2 or more at a time? or even make it responsive?
I’ve searched the internet and have some solutions which for some reason didn’t work correctly.
Instead of sliding items one by one it was sliding all of the shown items together replacing them with a new set of items (so pretty much the same behavior as in single item carousel).
At that time I had bootstrap v 3.3.2.
After spending some time on investigation how to fix that I figured out that newest updates to the bootstrap 3 css and script files changes the behavior of carousel.
I’ve found that with bootstrap 3.2.0 multiple items carousel behaves as I would expect this.

here is a codepen example of responsive multiple items carousel using bootstrap 3.2.0.
the code is below:

Html:
<div class="container">
 <div class="row">
  <div class="col-xs-11 col-md-10 col-centered">

   <div id="carousel" class="carousel slide" data-ride="carousel" data-type="multi" data-interval="2500">
    <div class="carousel-inner">
     <div class="item active">
      <div class="carousel-col">
       <div class="block red img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block green img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block blue img-responsive"></div>
      </div>
     </div>
     <div class="item">
      <div class="carousel-col">
       <div class="block yellow img-responsive"></div>
      </div>
     </div>
    </div>

    <!-- Controls -->
    <div class="left carousel-control">
     <a href="#carousel" role="button" data-slide="prev">
      <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
      <span class="sr-only">Previous</span>
     </a>
    </div>
    <div class="right carousel-control">
     <a href="#carousel" role="button" data-slide="next">
      <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
      <span class="sr-only">Next</span>
     </a>
    </div>
   </div>

  </div>
 </div>
</div>
Js:

$('.carousel[data-type="multi"] .item').each(function() {
 var next = $(this).next();
 if (!next.length) {
  next = $(this).siblings(':first');
 }
 next.children(':first-child').clone().appendTo($(this));

 for (var i = 0; i < 2; i++) {
  next = next.next();
  if (!next.length) {
   next = $(this).siblings(':first');
  }

  next.children(':first-child').clone().appendTo($(this));
 }
});
Css:

.col-centered {
    float: none;
    margin: 0 auto;
}

.carousel-control { 
    width: 8%;
    width: 0px;
}
.carousel-control.left,
.carousel-control.right { 
    margin-right: 40px;
    margin-left: 32px; 
    background-image: none;
    opacity: 1;
}
.carousel-control > a > span {
    color: white;
   font-size: 29px !important;
}

.carousel-col { 
    position: relative; 
    min-height: 1px; 
    padding: 5px; 
    float: left;
 }

 .active > div { display:none; }
 .active > div:first-child { display:block; }

/*xs*/
@media (max-width: 767px) {
  .carousel-inner .active.left { left: -50%; }
  .carousel-inner .active.right { left: 50%; }
 .carousel-inner .next        { left:  50%; }
 .carousel-inner .prev       { left: -50%; }
  .carousel-col                { width: 50%; }
 .active > div:first-child + div { display:block; }
}

/*sm*/
@media (min-width: 768px) and (max-width: 991px) {
  .carousel-inner .active.left { left: -50%; }
  .carousel-inner .active.right { left: 50%; }
 .carousel-inner .next        { left:  50%; }
 .carousel-inner .prev       { left: -50%; }
  .carousel-col                { width: 50%; }
 .active > div:first-child + div { display:block; }
}

/*md*/
@media (min-width: 992px) and (max-width: 1199px) {
  .carousel-inner .active.left { left: -33%; }
  .carousel-inner .active.right { left: 33%; }
 .carousel-inner .next        { left:  33%; }
 .carousel-inner .prev       { left: -33%; }
  .carousel-col                { width: 33%; }
 .active > div:first-child + div { display:block; }
  .active > div:first-child + div + div { display:block; }
}

/*lg*/
@media (min-width: 1200px) {
  .carousel-inner .active.left { left: -25%; }
  .carousel-inner .active.right{ left:  25%; }
 .carousel-inner .next        { left:  25%; }
 .carousel-inner .prev       { left: -25%; }
  .carousel-col                { width: 25%; }
 .active > div:first-child + div { display:block; }
  .active > div:first-child + div + div { display:block; }
 .active > div:first-child + div + div + div { display:block; }
}

.block {
 width: 306px;
 height: 230px;
}

.red {background: red;}

.blue {background: blue;}

.green {background: green;}

.yellow {background: yellow;}