A graphical toolkit for visualization
Protovis
Overview
Examples
Documentation
Download
Index
« Previous / Next »

Index Charts

View full screen.

With some time series, raw values are less important than relative change. Consider investors, who may be more interested in a stock’s growth than its price. Multiple stocks may have dramatically different baseline prices, but be meaningfully compared when normalized. An index chart is an interactive line chart that shows percentage changes for a collection of time-series based on a selected index point.

In this example, we see the percentage change of selected stock prices according to the day of purchase. For January 2005, one can see the rocky rise enjoyed by those who invested in Amazon, Apple, or Google at that time. Mouseover the chart to change the reference month.

Next: Parallel Coordinates

Source

<html>
  <head>
    <title>Index Chart</title>
    <link type="text/css" rel="stylesheet" href="ex.css?3.2"/>
    <script type="text/javascript" src="../protovis-r3.2.js"></script>
    <script type="text/javascript" src="stocks.js"></script>
    <style type="text/css">

#fig {
  width: 860px;
  height: 400px;
}

    </style>
  </head>
  <body><div id="center"><div id="fig">
    <script type="text/javascript+protovis">

var data = [],
    fy = function(d) d.price, // y-axis value
    fx = function(d) d.index,  // x-axis value
    ft = function() data[this.parent.index].ticker, // label
    w = 730,
    h = 360,
    S = pv.max(pv.values(stocks), function(s) s.values.length),
    idx = Math.floor(S/2) - 1,
    x = pv.Scale.linear(0,S-1).range(0,w),
    y = pv.Scale.linear(-1,5).range(0,h),
    rescale = true;

/* Normalize the data according to an index point. */
var indexify = function(data, cols, idx) {
    return cols.map(function(c) {
        var v = data[c].values[idx];
        return { ticker: c,
            values: data[c].values.map(function(d,i) {
	                  return {index:i, price:((d-v)/v)}; }) 
        }
    });
};

/* Compute new index values, rescale if needed, and render. */
var update = function() {
    data = indexify(stocks, names, idx);
    if (rescale) {
      var min = pv.min(data.map(function(d) pv.min(d.values, fy)));
      var max = pv.max(data.map(function(d) pv.max(d.values, fy)));
    }
    y.domain(min, max).nice();
    vis.render();
}

/* The visualization panel. Stores the active index. */
var vis = new pv.Panel()
    .def("i", -1)
    .left(60)
    .right(70)
    .top(20.5)
    .bottom(18)
    .width(w)
    .height(h);

/* Horizontal gridlines showing %-change. */
vis.add(pv.Rule)
    .data(function() y.ticks(8))
    .bottom(y)
    .strokeStyle(function(d) d==0 ? "black" : "#cccccc")
  .anchor("left").add(pv.Label)
    .text(function(d) (d * 100).toFixed(0) + "%");

/* Y-axis label */
vis.add(pv.Label)
    .data(["Gain / Loss Factor"])
    .left(-45)
    .bottom(h/2)
    .font("10pt Arial")
    .textAlign("center")
    .textAngle(-Math.PI/2);

/* Stock lines. */
vis.add(pv.Panel)
    .data(function() data)
  .add(pv.Line)
    .data(function(d) d.values)
    .left(x.by(fx))
    .bottom(y.by(fy))
    .lineWidth(2)
  .add(pv.Label)
    .visible(function() this.index == S-1)
    .textBaseline("middle")
    .textMargin(6)
    .text(ft);

/* Current index line. */
vis.add(pv.Rule)
    .visible(function() idx >= 0 && idx != vis.i())
    .left(function() x(idx))
    .top(-4)
    .bottom(-4)
    .strokeStyle("red")
  .anchor("bottom").add(pv.Label)
    .text(function() stocks.Date.values[idx]);

/* An invisible bar to capture events (without flickering). */
vis.add(pv.Panel)
    .events("all")
    .event("mousemove", function() { idx = x.invert(vis.mouse().x) >> 0; update(); });

update();

    </script>
  </div></div></body>
</html>

Data

Due to size, the data file is omitted from this example. See stocks.js.
Copyright 2010 Stanford Visualization Group