Apprenez à construire des visualisations Vega riches dans Kibana | Elastic Blog
Technique

Prise en main des visualisations Vega dans Kibana

La syntaxe déclarative Vega est un outil puissant pour visualiser vos données. Nouveauté dans Kibana 6.2 : vous pouvez désormais construire des visualisations riches Vega et Vega-Lite avec vos données Elasticsearch. Il est temps pour nous de découvrir le langage Vega au travers de quelques exemples simples.

Pour commencer, ouvrez léditeur Vega, un outil pratique pour tester le langage Vega "brut" (cest-à-dire sans personnalisations Elasticsearch). Copiez le code ci-dessous. Le texte "Hello Vega!" saffiche dans le panneau de droite.

Text Mark Example

{
  "$schema": "https://vega.github.io/schema/vega/v3.json",
  "width": 100, "height": 30,
  "background": "#eef2e8",
  "padding": 5,
  "marks": [
    {
      "type": "text",
      "encode": {
        "update": {
          "text":     { "value": "Hello Vega!" },
          "align":    { "value": "center"},
          "baseline": { "value": "middle"},
          "stroke":   { "value": "#A32299" },
          "angle":    { "value": 15 },
          "x":        { "signal": "width/2" },
          "y":        { "signal": "height/2" }
        }
      }
    }
  ]
}

Le bloc de repères est une série de primitives de dessin, p. ex. texte, lignes et rectangles. Chaque repère dispose dun grand nombre de paramètres indiqués dans lensemble dencodage. Chaque paramètre est défini soit sur une constante (valeur), soit sur le résultat dun calcul (signal) au stade de "mise à jour". Pour le repère de texte, nous définissons la chaîne de texte, nous veillons à ce que le texte soit correctement positionné par rapport aux coordonnées indiquées, dans le bon sens, et nous en définissons la couleur. Les coordonnées x et y sont calculées en fonction de la largeur et de la hauteur du graphe, en positionnant le texte au milieu. Il existe bien dautres paramètres de repères de texte. Il y a également un graphe de démonstration interactif pour tester différentes valeurs de paramètres pour le repère de texte.

Expliquons quelques éléments : $schema correspond tout simplement à lID de la version du moteur Vega requise ; Background permet davoir un graphe qui nest pas transparent ; width et height définissent la taille du quadrillage du dessin initial. La taille du graphe final peut changer dans certains cas, selon le contenu et les options dautodimensionnement. Remarque : La valeur autosize par défaut dans Kibana est fit, et non pas pad. De ce fait, les éléments height et width sont facultatifs. Le paramètre padding permet dajouter de lespace autour du graphe, en plus de définir la largeur et la hauteur.

Graphe axé sur les données

Prêt pour la prochaine étape ? Nous allons maintenant dessiner un graphe axé sur les données à laide du repère de rectangle. La section des données permet dutiliser plusieurs sources de données, quelles soient codées en dur ou quelles se présentent sous forme dURL. Dans Kibana, vous pouvez aussi utiliser des requêtes Elasticsearch directes. Notre table de données vals comprend quatre lignes et deux colonnes (category et count). La colonne category permet de positionner la barre sur laxe x, tandis que la colonne count définit la hauteur de la barre. Remarque : La valeur 0 pour la coordonnée y se trouve tout en haut. Plus la barre descend, plus la valeur augmente.

Rect Mark Example

{
  "$schema":"https://vega.github.io/schema/vega/v3.json",
  "width": 300, "height": 100,
  "data": [ {
    "name": "vals",
    "values": [
      {"category": 50,  "count": 30},
      {"category": 100, "count": 80},
      {"category": 150, "count": 10},
      {"category": 200, "count": 50}
    ]
  } ],
  "marks": [ {
    "type": "rect",
    "from": { "data": "vals" },
    "encode": {
      "update": {
        "x":     {"field": "category"},
        "width": {"value": 30},
        "y":     {"field": "count"},
        "y2":    {"value": 0}
      }
    }
  } ]
}

Le repère rect indique que la table vals est la source de données. Le repère est dessiné une fois par valeur de donnée source (connu aussi comme ligne de table ou datum). À la différence du graphe précédent, les paramètres x et y ne sont pas codés en dur : ils proviennent des champs du datum.

Montée en charge

La montée en charge est lun des concepts les plus importants de Vega, mais aussi lun des plus délicats. Dans les exemples précédents, les coordonnées des pixels à lécran étaient codées en dur dans les données. Même si cela facilite les choses, il est rare que les données réelles arrivent sous cette forme. À la place, les données sources se présentent sous leurs propres unités (p. ex. nombre dévénements), et cest au graphe deffectuer la montée en charge des valeurs sources en pixels selon la taille de graphe souhaitée.

Dans cet exemple, cest la montée en charge linéaire qui est utilisée. Il sagit essentiellement dune fonction mathématique servant à convertir une valeur du domaine des données sources (ici, les valeurs count de 1 000 à 8000, y compris count=0) selon la plage souhaitée (ici, la hauteur du graphe, de 0 à 99). La fonction de montée en charge yscale permet dajouter "scale": "yscale" aux paramètres y et y2 afin de convertir les valeurs count en coordonnées à lécran (0 devient 99, et 8 000 - la plus grande valeur des données sources - devient 0). Remarque : Le paramètre de plage height est un cas spécial car il fait en sorte que la valeur 0 apparaisse en bas du graphe.

Rect Mark with Scaling Example

{
  "$schema":"https://vega.github.io/schema/vega/v3.json",
  "width": 400, "height": 100,
  "data": [ {
    "name": "vals",
    "values": [
      {"category": 50,  "count": 3000},
      {"category": 100, "count": 8000},
      {"category": 150, "count": 1000},
      {"category": 200, "count": 5000}
    ]
  } ],
 "scales": [
    {
      "name": "yscale",
      "type": "linear",
      "zero": true,
      "domain": {"data": "vals", "field": "count"},
      "range": "height"
    }
  ],
  "marks": [ {
    "type": "rect",
    "from": { "data": "vals" },
    "encode": {
      "update": {
        "x":     {"field": "category"},
        "width": {"value": 30},
        "y":     {"scale": "yscale", "field": "count"},
        "y2":    {"scale": "yscale", "value": 0}
      }
    }
  } ]
}

Montée en charge de bande

Pour notre tutoriel, nous aurons besoin dun autre type de montée en charge Vega 15+ : une montée en charge de bande. Ce type de montée en charge sert lorsque nous disposons dun ensemble de valeurs (p. ex. des catégories) à représenter sous forme de bandes, occupant chacune la même largeur proportionnelle sur la largeur totale du graphe. Dans le cas présent, la montée en charge de bande attribue à chacune des quatre catégories uniques la même largeur proportionnelle (environ 400/4, moins 5 % de couche entre les barres et aux extrémités). {"scale": "xscale", "band": 1} obtient 100 % de la largeur des bandes pour le paramètre width du repère.

Rect Mark with Band Scaling Example

{
  "$schema":"https://vega.github.io/schema/vega/v3.json",
  "width": 400, "height": 100,
  "data": [ {
    "name": "vals",
    "values": [
      {"category": "Oranges", "count": 3000},
      {"category": "Pears",   "count": 8000},
      {"category": "Apples",  "count": 1000},
      {"category": "Peaches", "count": 5000}
    ]
  } ],
 "scales": [
    {
      "name": "yscale",
      "type": "linear",
      "zero": true,
      "domain": {"data": "vals", "field": "count"},
      "range": "height"
    },
    {
      "name": "xscale",
      "type": "band",
      "domain": {"data": "vals", "field": "category"},
      "range": "width",
      "padding": 0.05
    }
  ],
  "marks": [ {
    "type": "rect",
    "from": { "data": "vals" },
    "encode": {
      "update": {
        "x":     {"scale": "xscale", "field": "category"},
        "width": {"scale": "xscale", "band": 1},
        "y":     {"scale": "yscale", "field": "count"},
        "y2":    {"scale": "yscale", "value": 0}
      }
    }
  } ]
}

Axes

Un graphe type ne serait pas complet sans balises daxe. La définition des axes applique les mêmes montées en charge que celles indiquées précédemment. Pour les ajouter, rien de plus simple : il suffit de désigner la montée en charge par son nom et den préciser le côté de positionnement. Ajoutez le code suivant en tant quélément de niveau supérieur dans le dernier exemple de code.

Rect Mark with Axes Example

  "axes": [
    {"scale": "yscale", "orient": "left"},
    {"scale": "xscale", "orient": "bottom"}
  ],

Remarque : La taille du graphique a automatiquement augmenté pour sadapter à ces axes. Si vous souhaitez que le graphe conserve sa taille dorigine, ajoutez "autosize": "fit" dans la spécification.

Transformations des données et conditions

Pour pouvoir utiliser les données dans le dessin, il faut généralement les manipuler auparavant. Pour cela, Vega propose différentes transformations. Commençons par la transformation de formule la plus courante pour ajouter un champ de valeur count aléatoire à chaque datum source de façon dynamique. Dans ce graphe, nous allons également nous servir de la fonction de couleur de remplissage de la barre : les valeurs inférieures à 333 seront indiquées en rouge, celles comprises entre 333 et 666, en jaune, et celles supérieures à 666, en vert. Remarque : Il est possible dutiliser une montée en charge à la place, en mappant le domaine des données sources à lensemble de couleurs ou à un spectre de couleurs.

Random Data Example

{
  "$schema":"https://vega.github.io/schema/vega/v3.json",
  "width": 400, "height": 200,
  "data": [ {
    "name": "vals",
    "values": [
      {"category": "Oranges"},
      {"category": "Pears"},
      {"category": "Apples"},
      {"category": "Peaches"},
      {"category": "Bananas"},
      {"category": "Grapes"}
    ],
    "transform": [
      {"type": "formula", "as": "count", "expr": "random()*1000"}
    ]
  } ],
 "scales": [
    {
      "name": "yscale",
      "type": "linear",
      "zero": true,
      "domain": {"data": "vals", "field": "count"},
      "range": "height"
    },
    {
      "name": "xscale",
      "type": "band",
      "domain": {"data": "vals", "field": "category"},
      "range": "width",
      "padding": 0.05
    }
  ],
  "axes": [
    {"scale": "yscale", "orient": "left"},
    {"scale": "xscale", "orient": "bottom"}
  ],
  "marks": [ {
    "type": "rect",
    "from": { "data": "vals" },
    "encode": {
      "update": {
        "x":     {"scale": "xscale", "field": "category"},
        "width": {"scale": "xscale", "band": 1},
        "y":     {"scale": "yscale", "field": "count"},
        "y2":    {"scale": "yscale", "value": 0},
        "fill":  [
          {"test": "datum.count < 333", "value": "red"},
          {"test": "datum.count < 666", "value": "yellow"},
          {"value": "green"}
        ]
      }
    }
  } ]
}

Données dynamiques avec Elasticsearch et Kibana

Maintenant que vous disposez des connaissances de base, passons aux choses sérieuses. Nous allons créer un graphe linéaire basé sur le temps en nous servant de données Elasticsearch générées de façon aléatoire. La procédure est similaire à celle que nous avons vue initialement lors de la création dun graphe Vega dans Kibana. La seule différence, cest que nous allons nous servir du langage Vega au lieu des paramètres par défaut Kibana de Vega-Lite (la version simplifiée de niveau supérieur de Vega).

Dans cet exemple, nous allons coder les données en dur avec values, au lieu démettre la requête réelle avec url. Ainsi, nous pourrons continuer nos essais dans léditeur Vega qui ne prend pas en charge les requêtes Elasticsearch Kibana. Le graphe deviendra totalement dynamique dans Kibana si vous remplacez values par la section url comme indiqué ci-dessous.

Notre requête recense le nombre de documents par intervalle de temps, en se servant de la plage de temps et des filtres de contexte sélectionnés par lutilisateur du tableau de bord. Consultez comment interroger Elasticsearch à partir de Kibana pour en savoir plus.

   "url": {
      "%context%": true,
      "%timefield%": "@timestamp",
      "index": "_all",
      "body": {
        "aggs": {
          "time_buckets": {
            "date_histogram": {
              "field": "@timestamp",
              "interval": {"%autointerval%": true},
              "extended_bounds": {
                "min": {"%timefilter%": "min"},
                "max": {"%timefilter%": "max"}
              },
              "min_doc_count": 0
            }
          }
        },
        "size": 0
      }

Voici le résultat que nous obtenons lorsque nous lexécutons (nous avons supprimé certains champs non pertinents dans un souci de concision) :

     "aggregations": {
        "time_buckets": {
          "buckets": [
            {"key": 1528061400000, "doc_count": 1},
            {"key": 1528063200000, "doc_count": 45},
            {"key": 1528065000000, "doc_count": 49},
            {"key": 1528066800000, "doc_count": 17},
...

Comme vous pouvez le constater, les données réelles dont nous avons besoin se trouvent dans la série aggregations.time_buckets.buckets. Nous pouvons indiquer à Vega de se concentrer uniquement sur cette série en indiquant "format": {"property": "aggregations.time_buckets.buckets"} dans la définition des données.

Notre axe x nest plus basé sur les catégories, mais sur le temps : le champ key est exprimé sous forme dheure UNIX que Vega peut utiliser directement. Nous changeons donc le type xscale en temps, et nous ajustons tous les champs pour quils utilisent key et doc_count. Nous devons également modifier le type de repère en line, et inclure uniquement les canaux de paramètres x et y. Voilà ! Vous avez désormais un graphe linéaire ! Si vous le souhaitez, vous pouvez personnaliser les balises de laxe x avec les paramètresformat, labelAngle et tickCount.

Elasticsearch search result example

{
  "$schema": "https://vega.github.io/schema/vega/v3.json",
  "width": 400, "height": 200,
  "data": [
    {
      "name": "vals",
      "values": {
        "aggregations": {
          "time_buckets": {
            "buckets": [
              {"key": 1528063200000, "doc_count": 45},
              {"key": 1528065000000, "doc_count": 49},
              {"key": 1528068600000, "doc_count": 32},
              {"key": 1528072200000, "doc_count": 12},
              {"key": 1528074000000, "doc_count": 10},
              {"key": 1528075800000, "doc_count": 10},
              {"key": 1528083000000, "doc_count": 2},
              {"key": 1528088400000, "doc_count": 3},
              {"key": 1528092000000, "doc_count": 9},
              {"key": 1528093800000, "doc_count": 15},
              {"key": 1528095600000, "doc_count": 13},
              {"key": 1528097400000, "doc_count": 19},
              {"key": 1528099200000, "doc_count": 33},
              {"key": 1528101000000, "doc_count": 20},
              {"key": 1528102800000, "doc_count": 55},
              {"key": 1528104600000, "doc_count": 68},
              {"key": 1528108200000, "doc_count": 70},
              {"key": 1528110000000, "doc_count": 108},
              {"key": 1528113600000, "doc_count": 151},
              {"key": 1528117200000, "doc_count": 206},
              {"key": 1528122600000, "doc_count": 258},
              {"key": 1528129800000, "doc_count": 250},
              {"key": 1528135200000, "doc_count": 220}
            ]
          }
        }
      },
      "format": {"property": "aggregations.time_buckets.buckets"}
    }
  ],
  "scales": [
    {
      "name": "yscale",
      "type": "linear",
      "zero": true,
      "domain": {"data": "vals", "field": "doc_count"},
      "range": "height"
    },
    {
      "name": "xscale",
      "type": "time",
      "domain": {"data": "vals", "field": "key"},
      "range": "width"
    }
  ],
  "axes": [
    {"scale": "yscale", "orient": "left"},
    {"scale": "xscale", "orient": "bottom"}
  ],
  "marks": [
    {
      "type": "line",
      "from": {"data": "vals"},
      "encode": {
        "update": {
          "x": {"scale": "xscale", "field": "key"},
          "y": {"scale": "yscale", "field": "doc_count"}
        }
      }
    }
  ]
}

Gardez un œil sur notre blog pour lire dautres articles sur Vega. Parmi les thèmes abordés prochainement : comment gérer les résultats Elasticsearch, notamment les agrégations et les données imbriquées.

Liens utiles

Précédents articles sur Vega