Saturnboy
 11.4

Interactive Visualization with Axiis

, , ,

Continuing my Quest for Cool, one of my favorite aspects of data visualization is user interaction. Interactive visualizations are not only cool, but they can be extremely useful getting information to the user.

Here is a screenshot from Google Analytics showing the main line chart of visitors:

google-analytics

When you rollover one of the points, you get a popup telling you the exact number of visitors on that date. This simple rollover, while cool, is not particularly useful to me. If I wanted to know exact numbers, I’d look at an Excel printout. Thankfully, we can use Axiis to provide something much better.

Column Stack Chart

I’ll use Axiis to show two different visualization options for the same data. I made some artificial visitor data per day. Here it is as a vanilla line chart I grabbed from Excel:

data

Not very exciting, but the classic weekend lulls make our dataset a little hard to follow. In XML, it looks like this:

<visitors>
    <week label="A">
        <day label="Sun" val="0.2" />
        <day label="Mon" val="1.6" />
        <day label="Tue" val="2.0" />
        <day label="Wed" val="2.4" />
        <day label="Thu" val="1.8" />
        <day label="Fri" val="1.6" />
        <day label="Sat" val="0.4" />
    </week>
    <week label="B">
        <day label="Sun" val="0.3" />
        <day label="Mon" val="1.2" />
        <day label="Tue" val="1.8" />
        ...
    </week>
    ...
</visitors>

Since the data per day is a little annoying, let’s try to smooth it out by collecting up the day data into weeks. This is easy to do in Axiis by using the ColumnStack layout. Recall that Layouts in Axiis specify how incoming data will be rendered to the display. Here’s an abbreviated snippet of code showing the relevant parts:

<axiis:DataCanvas id="dc" ...>
    <axiis:layouts>
        <axiis:HBoxLayout id="myLayout" ...>
 
            <axiis:layouts>
                <axiis:ColumnStack id="myStack" ...
                    dataProvider="{myLayout.currentDatum.day}"
                    dataField="val" />
            </axiis:layouts>
 
            <axiis:drawingGeometries ... />
        </axiis:HBoxLayout>
    </axiis:layouts>
</axiis:DataCanvas>

First, we use an HBoxLayout layout to render our weeks into columns (the dataProvider is the visitors.week array), and inside that we use a ColumnStack layout to render our days stacked one on top of another (the dataProvider is the current week’s day array).

Here is the result (view source enabled):

Flash is required. Get it here!

The column stack chart is nice because it enables us to distinguish the macro trend over the weeks, traffic is obviously dying off from week A to week H. But it does a poor job illuminating intra-week data. For example, it’s hard to tell how Monday of week A compares to Monday of week B.

Interactive Column-in-Column Chart

Another option to visualize our simulated visitor data is empower the user. Start with a super simple column chart of the aggregated week data, and allow the user to drill down into the day data via some interaction. The implementation I’ve chosen below uses an inset column chart to show the day data when the user mouses over a week column.

In order to display week data, we first need to pre-process our day data to aggregate it into weeks. Axiis makes this trivial with DataSet:

var ds:DataSet = new DataSet();
ds.processXmlString(myXML.toXMLString());
ds.aggregateData(ds.data.object.visitors, "week.day", ["val"]);

The aggregateDate() method walks our XML hierarchy and rolls up aggregates of day.val at every level. When processing is complete, visitors has a new aggregates object that contains day_val_sum (and min, max, and average) for all the days in the entire data set. And one level down, week also has a new aggregates object that contains day_val_sum for just the days in that week.

Next, we construct our visualization using a pair of DataCanvas objects and a pair of BaseLayout layouts, one for the main column chart showing aggregated week data, and the other for an inset column chart showing just the current week’s day data. Here I’m using the same column-chart-the-long-way code as I used last post.

<axiis:DataCanvas id="dc" ...>
    <axiis:layouts>
        <axiis:BaseLayout id="myLayout" ...
            itemMouseOver="mouseOverHandler(event)">
 
            <axiis:drawingGeometries>
                <degrafa:RegularRectangle id="myBar" ... />
                <degrafa:RasterText id="myBarLabel"... />
            </axiis:drawingGeometries>
 
            <axiis:referenceRepeater ... />
 
            <axiis:states>
                <axiis:State
                    enterStateEvent="mouseOver"
                    exitStateEvent="mouseOut" ... />
            </axiis:states>
        </axiis:BaseLayout>
    </axiis:layouts>
</axiis:DataCanvas>
 
<axiis:DataCanvas id="dc2" ...>
    <axiis:layouts>
        <axiis:BaseLayout id="myLayout2" ...>
 
            <axiis:drawingGeometries>
                <degrafa:RegularRectangle ... />
                <degrafa:RasterText ... />
            </axiis:drawingGeometries>
 
            <axiis:referenceRepeater ... />
        </axiis:BaseLayout>
    </axiis:layouts>
</axiis:DataCanvas>

The interaction is achieved by the itemMouseOver event on the main column chart’s layout. Note that we are not listening for events on the columns themselves, rather we listen on the parent BaseLayout. The layout is responsible for attaching individual listeners to all of its children as they are rendered to the display. Layouts have events for all possible mouse interactions, including click, double-click, mouse over, mouse out, etc. This is one of the nicest features of Axiis. Since user interaction is managed by the layout and not by the rendered geometry, I change whatever I want about the geometry or how it is rendered and user interactions are preserved. The code is also pleasantly clean.

What’s more, the main column chart uses an Axiis State to create a visual rollover effect on the columns. The State is used to trigger some visual changes (column stroke weight is increased and label font is bolded) on mouseOver and then reverts the changes on mouseOut.

Here is the event handler attached to the itemMouseOver event on the layout:

private function mouseOverHandler(e:LayoutItemEvent):void {
    //change dataset to current mouseOver'd column
    myLayout2.dataProvider = e.item.data.day;
 
    //set color
    var color:uint = myPalette.colors[e.item.index];
    myPalette2.colorFrom = color;
    myPalette2.colorTo = PaletteUtils.darker(PaletteUtils.darker(color));
 
    //hide text
    insetText.visible = false;
}

The LayoutItemEvent event contains all kinds of useful information when it arrives at our handler. First, we set the dataProvider of our inset chart using the day data from the highlighted week column. Next, we use some Degrafa color magic to set the colors of the inset’s columns from the color of the highlighted week column. And finally, we hide the reminder text.

Here is the result (view source enabled):

Flash is required. Get it here!

The end result is a fairly cool visualization that does a good job initially hiding unnecessary information, but later reveals it upon user interaction. For example, toggle back and forth between week B and week C. You can easily see a shift in traffic to the first half of week C compared to week B.

Postscript

I recently discovered another amazing data visualization framework. Protovis is an awesome Javascript library created by some hella smart dudes at Stanford. If I ever needed to do any visualizations on the web, and was banned from using Flex, I’d definitely use Protovis.

Files

Comments are closed

© 2017 saturnboy.com