Widget Tricks - ods-analysis, quick intro

The technical documentation says :

This widget exposes the results of an analysis (as an object containing a results array and optionally an aggregations object) in a variable available in the scope. It can be used with AngularJS’s ngRepeat to simply build a table of analysis results.

Normal people would say :

Ods-analysis is the way to get the values behind a chart ! What you can do with a chart, you can do the same with ods-analysis. You get the figures, and do whatever you want in your dashboard with the results !

The syntax is different, but options and parameters are pretty similar. You have a context, an X axis, you define series, optionnaly you sort the results. To conclude, we can say that : ods-analysis is the low level widget of ods-chart, its underlying layer !

What an ods-analysis results object looks like

The ods-analysis output is a json object, containing one key results. It’s value is a json array and each item contains the x value of the analysis, and one to many series.

Compare, side-by-side the ods-chart values and the ods-analysis results. They both have the same settings (x axis, series, number of results and sort parameters)

<ods-dataset-context context="shanghaiworlduniversityranking"
                     shanghaiworlduniversityranking-dataset="shanghai-world-university-ranking"
                     shanghaiworlduniversityranking-domain="public"
                     shanghaiworlduniversityranking-parameters="{'sort':'world_rank'}">
    <div class="row">
        <div class="col-md-7">
            <ods-chart single-y-axis="true"
                       min="0" max="110" step="10"
                       labels-x-length="43"
                       label-x="University name">
                <ods-chart-query context="shanghaiworlduniversityranking"
                                 field-x="university_name"
                                 maxpoints="10" sort="serie1-3">
                    <ods-chart-serie expression-y="pub"
                                     chart-type="column"
                                     function-y="MAX"
                                     label-y="Best Publication score" color="#FF515A"
                                     display-values="true">
                    </ods-chart-serie>
                    <ods-chart-serie expression-y="alumni"
                                     chart-type="column"
                                     function-y="MAX"
                                     label-y="Best Alumni score" color="#1B6698"
                                     display-values="true">
                    </ods-chart-serie>
                    <ods-chart-serie expression-y="award"
                                     chart-type="column"
                                     function-y="MAX"
                                     label-y="Best Award score" color="#FCD23B"
                                     display-values="true">
                    </ods-chart-serie>
                </ods-chart-query>
            </ods-chart>
        </div>
        <div class="col-md-5">
            <div ods-analysis="analysisshanghai"
                 ods-analysis-context="shanghaiworlduniversityranking"
                 ods-analysis-max="10"
                 ods-analysis-x="university_name"
                 ods-analysis-serie-bestpubscore="MAX(pub)"
                 ods-analysis-serie-bestalumniscore="MAX(alumni)"
                 ods-analysis-serie-bestawardscore="MAX(award)"
                 ods-analysis-sort="bestawardscore">
                <pre ng-bind="analysisshanghai|json"></pre>
            </div>
        </div>
    </div>
</ods-dataset-context>

Top 10

Like with charts, it’s conveniant to use ods-analysis to sort aggregations by the computed serie (like for exemple the sum or average of numerical fields). In this example, the ods-analysis results object is used to print lines of an HTML table to display the information as a table.

<ods-dataset-context context="population"
                     population-domain="discovery"
                     population-dataset="population-millesimee-communes-2016@public">
    <div class="row">
        <div class="col-md-6">
            <ods-chart single-y-axis="true" scientific-display="true" labels-x-length="18" align-month="true">
                <ods-chart-query context="population" field-x="nom_de_la_commune" maxpoints="10" sort="serie1-1">
                    <ods-chart-serie expression-y="population_totale" chart-type="column" function-y="MAX"
                                     label-y="Population 2016" color="#142E7B" display-values="true"
                                     scientific-display="true">
                    </ods-chart-serie>
                </ods-chart-query>
            </ods-chart>
        </div>
        <div class="col-md-6">
            <div ods-analysis="analysispopulation"
                 ods-analysis-context="population"
                 ods-analysis-max="10"
                 ods-analysis-x="nom_de_la_commune"
                 ods-analysis-serie-population="MAX(population_totale)"
                 ods-analysis-sort="population">
                <div id="top10">
                    <table>
                        <tr>
                            <th>Position</th>
                            <th>Commune</th>
                            <th>Population</th>
                        </tr>
                        <tr ng-repeat="(position,analyse) in analysispopulation.results">
                            <td>{{ position + 1 }}</td> <!-- Start at 0 -->
                            <td>{{ analyse.x | capitalize }}</td>
                            <td>{{ analyse.population | number }}</td>
                        </tr>
                    </table>
                </div>
            </div>
        </div>
    </div>
</ods-dataset-context>
#top10 table {
    width: 100%;
}
#top10 tr th, #top10 tr td {
    padding: 5px;
}
#top10 table td, #top10 table th {
    text-align: center;
}
#top10 tr:nth-child(odd) {
    background-color: #f1f1f1;
}
#top10 tr:nth-child(1) { /* Headers */
    background-color: #142e7a;
    color: white;
}
#top10 tr:nth-child(2) { /* First pos. */
    font-weight: 800;
    font-size: 1.4em;
}
#top10 tr:nth-child(3) { /* Second pos. */
    font-weight: 700;
    font-size: 1.3em;
}
#top10 tr:nth-child(4) { /* Third pos. */
    font-weight: 600;
    font-size: 1.2em;
}

Min/Max, Best/Worst, Size/Length

Ods-analysis results can also be processed to get the minimum and maximum value of a serie, and also compute the length of the array. By doing so, you can get interesting information like the biggest city or smallest city in your dataset by summing the population as a serie for example.

When you analyse your domain statistics, you can compute an ods-analysis with the user ID or IP address as the X axis, and then get the length of the ods-analysis results. It then correspond to the number of different users that reached your domain.

In the following example, we get the best or worst universities, by ranking score.

<ods-dataset-context context="shanghai"
                     shanghai-dataset="shanghai-world-university-ranking"
                     shanghai-domain="public">
    <div class="row">
        <div class="col-md-6">
            <h3>
                Best Universities (regarding publication ranking)
            </h3>
            <ods-chart single-y-axis="true" align-month="true">
                <ods-chart-query context="shanghai"
                                 field-x="university_name" maxpoints="5" sort="serie1-1">
                    <ods-chart-serie expression-y="pub" chart-type="column" function-y="AVG"
                                     label-y="Average publication ranking" color="#142E7B" display-units="false"
                                     display-values="true" scientific-display="true">
                    </ods-chart-serie>
                </ods-chart-query>
            </ods-chart>
            <h3>
                Worst Universities (regarding publication ranking)
            </h3>
            <ods-chart single-y-axis="true" align-month="true">
                <ods-chart-query context="shanghai"
                                 field-x="university_name" maxpoints="5" sort="-serie1-1">
                    <ods-chart-serie expression-y="pub" chart-type="column" function-y="AVG"
                                     label-y="Average publication ranking" color="#142E7B" display-units="false"
                                     display-values="true" scientific-display="true">
                    </ods-chart-serie>
                </ods-chart-query>
            </ods-chart>
        </div>

        <div class="col-md-6">
            <ods-dataset-context context="shanghai"
                                 shanghai-dataset="shanghai-world-university-ranking"
                                 shanghai-domain="public">
                <div ods-analysis="analysisminmax"
                     ods-analysis-context="shanghai"
                     ods-analysis-max="0"
                     ods-analysis-x="university_name"
                     ods-analysis-serie-pubrank="AVG(pub)"
                     ods-analysis-sort="pubrank">
                    <h2>
                        First and last items of ods-analysis
                    </h2>
                    <h3>
                        Best University (regarding publication ranking)
                    </h3>
                    <p>
                        <b>{{ analysisminmax.results[0].x }}</b> with a score of <b>{{ analysisminmax.results[0].pubrank
                        }}</b>
                    </p>
                    <h3>
                        Worst University (regarding publication ranking)
                    </h3>
                    <p>
                        <b>{{ analysisminmax.results[analysisminmax.results.length-1].x }}</b> with a score of <b>{{
                        analysisminmax.results[analysisminmax.results.length-1].pubrank | number : 2 }}</b>
                    </p>
                    <br/><br/>
                    <div ods-subaggregation="analysisminmax.results"
                         ods-subaggregation-serie-bestrank="MAX(pubrank)"
                         ods-subaggregation-serie-worstrank="MIN(pubrank)">
                        <h2>
                            Max and min values of ods-analysis results
                        </h2>
                        <p>
                            Best ranking : <b>{{ results[0].bestrank | number }}</b>
                        </p>
                        <p>
                            Worst ranking : <b>{{ results[0].worstrank | number:2 }}</b>
                        </p>
                    </div>
                    <br/><br/>
                    <h2>
                        Length of ods-analysis.results
                    </h2>
                    <p>
                        Number of universities in this ranking : <b>{{ analysisminmax.results.length | number }}</b>
                    </p>
                </div>
            </ods-dataset-context>
        </div>
    </div>
</ods-dataset-context>

Multiple aggregations at once

Instead of using several contexts filtered on several values of the same field, and then computing an ods-aggregation for each context. An ods-analysis if often the good choice to compute them all with only one API Call (it’s way faster and more efficient).

<ods-dataset-context context="population"
                     population-domain="public"
                     population-dataset="population-millesimee-communes-2016">
    <div class="row">
        <div class="col-md-5">
            <ods-chart single-y-axis="true" scientific-display="true" labels-x-length="18" align-month="true">
                <ods-chart-query context="population" field-x="nom_de_la_commune" maxpoints="9" sort="serie1-1">
                    <ods-chart-serie expression-y="population_totale" chart-type="column" function-y="MAX"
                                     label-y="Population 2016" color="#142E7B" display-values="true"
                                     scientific-display="true">
                    </ods-chart-serie>
                </ods-chart-query>
            </ods-chart>

        </div>
        <div class="col-md-7">
                <div ods-aggregation="populationtotal"
                     ods-aggregation-context="population"
                     ods-aggregation-function="SUM"
                     ods-aggregation-expression="population_totale">
                    <h3>
                        Overall population (City population sum)
                    </h3>
                    <p>
                        <b>{{ populationtotal | number }}</b> residents
                    </p>
                    <div ods-analysis="analysispopulation"
                         ods-analysis-context="population"
                         ods-analysis-max="9"
                         ods-analysis-x="nom_de_la_commune"
                         ods-analysis-serie-population="MAX(population_totale)"
                         ods-analysis-sort="population">
                        <div class="row">
                            <div class="col-xs-4" ng-repeat="(position,analyse) in analysispopulation.results">
                                <div class="kpi-bloc"
                                     ng-class="{'top3': position < 3}">
                                    <div class="position">
                                        {{ position + 1 }} <!-- Start at 0 -->
                                    </div>
                                    <div class="commune">
                                        {{ analyse.x | capitalize }}
                                    </div>
                                    <div class="population">
                                        {{ analyse.population | number }}
                                    </div>
                                    <div class="ratio">
                                        (<b>{{ analyse.population / populationtotal * 100 | number : 2 }}%</b> of total population)
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
        </div>
    </div>
</ods-dataset-context>
.kpi-bloc {
    border: 1px solid;
    padding: 2px;
    margin-bottom: 15px;
    text-align: center;
    height: 130px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}
.kpi-bloc.top3 {
    background-color: #fbd66f6b;
}
.commune {
    font-weight: 100;
    font-size: 1.2em;
    line-height: 20px;
}
.position {
    font-size: 1.1em;
}
.population {
    font-weight: 700;
}
.ratio {
    font-size: 0.9em;
}

HTML/CSS Home-made chart

For specific cases, ods-analysis, combined with some advanced HTML and CSS can be a complex but smart choice for data visualizations.

In this example, a very simple bar chart is made by creating one line for each X values, the width of the bar is defined by its serie. Then, by clicking on the bar, it expends and opens a chart to have more details on the selected information.

<ods-dataset-context context="populationmillesime"
           populationmillesime-domain="public"
           populationmillesime-dataset="population-francaise-communes"
           populationmillesime-parameters="{'refine.annee_recensement':'2016'}">
  <div class="row">
    <div class="col-md-5">
        <ods-chart single-y-axis="true" scientific-display="true" labels-x-length="18" align-month="true">
              <ods-chart-query context="populationmillesime" field-x="nom_de_la_commune" maxpoints="8" sort="serie1-1">
                  <ods-chart-serie expression-y="population_totale" chart-type="bar" function-y="MAX"
                                   label-y="2016 Census population" color="#142E7B" display-values="true" scientific-display="true">
                  </ods-chart-serie>
              </ods-chart-query>
        </ods-chart>
    </div>
    <div class="col-md-7">
          <h3>
              Top cities by population, click to see the evolution over years
          </h3>
          <div ods-analysis="analysispopulationhomemadechart"
               ods-analysis-context="populationmillesime"
               ods-analysis-max="8"
               ods-analysis-x="nom_de_la_commune"
               ods-analysis-serie-population="MAX(population_totale)"
               ods-analysis-sort="population">
              <div ods-subaggregation="analysispopulationhomemadechart.results"
                   ods-subaggregation-serie-max="MAX(population)">
                  <div class="homemadechart"
                       ng-init="select = {'commune':''}">
                      <div class="homemadechart__line"
                           ng-repeat="(position,analysehomemadechart) in analysispopulationhomemadechart.results">
                          <div class="homemadechart__line-progressionbar"
                               ng-click="select.commune = analysehomemadechart.x"
                               ng-class="{'selected': select.commune == analysehomemadechart.x}"
                               style="width: {{ analysehomemadechart.population / results[0].max * 100 }}%">
                              <div class="homemadechart__line-commune">
                                  {{ analysehomemadechart.x | capitalize }}
                              </div>
                              <div class="homemadechart__line-population">
                                  {{ analysehomemadechart.population | number }}
                              </div>
                          </div>
                          <div class="homemadechart__line-progressionbackground">
                          </div>
                          <div class="homemadechart__focus"
                               ng-if="select.commune == analysehomemadechart.x">
                              <div class="homemadechart__focus__close"
                                   ng-click="select.commune = undefined">
                                  <i class="fa fa-times" aria-hidden="true"></i>
                              </div>
                              <h4>
                                  Population evolution in {{ analysehomemadechart.x }} since 2006
                              </h4>
                              <ods-dataset-context context="historic"
                                                   historic-domain="public"
                                                   historic-dataset="population-francaise-communes"
                                                   historic-parameters="{'q': 'nom_de_la_commune:&quot;' + analysehomemadechart.x + '&quot;'}">
                                  <ods-chart timescale="year" single-y-axis="true"
                                             logarithmic="true" align-month="true">
                                      <ods-chart-query context="historic"
                                                       field-x="annee_recensement" maxpoints="0"
                                                       timescale="year">
                                          <ods-chart-serie expression-y="population_totale"
                                                           chart-type="column"
                                                           function-y="MAX" color="#142E7B"
                                                           scientific-display="true">
                                          </ods-chart-serie>
                                      </ods-chart-query>
                                  </ods-chart>
                              </ods-dataset-context>
                          </div>
                      </div>
                  </div>
              </div>
          </div>
    </div>
</ods-dataset-context>
.homemadechart__line {
    position: relative;
    width: 100%;
    margin: 5px 0;
}
.homemadechart__line-progressionbar {
    position: absolute;
    background-color: #142e7a;
    color: white;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-radius: 1000px;
    height: 30px;
    padding: 0 10px;
    border: 2px solid #e0e0e0;
}
.homemadechart__line-progressionbar:hover, .homemadechart__line-progressionbar.selected {
    border-color: #142e79;
    font-weight: 600;
}
.homemadechart__line-progressionbackground {
    width: 100%;
    height: 30px;
    background-color: #e0e0e0;
    display: block;
    border-radius: 1000px;
}
.homemadechart__focus {
    position: relative;
    padding: 10px 15px 0px;
}
.homemadechart__focus h4 {
    margin: 0;
}
.homemadechart__focus__close {
    position: absolute;
    right: 20px;
    top: 10px;
    padding: 0px;
    font-size: 1.2em;
}
.homemadechart .odswidget.odswidget-charts {
    height: 250px;
    padding: 20px 30px 0px;
}

toObject AngularJS filter

A custom AngularJS filter can help to switch from a json array to a json dict by defining the desired key contained in the array items.

The main usage is to pick specific items easily in the json object instead of iterating over each elements.

<ods-dataset-context context="shanghaiworlduniversityranking"
                     shanghaiworlduniversityranking-dataset="shanghai-world-university-ranking"
                     shanghaiworlduniversityranking-domain="public"
                     shanghaiworlduniversityranking-parameters="{'sort':'world_rank'}">
    <div class="row"
         ods-analysis="analysisshanghai"
         ods-analysis-context="shanghaiworlduniversityranking"
         ods-analysis-max="20"
         ods-analysis-x="university_name"
         ods-analysis-serie-bestpubscore="MAX(pub)"
         ods-analysis-serie-bestalumniscore="MAX(alumni)"
         ods-analysis-serie-bestawardscore="MAX(award)"
         ods-analysis-sort="bestawardscore">
        {{ mynewstructure = (analysisshanghai.results | toObject : 'x') ; "" }}
        <div class="col-sm-6">
            <h3>
                ods-analysis
            </h3>
            <pre ng-bind="analysisshanghai|json"></pre>
        </div>
        <div class="col-sm-6">
            <h3>
                mynewstructure = (odsanalysis.results | toObject : 'x')
            </h3>
            <pre ng-bind="analysisshanghai.results | toObject : 'x' |json"></pre>
        </div>
        <div class="col-sm-12">
            <h3>
                Princeton University score <b>{{ mynewstructure['Princeton University'].bestawardscore }}</b>
            </h3>
            <pre>mynewstructure['Princeton University'].bestawardscore</pre>
            <h3>
                Cornell University score <b>{{ mynewstructure['Cornell University'].bestawardscore }}</b>
            </h3>
            <pre>mynewstructure['Cornell University'].bestawardscore</pre>
            <h3>
                Columbia University score <b>{{ mynewstructure['Columbia University'].bestawardscore }}</b>
            </h3>
            <pre>mynewstructure['Columbia University'].bestawardscore</pre>
        </div>
    </div>
</ods-dataset-context>