# Displaying results as charts Numerical results are often easier to understand when rendered as a chart than a list of numbers. The SWISH web interface for SWI-Prolog allows rendering data through [C3.js](http://c3js.org). C3 is a JavaScript library that used [D3.js](http://d3js.org) for the actual rendering. However, C3.js can create many useful charts just from data, while D3.js typically requires writing JavaScript and CSS. ## How it works Creating a C3 chart requires including the directive `:- use_rendering(c3).` and binding a Prolog variable to a _dict_ with the _tag_ `c3` and the data needed by C3. The `c3` renderer interferes in two ways with the data: - If no size is specified, the width is set to 85% of the available width and the height to `width/2+50`. The chart is resized if the available space changes. - The renderer performs some basic sanity checks on the data and may report an error. ## Our first chart As a first example, we will create a chart for the _sine_ function in the range `0..360` degrees. The predicate sin/2 defines the sine relation for 37 datapoints on 10 degrees intervals. We use findall/3 to create the required data rows. Now, we instantiate the C3 data structure, specifying the row names and the X-axis.
:- use_rendering(c3). sin(X,Y) :- between(0,36,I), X is I*10, Y is sin(X*pi/180). chart(Chart) :- findall([X,Y], sin(X,Y), Data), Chart = c3{data:_{x:x, rows:[[x,sine]|Data]}}.
chart(Sine).
## Alternatives for the the data By nature, Prolog is a relational language and the set of solutions naturally form a table, where each solution is a row. C3 wants to see a JSON array-of-arrays, where each sub-array is a row and the first row defines the column names, much as you are used to in a spreadsheet. The above creates the data as follows: ``` rows: [ [x, sine], [0, 0.0], [10, 0.173...], ... ] ``` The c3 rendering library allows the rows to be compound terms. If we have a simple X-Y chart we can thus represent the same data as Prolog _pairs_, a representation that is more common in the Prolog world. ``` rows: [ x - sine, 0 - 0.0, 10 - 0.173..., ... ] ``` We can use _dicts_. If we do so, we can omit the first row that defines the column names as the dict _keys_ are used for that. So, the data can be represented as: ``` rows: [ r{x:0, sin:0.0}, r{x:10, sin:0.173} ... ] ``` Predicates from library(dicts) can be used to combine series. For example, dicts_join/4 can be used to combine two series on a common key. The example below illustrates this.
:- use_rendering(c3). sin(X,Y) :- between(0,36,I), X is I*10, Y is sin(X*pi/180). cos(X,Y) :- between(0,36,I), X is I*10, Y is cos(X*pi/180). chart(Chart) :- findall(r{x:X,sin:Y}, sin(X,Y), SinData), % create sin-series findall(r{x:X,cos:Y}, cos(X,Y), CosData), % create cos-series dicts_join(x, SinData, CosData, Data), % join on common 'x' key Chart = c3{data:_{x:x, rows:Data}}.
chart(Chart).
You can also represent data with colums. In this case the data should be a list of lists where each sublist represent a series of values and the first element is the name of the series. ``` columns: [ [x,0,10,...], [sine,0.0, 0.173,..], [cosine, 1.0, ...] ] ```
:- use_rendering(c3). sin(X,Y) :- between(0,36,I), X is I*10, Y is sin(X*pi/180). cos(X,Y) :- between(0,36,I), X is I*10, Y is cos(X*pi/180). chart(Chart) :- findall(X, sin(X,Y), XData), % create x values findall(Y, sin(X,Y), SinData), % create sin-series findall(Y, cos(X,Y), CosData), % create cos-series Chart = c3{data:_{x:x, columns:[ [x|XData],[sine|SinData],[cosine|CosData]]}}.
chart(Chart).
## Creating pie and bar charts In this example we compute some simple statistics on HTML pages. First, we create a program that created a sorted list of `[Tag,Count]` pairs for each element that appears on a page. Note that the pairs are typically represented as `Tag-Count`, but this representation does not fit C3 well. In this example we stay with the C3 representation.
:- use_rendering(c3). :- use_module(library(sgml)). :- use_module(library(xpath)). popular_elements(Popular) :- popular_elements('http://www.swi-prolog.org', Popular). popular_elements(URL, Popular) :- findall(Elem, elem_in(URL, Elem), Elems), frequency_count(Elems, Popular). frequency_count(Elems, Popular) :- sort(0, =<, Elems, Sorted), count_same(Sorted, Counted), sort(2, >=, Counted, Popular). count_same([], []). count_same([H|T0], [H-C|T]) :- count_same(H, T0, 1, C, T1), count_same(T1, T). count_same(E, [E|T0], C0, C, T) :- !, C1 is C0+1, count_same(E, T0, C1, C, T). count_same(_, T, C, C, T). elem_in(URL, Elem) :- load_html(URL, DOM, []), xpath(DOM, //'*'(self), element(Elem,_,_)).
Below, we run this program in three different queries. 1. Traditional Prolog. 2. Rendered as a C3 pie chart. 3. Rendered as a bar chart. The latter requires us to name the rows, define the x-axis and specify that the x-axis must be rendered as text.
popular_elements(Pairs).
popular_elements(_Pairs), Chart = c3{data:_{columns:_Pairs, type:pie}}.
popular_elements(_Pairs), Chart = c3{data:_{x:elem, rows:[elem-count|_Pairs], type:bar}, axis:_{x:_{type:category}}}.
## Further reading C3 defines most standard charts and many options to make the resulting charts look nice by providing labels for the axis, adding a legenda, choosing color schemes, etc. Please visit the [C3 examples](http://c3js.org/examples.html) for details.