Two-dimensional grouped bar charts

Example

The following example dataset:

val sales = Map(
  ("apple", 2011) -> 43.0,
  ("apple", 2012) -> 83.8,
  ("banana", 2011) -> 11.3,
  ("banana", 2012) -> 77.9,
  ("coconut", 2011) -> 88.0,
  ("coconut", 2012) -> 10.1
)
// sales: scala.collection.immutable.Map[(String, Int),Double] = Map((coconut,2011) -> 88.0, (banana,2012) -> 77.9, (apple,2012) -> 83.8, (apple,2011) -> 43.0, (banana,2011) -> 11.3, (coconut,2012) -> 10.1)

Shared imports

import cats.implicits._
import spire.implicits.DoubleAlgebra
import spire.implicits.IntAlgebra
import axle.visualize.BarChartGrouped
import axle.visualize.Color._

The data can be grouped in two ways to produce bar charts:

val chart = BarChartGrouped[String, Int, Double, Map[(String, Int), Double], String](
  () => sales,
  title = Some("fruit sales"),
  colorOf = (label: String, year: Int) => year match {
    case 2011 => red
    case 2012 => blue
  }
)
// chart: axle.visualize.BarChartGrouped[String,Int,Double,Map[(String, Int),Double],String] = BarChartGrouped(<function0>,true,700,600,50,0.8,20,50,80,Some(fruit sales),None,Courier New,12,Palatino,20,None,None,None,Some(UnittedQuantity(36.0,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),<function2>,<function2>,<function2>)

Create the SVG

import axle.web._
// import axle.web._

svg(chart, "barchart1.svg")

barchart1

Or alternatively

val chart = BarChartGrouped[Int, String, Double, Map[(Int, String), Double], String](
  () => sales map { case (k, v) => (k._2, k._1) -> v},
  colorOf = (year: Int, label: String) => label match {
    case "apple" => red
    case "banana" => yellow
    case "coconut" => brown
  },
  title = Some("fruit sales")
)
// chart: axle.visualize.BarChartGrouped[Int,String,Double,Map[(Int, String),Double],String] = BarChartGrouped(<function0>,true,700,600,50,0.8,20,50,80,Some(fruit sales),None,Courier New,12,Palatino,20,None,None,None,Some(UnittedQuantity(36.0,UnitOfMeasurement(degree,°,Some(http://en.wikipedia.org/wiki/Degree_(angle))))),<function2>,<function2>,<function2>)

Create the second SVG

import axle.web._
// import axle.web._

svg(chart, "barchart2.svg")

barchart2

Animation

This example keeps the “bar” value steady at 1.0 while assigning a new random Double (between 0 and 1) to “foo” every second.

Imports

import axle.visualize._
import spire.implicits._
import scala.util.Random.nextDouble
import axle.jung._
import axle.quanta.Time
import edu.uci.ics.jung.graph.DirectedSparseGraph
import monix.reactive._
import monix.execution.Scheduler.Implicits.global
import axle.reactive.intervalScan
import axle.reactive.CurrentValueSubscriber
import axle.awt.play

Define stream of data updates

val groups = Vector("foo", "bar")
// groups: scala.collection.immutable.Vector[String] = Vector(foo, bar)

val initial = Map("foo" -> 1d, "bar" -> 1d)
// initial: scala.collection.immutable.Map[String,Double] = Map(foo -> 1.0, bar -> 1.0)

val tick = (previous: Map[String, Double]) => previous + ("foo" -> nextDouble)
// tick: Map[String,Double] => scala.collection.immutable.Map[String,Double] = <function1>

implicit val timeConverter = {
  import axle.algebra.modules.doubleRationalModule
  Time.converterGraphK2[Double, DirectedSparseGraph]
}
// timeConverter: axle.quanta.UnitConverterGraph[axle.quanta.Time,Double,edu.uci.ics.jung.graph.DirectedSparseGraph[axle.quanta.UnitOfMeasurement[axle.quanta.Time],Double => Double]] with axle.quanta.TimeConverter[Double] = axle.quanta.Time$$anon$1@21ec06b2

import timeConverter.second
// import timeConverter.second

val dataUpdates: Observable[Map[String, Double]] = intervalScan(initial, tick, 1d *: second)
// dataUpdates: monix.reactive.Observable[Map[String,Double]] = monix.reactive.internal.operators.ScanObservable@120912c8

Create CurrentValueSubscriber, which will be used by the BarChart to get the latest value

val cvSub = new CurrentValueSubscriber[Map[String, Double]]()
val cvCancellable = dataUpdates.subscribe(cvSub)

val chart = BarChart[String, Double, Map[String, Double], String](
  () => cvSub.currentValue.getOrElse(initial),
  title = Some("random")
)

Animate

val paintCancellable = play(chart, dataUpdates)

Tear down the resources

paintCancellable.cancel()
cvCancellable.cancel()