2. Data accessor syntax

The HTML side of the plotting API uses a slight extension of the JavaScript expression syntax to express values to be plotted. Within plotting directives, data values to be plotted, as well as other plot attributes (e.g. marker size, colours, and so on) are specified using an extended JavaScript expression syntax. There are three aspects to this syntax: permitted operators, name scoping and attribute data binding, and standard functions.

In order to distinguish between Radian expressions and raw string values, all Radian expressions are enclosed in double square brackets, e.g. [[x+y]]. This is the same sort of approach as taken in Angular, where Angular expressions are enclosed in double braces. The Radian expression syntax is a superset of full JavaScript expression syntax, so is rather more flexible than the more restricted grammar supported for Angular expressions.


Data accessor operators

Data accessor syntax allows three extensions to standard JavaScript syntax. These are intended to make it more convenient to write the kinds of expressions needed in common plot types.

Exponentiation operator Instead of writing Math.pow(x,y), one may write x**y. The ** operator has a higher precedence than any other binary operator.

Pluck operator If y is an identifier, the expression x#y is equivalent to

x.map(function(e) { return e.y; )}

while if y is an integer literal, x#y is equivalent to

x.map(function(e) { return e[y]; )}

and if expr is a general JavaScript expression, x#(expr) (note parentheses!) is equivalent to

x.map(function(e) { return e[expr]; )}

This allows fields from arrays of objects or arrays of arrays to be plucked out into flat arrays in a simple way (method “plucking” of the form x#f(z) is also supported). This syntax is particularly useful for JSON arrays of objects or for compound object fields within other data. For instance, if we have a data set d with a field date containing an array of date fields, we can extract the day of year of each date as a single array using the expression d.date#dayOfYear(). Note that the only possibilities for the right hand side of the # operator are an identifier, a literal integer or a parenthesised expression (the parentheses are necessary for disambiguation of the x#y and x#(y) cases – in the first case y is an identifier, in the second an expression).

Expression vectorisation Standard functions and arithmentic operators are automatically vectorised, so that, for example, sin(x) calculates either the sine of a single value or an array and x + y adds two arrays, two scalars or a scalar and an array in the natural way. This means that we can express functional plots in a very simple way:

<plot height=300 aspect=3 stroke-width=1>
  <lines x="[[seq(-1,1,100)]]" y="[[-x**3+x+sin(x)]]" stroke="red"></lines>
</plot>

Name scoping and data binding in data accessors

The following names are in scope within data accessor expressions:

What this means is that it is possible to pass data values around using attributes with meaningful names. We implicitly bring data set names into scope as well as simplified standard function names to make writing plot directives quick and simple.

Combined with Angular’s ng-model data binding attribute, we can do some very powerful things in very simple ways:

<div class="form-inline">
  <label>Mean</label>
  <input type="text" ng-model="mu" ng-init="mu=5">
  <label>&nbsp;&nbsp;Standard deviation</label>
  <input type="text" ng-model="sigma" ng-init="sigma=1">
</div>
<br>

<plot height=300 aspect=3 stroke-width=2 stroke="red">
  <lines x="[[seq(0,10,200)]]" y="[[normal(x,mu,sigma)]]"></lines>
</plot>

In this example, updating the values in the input text fields triggers an immediate update of the plot, pulling in the new values from the UI elements. This means that plots can be attached to arbitrary UI elements with ease. Note how names of Angular scope variables defined for instance using ng-model may be used within Radian expressions. The Radian expression parsing infrastructure interacts correctly with Angular data binding so that plots are regenerated when Angular expressions within Radian expressions change.


Handling of dates

Time and date data can be read using a <metadata> directive with a FORMAT="date" attribute. Individual fields of date values can then be plucked from date data using methods of the standard JavaScript Date class: for instance, date#getMonth() extracts the month field from a series of dates. In combination with aggregation functions and arbitrary mapping functions, this allows for various complex manipulations of date data. This example shows extraction of date fields, aggregation (using the unique and meanBy functions) and the use of a user-defined function (midMonths) injected into the plotLib plotting library in the Angular controller used to manage the page:

<plot height=300 aspect=3 stroke-width=2>
  <lines x="vic2012ooa.date" y="vic2012ooa.tmp" stroke="red"></lines>
  <lines x="[[midMonths(unique(vic2012ooa.date#getMonth()), 2012)]]"
         y="[[meanBy(vic2012ooa.prc,vic2012ooa.date#getMonth())]]"
         stroke="blue"></lines>
</plot>

Examples

In this simple example, we use Angular data binding to set the mu and sigma attributes from scope variables called mean and sdev. These “attribute variables” are then in scope for the calculation of x and y attributes for the plot:

<lines mu="[[mean]]" sigma="[[sdev]]"
       x="[[seq(mu-3*sigma,mu+3*sigma,100)]]"
       y="[[normal(x,mu,sigma)]]">
</lines>

The processing of this example goes as follows:

Evaluate     Requires
x mu, sigma
y x, mu, sigma
mu mean
sigma sdev
mean AngularJS scope variable
sdev AngularJS scope variable