Métricas/KPI y tablas de datos

Las vistas de gráficos, mapas, tablas y calendario son vistas predeterminadas. Tiene a su disposición múltiples parámetros adicionales en la documentación. Entre tanto, hay otros muchos widgets tan interesantes como los principales.

Consulte la lista de widgets para obtener más información.

Seleccionar un juego de datos

Para ilustrar las métricas, los KPI, las ayudas de herramientas de mapas avanzadas y mucho más, utilizaremos un juego de datos con todos los informes de regularidad de la compañía ferroviaria francesa desde 2011. Este juego de datos contiene, para cada una de las líneas del tren de alta velocidad TGV (100 líneas), el número de trenes programados al mes, el número de trenes que no han salido de la estación y el número de trenes con retraso.

ID de conjunto de datos: regularite-mensuelle-tgv Puede buscarlo en data.opendatasoft.com. A continuación, añada este conjunto de datos al dominio: Conjunto de datos nuevo, Añadir un conjunto de datos de la red Opendatasoft, seleccione el conjunto de datos, guárdelo y publíquelo.

Crear indicadores KPI

Para crear métricas/KPI, utilizamos odsAggregation a fin de calcular los valores de suma, promedio, mínimo y máximo de un campo de datos. En función del contexto, la métrica se actualizará dinámicamente durante la navegación del usuario (consultas y filtros).

A partir de una página nueva, empezamos obteniendo un contexto de este juego de datos. El procedimiento más sencillo es ir a la página de Explorar/catálogo, ir al juego de datos y, en la vista de tabla, copiar y pegar el código del widget de los vínculos de compartir siguientes:

<ods-dataset-context
        context="regularitemensuelletgv"
        regularitemensuelletgv-dataset="regularite-mensuelle-tgv">

        <ods-table context="regularitemensuelletgv"></ods-table>

</ods-dataset-context>

En la parte superior de la vista de tabla, añadiremos un contador basado en el campo nombre_de_trains_programmes (número de trenes programados). Vamos a obtener el número de trenes programados desde 2011 en Francia.

Con odsAggregation, los parámetros son:
  • El nombre de la variable que almacenará el resultado de la agregación

  • El contexto para trabajar

  • La función

  • La expresión (el nombre del campo)

Con un filtro number para que se visualice correctamente en un span HTML debería tener algo parecido a esto:

<div class="container">

    <ods-dataset-context
                         context="regularitemensuelletgv"
                         regularitemensuelletgv-dataset="regularite-mensuelle-tgv">

        <div ods-aggregation="myvar"
             ods-aggregation-context="regularitemensuelletgv"
             ods-aggregation-function="SUM"
             ods-aggregation-expression="nombre_de_trains_programmes">

                <span>Scheduled train : {{ myvar | number }}</span>

        </div>

        <br/>

        <ods-table context="regularitemensuelletgv"></ods-table>

    </ods-dataset-context>

</div>

Visualiza un número (aprox. 1,7 millones) al principio de la página que ya necesita código CSS para resaltarlo. Las métricas suelen ir delimitadas en un cuadro, con un tamaño de fuente y un peso importantes.

Añada este código CSS a la página:

.kpi {
    padding: 2px 30px; /* Give some air to the digits */
    border: 1.5px solid #010201; /* the border */
    border-radius: 5px; /* rounded corners */
    font-size: 2em; /* bigger font */
    font-weight: 500; /* thicker font */
    margin: 10px 5px; /* give some air around the KPI */
}

Y añada la clase kpi a la etiqueta span.

Guarde los cambios, actualice y observe cómo ha mejorado.

Antes de proseguir con CSS y especialmente el código con capacidad de respuesta, vamos a añadir 2 métricas más: número de trenes cancelados (nombre_de_trains_annules) y número de trenes con retraso (nombre_de_trains_en_retard_a_l_arrivee).

<div ods-aggregation="myvar"
     ods-aggregation-context="regularitemensuelletgv"
     ods-aggregation-function="SUM"
     ods-aggregation-expression="nombre_de_trains_programmes">
        <span class="kpi">Scheduled train : {{ myvar | number }}</span>
</div>

<br/>

<div ods-aggregation="myvar"
     ods-aggregation-context="regularitemensuelletgv"
     ods-aggregation-function="SUM"
     ods-aggregation-expression="nombre_de_trains_annules">
        <span class="kpi">Canceled train : {{ myvar | number }}</span>
</div>

<br/>

<div ods-aggregation="myvar"
     ods-aggregation-context="regularitemensuelletgv"
     ods-aggregation-function="SUM"
     ods-aggregation-expression="nombre_de_trains_en_retard_a_l_arrivee">
        <span class="kpi">Delayed train : {{ myvar | number }}</span>
</div>

<br/>

<ods-table context="regularitemensuelletgv"></ods-table>

Resultado:

../../../_images/advanced__kpi-1.png

Funciona, pero todas las métricas son independientes. Podemos añadir filtros para que sean dinámicas en función de los filtros de usuario, etc. pero no podemos calcular nada a partir de ahí. Sería interesante tener el porcentaje de trenes cancelados o con retraso respecto del total.

Para ello, debe encadenar ods-aggregation con varios nombres de variables.

Debe tener el aspecto siguiente:

    <div ods-aggregation="total"
     ods-aggregation-context="regularitemensuelletgv"
     ods-aggregation-function="SUM"
     ods-aggregation-expression="nombre_de_trains_programmes">
    <div ods-aggregation="canceled"
         ods-aggregation-context="regularitemensuelletgv"
         ods-aggregation-function="SUM"
         ods-aggregation-expression="nombre_de_trains_annules">
        <div ods-aggregation="delayed"
             ods-aggregation-context="regularitemensuelletgv"
             ods-aggregation-function="SUM"
             ods-aggregation-expression="nombre_de_trains_en_retard_a_l_arrivee">

                <span class="kpi">Scheduled : {{ total | number }}</span>
                <span class="kpi">Canceled : {{ canceled | number }}</span>
                <span class="kpi">Delayed : {{ delayed | number }}</span>
        </div>
    </div>
</div>

A continuación, ya es posible, con la expresión AngularJS, calcular los porcentajes entre los valores:

<span class="kpi">Scheduled : {{ total | number }}</span>
<span class="kpi">Canceled : {{ canceled | number }}</span>
<span class="kpi">Delayed : {{ delayed | number }}</span>
<span class="kpi">% Canceled : {{ canceled / total * 100 | number : 2 }}%</span>
<span class="kpi">% Delayed : {{ delayed / total * 100 | number : 2 }}%</span>

Nota

| number : 2 es el filtro AngularJS para imprimir en un formato estético los valores numéricos. El parámetro opcional 2 limita los decimales a solo 2.

Antes de continuar vamos a limpiar el código HTML para poder aplicar CSS para una visualización con capacidad de respuesta, un tamaño de fuente distinto para el titulo y el valor, etc.:

<div class="row">
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">
            <div class="kpi-title">
                Scheduled
            </div>
            <div class="kpi-value">
                {{ total | number }}
            </div>
        </div>
    </div>
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">
            <div class="kpi-title">
                Canceled
            </div>
            <div class="kpi-value">
                {{ canceled | number }}
            </div>
        </div>
    </div>
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">
            <div class="kpi-title">
                Delayed
            </div>
            <div class="kpi-value">
                {{ delayed | number }}
            </div>
        </div>
    </div>
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">
            <div class="kpi-title">
                % Canceled
            </div>
            <div class="kpi-value">
                {{ canceled / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
            </div>
        </div>
    </div>
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">
            <div class="kpi-title">
                % Delayed

            </div>
            <div class="kpi-value">
                {{ delayed / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
            </div>
        </div>
    </div>
    <div class="col-md-2 col-sm-3 col-xs-4">
        <div class="kpi">

            <div class="kpi-title">
                On time
            </div>
            <div class="kpi-value">
                {{ total - delayed - canceled | number }}
            </div>
        </div>
    </div>
</div>

Sustituya el código CSS:

.kpis {
    display: inline-flex;
}

.kpi {
    text-align: center;

    padding: 5px 0px;
    margin-bottom: 10px;
    height: 70px;

    border: 1.5px solid #010201; /* the border */
    border-radius: 5px; /* rounded corners */
}

.kpi-title {
    font-size: 1em; /* bigger font */
    font-weight: 400; /* thicker font */
}

.kpi-value {
    font-size: 2em; /* bigger font */
    font-weight: 500; /* thicker font */
}

.kpi-value-unit {
    font-size: 1.5rem; /* bigger font */
    font-weight: 400; /* thicker font */
}

Guarde los cambios, actualice y observe:

../../../_images/advanced__kpi-2.png

Colorear el KPI - ng-class

La directiva ng-class es muy práctica para aplicar un estilo a un elemento HTML en función de un valor, el contexto, una expresión o cualquier otro contenido dinámico que se pueda emplear en una expresión AngularJS.

Por ejemplo, para definir un umbral en las métricas (verde si el porcentaje de trenes cancelados es inferior a 0,20% y rojo si es mayor), añada esto al elemento:

<div class="col-md-2 col-sm-3 col-xs-4">
    <div class="kpi" ng-class="{'good' : canceled / total * 100 < 0.2, 'bad' : canceled / total * 100 >= 0.2}">
        <div class="kpi-title">
            % Canceled
        </div>
        <div class="kpi-value">
            {{ canceled / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
        </div>
    </div>
</div>

Y este código CSS:

.good {
    color: #55cd61;
    border-color: #55cd61;
}

.medium {
    color: #ff9c22;
    border-color: #ff9c22;
}

.bad {
    color: #e50000;
    border-color: #e50000;
}

Guarde los cambios, actualice y compruebe que las métricas ahora aparecen en rojo (se utiliza la clase CSS bad).

Para ver claramente el comportamiento dinámico, vamos a añadir algunos filtros a la izquierda de la tabla (en la segunda línea). Para ello:
  • Añada div con la clase row para delimitar la tabla.

  • Añada div con col-md-9 alrededor de la tabla.

  • Añada un widget ods-facets con 2 ods-facet para los filtros date y depart.

  • Delimite ods-facets con div con el estilo col-md-3.

  • De modo opcional, añada varios ods-box con el estilo div para delimitar los bloques con un fondo blanco.

Debería tener el aspecto siguiente:

<div class="row">
    <div class="col-md-3">
        <div class="ods-box">
            <ods-facets context="regularitemensuelletgv">
                <h2>
                    Date
                </h2>
                <ods-facet name="date"></ods-facet>
                <h2>
                    Origin station
                </h2>
                <ods-facet name="depart"></ods-facet>
            </ods-facets>
        </div>
    </div>
    <div class="col-md-9">
        <div class="ods-box">
            <ods-table context="regularitemensuelletgv"></ods-table>
        </div>
    </div>
</div>

Guarde los cambios, actualice y observe el comportamiento con los filtros seleccionados. En 2013, la estación de París Este tuvo menos del 0,20% de trenes cancelados. Compruébelo.

../../../_images/advanced__kpi-ngclass-1.png ../../../_images/advanced__kpi-ngclass-2.png

Nota

La sintaxis de ng-class, delimitada por corchetes, consiste en la clase CSS entrecomillada seguida de : y la expresión AngularJS:

{ 'clase CSS' : expresión AngularJS, ... }

Se pueden especificar varias clases y expresiones, y todas las expresiones se probarán de izquierda a derecha.

Comparar con filtros: resaltar las diferencias con colores

Es interesante comparar el juego de datos completo y una vista filtrada por el usuario, y ver si las métricas (especialmente los porcentajes) son diferentes, mayores o menores.

El plan es tener 2 contextos, uno que representa el juego de datos completo y otro que el usuario puede filtrar. Una vez aplicado un filtro, si la métrica es mayor o menor que la métrica del juego de datos completo, se resaltará con un color.

Para ello, necesitará:
  • Un contexto secundario

  • Complementar ods-filters y ods-table en este contexto secundario

  • Calcular las mismas métricas para ambos contextos

  • Añadir la métrica secundaria SI y SOLO SI se selecciona un filtro

  • Configurar ng-class para comparar la métrica completa con la filtrada

Procedamos.

2 contextos y 1 juego de datos:

<ods-dataset-context
                     context="regularitemensuelletgv,regularitemensuelletgvfiltered"
                     regularitemensuelletgv-dataset="regularite-mensuelle-tgv"
                     regularitemensuelletgvfiltered-dataset="regularite-mensuelle-tgv">

Los bloques de métricas completos ahora deberían tener el aspecto siguiente:

<div ods-aggregation="total"
     ods-aggregation-context="regularitemensuelletgv"
     ods-aggregation-function="SUM"
     ods-aggregation-expression="nombre_de_trains_programmes">
    <div ods-aggregation="canceled"
         ods-aggregation-context="regularitemensuelletgv"
         ods-aggregation-function="SUM"
         ods-aggregation-expression="nombre_de_trains_annules">
        <div ods-aggregation="delayed"
             ods-aggregation-context="regularitemensuelletgv"
             ods-aggregation-function="SUM"
             ods-aggregation-expression="nombre_de_trains_en_retard_a_l_arrivee">

            <div ods-aggregation="totalfiltered"
                 ods-aggregation-context="regularitemensuelletgvfiltered"
                 ods-aggregation-function="SUM"
                 ods-aggregation-expression="nombre_de_trains_programmes">
                <div ods-aggregation="canceledfiltered"
                     ods-aggregation-context="regularitemensuelletgvfiltered"
                     ods-aggregation-function="SUM"
                     ods-aggregation-expression="nombre_de_trains_annules">
                    <div ods-aggregation="delayedfiltered"
                         ods-aggregation-context="regularitemensuelletgvfiltered"
                         ods-aggregation-function="SUM"
                         ods-aggregation-expression="nombre_de_trains_en_retard_a_l_arrivee">

                        <div class="row">
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        Scheduled
                                    </div>
                                    <div class="kpi-value">
                                        {{ totalfiltered | number }}
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ total | number }})
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        Canceled
                                    </div>
                                    <div class="kpi-value">
                                        {{ canceledfiltered | number }}
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ canceled | number }})
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        Delayed
                                    </div>
                                    <div class="kpi-value">
                                        {{ delayedfiltered | number }}
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ delayed | number }})
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        % Canceled
                                    </div>
                                    <div class="kpi-value">
                                        {{ canceledfiltered / totalfiltered * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ canceled / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>)
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        % Delayed

                                    </div>
                                    <div class="kpi-value">
                                        {{ delayedfiltered / totalfiltered * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ delayed / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>)
                                    </div>
                                </div>
                            </div>
                            <div class="col-md-2 col-sm-3 col-xs-4">
                                <div class="kpi">
                                    <div class="kpi-title">
                                        On time
                                    </div>
                                    <div class="kpi-value">
                                        {{ totalfiltered - delayedfiltered - canceledfiltered | number }}
                                    </div>
                                    <div class="kpi-value-reference">
                                        ({{ total - delayed - canceled | number }})
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Adapte el código CSS actualizando o añadiendo estas clases:

.kpi {
    height: 90px;
    /* ... */
}

.kpi-value-reference {
    font-size: 1em; /* bigger font */
    font-weight: 400; /* thicker font */
}

.kpi-value-unit {
    font-size: 0.7em; /* bigger font */
    /* ... */
}

Por último, colorearemos las métricas de porcentajes de retrasos y cancelaciones. Si el resultado es mayor que el promedio total, lo mostraremos en rojo; y si es menor, en verde.

<div class="col-md-2 col-sm-3 col-xs-4">
    <div class="kpi" ng-class="{
                               'good': (canceledfiltered / totalfiltered * 100) < (canceled / total * 100),
                               'bad': (canceledfiltered / totalfiltered * 100) > (canceled / total * 100),
                               }">
        <div class="kpi-title">
            % Canceled
        </div>
        <div class="kpi-value">
            {{ canceledfiltered / totalfiltered * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
        </div>
        <div class="kpi-value-reference">
            ({{ canceled / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>)
        </div>
    </div>
</div>
<div class="col-md-2 col-sm-3 col-xs-4">
    <div class="kpi" ng-class="{
                               'good': (delayedfiltered / totalfiltered * 100) < (delayed / total * 100),
                               'bad': (delayedfiltered / totalfiltered * 100) > (delayed / total * 100),
                               }">
        <div class="kpi-title">
            % Delayed

        </div>
        <div class="kpi-value">
            {{ delayedfiltered / totalfiltered * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>
        </div>
        <div class="kpi-value-reference">
            ({{ delayed / total * 100 | number : 2 }}<span class="kpi-value-unit"> %</span>)
        </div>
    </div>
</div>

Guarde los cambios, actualice y pruebe con el año 2015:

../../../_images/advanced__kpi-ngclass-3.png

Crear una tabla de datos

odsAnalysis obtiene el análisis de una o varias agregaciones para cada uno de los valores de un campo. Puede entenderse exactamente como un gráfico, pero en lugar de mostrar curvas, columnas o segmentos, odsAnalysis devuelve cada uno de los valores, y usted decide si los visualiza en una tabla u otra expresión.

En primer lugar, probaremos el resultado configurando el análisis correcto en la ficha de análisis del juego de datos. Podría ser interesante visualizar la tabla de las 10 estaciones con mejor regularidad. Para ello:

  • Eje X : "départ" (estación de origen)
    • 4 series: - AVG (régularité) Regularidad - AVG (nombre_de_trains_programmes) Número de trenes programados - AVG (nombre_de_trains_annules) Número de trenes cancelados - AVG (nombre_de_trains_en_retard_a_l_arrivee) Número de trenes con retraso

  • Ordenar por serie de regularidad

  • Número de puntos: 10 máx.

../../../_images/advanced__data-table-1.png

El objetivo es reproducir este análisis exacto con odsAnalysis. A modo de recordatorio, consulte la documentación aquí.

Parámetros de odsAnalysis:
  • ods-analysis: el nombre de la variable

  • ods-analysis-context: el contexto para trabajar

  • ods-analysis-max: 10 elementos máx.

  • ods-analysis-x: el campo para trabajar (se calculará toda la agregación para cada uno de los valores de este campo)

  • ods-analysis-serie-xxx: definir una serie denominada xxx a partir de una expresión (el campo) y una función (AVG, MAX, MIN, SUM, etc.)

  • ods-analysis-sort: ordenar en una serie especificando el nombre (o -nombre para invertir el orden)

Antes de pasar al código (HTML), examine el resultado almacenado en la variable:

<div class="row">
    <div ods-analysis="results"
         ods-analysis-context="regularitemensuelletgvfiltered"
         ods-analysis-max="10"
         ods-analysis-x="depart"
         ods-analysis-serie-regularity="AVG(regularite)"
         ods-analysis-serie-scheduled="AVG(nombre_de_trains_programmes)"
         ods-analysis-serie-canceled="AVG(nombre_de_trains_annules)"
         ods-analysis-serie-delayed="AVG(nombre_de_trains_en_retard_a_l_arrivee)"
         ods-analysis-sort="regularity"
         >
        {{ results }}
    </div>
</div>

Una impresión del bloque json en formato bonito:

{
   "results":[
      {
         "scheduled":97.37288135593221,
         "canceled":0.2033898305084746,
         "regularity":94.60508474576272,
         "delayed":5.288135593220339,
         "x":"ST MALO"
      },
      {
         "scheduled":283.728813559322,
         "canceled":1.2203389830508475,
         "regularity":94.28813559322033,
         "delayed":16.135593220338983,
         "x":"NANCY"
      },
      {
         "scheduled":160.64406779661016,
         "canceled":0.559322033898305,
         "regularity":92.61186440677963,
         "delayed":11.677966101694915,
         "x":"QUIMPER"
      }
   ]
}

Tenemos una lista de resultados json. Cada uno de los bloques tiene un valor x, la estación de origen, y 4 agregaciones/valores que corresponden a las series. La directiva AngularJS ng-repeat permite iterar por la lista results. Para cada uno de los elementos, imprimimos una línea nueva en la tabla.

<div ods-analysis="tgvanalysis"
     ods-analysis-context="regularitemensuelletgvfiltered"
     ods-analysis-max="10"
     ods-analysis-x="depart"
     ods-analysis-serie-regularity="AVG(regularite)"
     ods-analysis-serie-scheduled="AVG(nombre_de_trains_programmes)"
     ods-analysis-serie-canceled="AVG(nombre_de_trains_annules)"
     ods-analysis-serie-delayed="AVG(nombre_de_trains_en_retard_a_l_arrivee)"
     ods-analysis-sort="regularity"
     >
    <table id="top10">
        <thead>
            <tr>
                <td>Position</td>
                <td>Train station</td>
                <td>Regularity</td>
                <td>Scheduled</td>
                <td>Canceled</td>
                <td>Delayed</td>
            </tr>
        </thead>
        <tbody>
            <tr ng-repeat="(i, result) in tgvanalysis.results">
                <td>
                    {{ i + 1 }}
                </td>
                <td>
                    {{ result.x }}
                </td>
                <td>
                    {{ result.regularity | number : 2 }}
                </td>
                <td>
                    {{ result.scheduled | number : 2 }}
                </td>
                <td>
                    {{ result.canceled | number : 2 }}
                </td>
                <td>
                    {{ result.delayed | number : 2 }}
                </td>
            </tr>
        </tbody>
    </table>
</div>

Algo de código CSS para una buena presentación de la tabla HTML:

#top10 {
    margin: 20px auto;
}

#top10 thead > tr {
    background-color: #007396;
    color: white;
}

#top10 td {
    min-width: 100px;
    padding: 3px 10px;
    text-align: center;
}

#top10 tr:nth-child(even) {
    background-color: #ededed;
}

#top10 tr:hover{
    background-color:#ccc;
}

Guarde los cambios, actualice y juegue con los filtros:

../../../_images/advanced__data-table-2.png