Lorsque j’ai voulu me lancer dans la data visualisation intégrée à un site web, j’ai directement voulu utiliser la librairie la plus célèbre en la matière, à savoir D3.js. Mais j’ai trouvé une alternative qui m’a interpellé car basé à la fois sur D3.js ainsi que Stack.gl, permettant de réaliser des graphiques plus interactifs, à première vue.
Celle-ci est disponible à la fois en Python, R, et enfin Javascript, celle qui nous intéresse dans notre article. Et c’est pour cette raison que je l’écris, car celui-ci n’est pas vraiment disponible en Typescript et bien intégré à l’écosystème d’Angular, qui a des notions de modules, de composants, etc.
On utilisera des données du Covid19 pour alimenter en data nos graphiques. C’est parti !
Quelques infos sur Plotly.js
Pour créer un graphique, on va avoir besoin de trois choses principalement qui se découpe de la façon suivante :
Data
Un objet data qui va contenir l’ensemble des points (ordonnées Y, et abscisse X) que l’on souhaite afficher sur notre graphique.
On le défini dans notre composant en Typescript.
Layout
Un objet layout qui définit les caractéristiques générales au niveau de l’UI de notre graphique, comme le titre, la taille de notre graphique, etc. Si l’on souhaite modifier l’allure d’une courbe en particulier, cela se fera dans l’objet data cependant.
On le défini dans notre composant en Typescript.
Config
C’est l’objet final crée, qui englobe notre objet Data ainsi que notre objet Layout.
On le défini dans notre composant en Typescript, et on ira le binder avec notre fichier de vue en HTML.
Intégration de Plotly.js
Je commence par initialiser un nouveau projet Angular pour illustrer notre exemple. Je vous renvoie sur un précédent article, expliquant comment initialiser une application Angular.
On installe via npm les modules nécessaires :
npm install angular-plotly.js plotly.js
On ajoute le module Plotly à notre module globall App :
Dans votre fichier de configuration tsconfig.json, passez ‘target’ en ‘es5’, si vous avez une erreur dans votre console comme quoi Plotly n’est pas défini dans votre document.
Création d’un graphique avec une courbe
Partie vue
On ajout un composant ‘plotly-plot’. Celui-ci est composé de plusieurs directives et attributs :
fxFlex & Style : directive FlexLayout de Angular, permettant à notre composant de prendre toute la hauteur et largeur du parent disponible
useResizeHandler : directive permettant de resize automatiquement le graphique selon la taille de la fenêtre
data: object contenant l’ensemble des données du graphique
config: object contenant la configuration général de notre graphique
layout: object contenant la configuration graphique de notre graphique
Partie composant
On déclare nos attributs généraux :
J’initialise mes précédents attributs dans le constructeur du composant :
Par la suite, je vais charger mon fichier de données. J’utilise un fichier CSV qui va être lu en local via le httpClient :
Enfin, ma fonction permettant de parser mon fichier CSV de string :
Le plus important sont les lignes suivantes :
4-5 : création de tableau temporaire, contenant nos données
14-15: on remplit nos tableau déclarés précédemment des données du fichier CSV en cours de parsage
23-24: ajout de nos nouvelles données
Voici le résultat de ce que l’on obtient selon le type de graphique que l’on choisit dans notre objet de data :
Vous avez une multitude de type de graphique selon ce que vous voulez donner comme aspect à vos données, je vous laisse lire la doc pour en savoir plus.
Création d’un graphique avec une multitude de courbe
On va reprendre l’exemple précédent, et y ajouter une nouvelle courbe concernant les cas soignés. Je vais enlever quelques données en début de pandémie, étant donné que l’on a eu des cas à partir du 1er Mars à peu près. Cela permettra une meilleure visibilité pour mon tutoriel.
On commence par ajouter une nouvelle courbe en ajoutant une donnée dans notre tableau de données. On fait cela comme précédemment, dans le constructeur du composant :
Vous pouvez apercevoir quelques changements comparé à la première partie de ce tutoriel :
marker : permet d’affecter une couleur à notre courbe
name : nom de la courbe dans la légende
legendgroup : permet de grouper plusieurs courbes dans un même groupe, et de pouvoir les cacher en cliquant dessus dans la légende pour toute les faire disparaître
La dernière étape va être de modifier notre fonction de parsing de notre fichier CSV qui contient nos données, afin de récupérer des informations pour une seconde courbes, qui sera elle concernant les cas soignés :
Lignes 4 : L’axe X des abscisses ne change pas, puisque on veut garder nos dates.
Lignes 6 et 18 : On va créer un nouveau tableau contenant des nombres, et le remplir de la même façon que précédemment, mais avec un indice différent et donc une donnée différente.
Lignes 29-30 : correspond à notre second objet de données créer précédemment. On fait attention de lui affecter en abscisses nos données DATE, et en ordonnés notre tableau contenant le nombre de cas soignés.
Voici le résultat de ce que l’on obtient selon le type de graphique que l’on choisit dans notre objet de data :
Quelques exemples de customisation de l’UI
Je vous présente quelques attributs plutôt chouettes pour changer rapidement le sous type de nos graphiques que je vous ai présenté précédemment, à savoir Scatter et Bar
Vous avez moyen de vraiment poussé beaucoup de chose dans l’UI du graphique, regardez la documentation si vous voulez des envies bien précises.
Sous-type de BAR
L’attribut ‘barmod’ se définit dans l’objet LAYOUT de notre graphique ( attribut ‘layout’ dans nos exemples précédents )
Espacement de BAR
Vous pouvez gérer l’espacement entre les bars pour optimiser la lisibilité de votre graphique. Vous avez deux arguments pour cela :
bargap : espacement entre les bars d’un même groupe
bargroupgap : espacement entre les bars de groupes différents
Ces deux arguments se définissent dans l’objet LAYOUT.
Sous-type de SCATTER
L’attribut ‘mode’ se définit dans l’objet DATA de notre graphique ( attribut ‘allData’ dans nos exemples précédents )
Conclusion
Vous avez donc accès pleinement à la librairie Plotly.js dans votre application Angular.
Rien de bien complexe sur son intégration donc, juste un zeste déroutant d’utiliser du Javascript dans du Typescript, on mélange du typage fort avec des objets que l’on remplit d’attributs à la volé.
Vous pouvez ajouter des events de clic, de listener, pour rendre tout cela un peu plus dynamique comme par exemple divers chargements de données pour combiner plusieurs sources, modifier en temps réel l’allure et l’UI des graphiques, etc.
Je vous propose aujourd’hui de réaliser une carte choroplèthe. C’est une carte de chaleur mettant en évidences certaines zones de différents gradients de couleurs pour montrer une intensité plus ou moins forte sur un type de donnée.
C’est d’actualité, je vous propose une carte de la France, découpé en Région, mettant en évidence l’évolution du COVID-19 sur une date donnée.
Leaflet, Openlayers pour ne citer que les plus grands, sont des librairies javascript permettant d’afficher des cartes, et d’y ajouter une multitude d’actions. Vous pouvez ajouter des dessins, des actions, des couleurs, zones, marqueurs, etc. Le but principal est de les rendre interactives pour mettre en évidences toute sorte de chose.
OpenLayers : considéré comme la référence actuellement, c’est un vrai framework à part entière. Permet donc de réaliser des choses très poussées.
Leaflet : certainement le plus populaire. Certaines fonctionnalités ne pourront pas être aussi poussé que Openlayers, car plus léger. Il marque cependant des points quant à sa prise en main, qui s’en fera plus rapidement.
Intégration de Leaflet
Intégration & affichage du fond de carte
Installation de la librairie
On installe Leaflet et son module NPM facilitant son utilisation via :
On installe les définitions de types pour se faciliter la vie pour coder : npm install --save-dev @types/leaflet
Import de la librairie
On intègre le module Leaflet dans la partie ‘Imports’ de notre fichier de définition de notre module principal :
Ajout du fond de carte
On commence par la mise en place de notre carte dans la vue. Pour cela on créer une division avec un composant leaflet :
Pour la partie back, on va définir un objet contenant les caractéristiques de notre carte qui sera bindé avec la vue :
Rien de bien complexe. On ajoute un layer à nos options qui est le fond de notre carte, en le faisant pointer au service de cartographie de Openstreetmap. On lui définit un niveau de zoom maximum utilisable par l’utilisateur, ainsi qu’un bandeau de droit d’auteur qui s’affichera en bas à droite de la carte.
On ajoute en plus des options d’initialisation que l’on retrouvera par défaut lorsque on arrive sur la page de la carte, à savoir le niveau de zoom actuelle de la carte ainsi que le point (latitude, longitude) à afficher au centre de notre écran.
Style de la carte
Si vous avez suivi les instructions, vous devriez vous retrouver avec une carte bien cassé, et c’est normal 😂
On va y remédier en ajoutant un fichier CSS de style, permettant un affichage correct de notre carte. Cet ajout de ce fichier de style se réalise dans notre fichier de configuration de notre application, à savoir angular.json :
Ajout de données GeoJSON
Type des données
Le GeoJSON est un format de donnée géospatial, suivant le format JSON. Pour faire simple, cela consiste à réunir une multitude de points GPS (latitude et longitude) afin de créer des marqueurs sur la carte. Selon le type des données, vous pouvez ainsi dessiner des traits, rectangle et toute sorte de polygone sur la carte via ces points. On va alors exploiter ces possibilités afin de découper notre France en région.
Il existe déjà une multitude de dataset GeoJSON avec toute sorte de découpage, que ça soit en fonction d’état, de départements, etc. Plus vous aurez de points au sein de votre fichier, et plus vos tracés seront précis. Cependant votre fichier sera alors plus lourd, alourdissant notre page et donc les temps de chargement.
Voici la structure de mon fichier des Régions de France :
Nous avons 18 objets, représentant nos 18 régions.
Chaque région comporte les éléments suivants :
properties : contient des données, comme le nom de la région. C’est ici que nous ajouterons le nombre de cas actifs de patient du Covid19.
geometry : contient les couples Latitude/Longitude de points permettant les tracés de chaque région.
Je vais ajouter mes données à la main dans mon objet properties, en ajoutant un nouvel attribut :
« confirmed »: « 10 »
Je le fais à la main car peu de donnée, et surtout choisit aléatoirement. Le but n’est pas de montrer les vraies stats mais de vous montrer comment afficher ces données. Je vous laisse le soin d’ajouter des vraies données avec des scripts Python pour manipuler ces objets ci 😎
Affichage des données
On va ajouter une nouvelle ‘couche’ contenant nos données des régions sur notre carte. Pour cela on ajout dans notre vue dans notre composant leaflet, l’attribut leafletLayers que l’on va bind avec notre contrôleur :
On initialise ce nouvel attribut dans notre composant :
On ajoute ensuite nos données de nos régions :
J’initialise nos données dans un hook Angular, ngOnInit, pour être sûr que la carte Leaflet soit bien déjà initialisée. J’utilise ensuite le module HttpClient pour lire note fichier de donnée en local, disposé dans mon dossier des Assets. Je vais ensuite les ajouter dans mon attribut layers, via la méthode geoJSON de Leaflet qui permet de lire des données GeoJSON. J’initialise mes régions avec une couleur bleu en fond, une certaine opacité et épaisseur de bordure, qui sert à délimiter les régions entre elle.
Changer l’UI d’une région à son survol
On va améliorer l’interface de notre carte, en mettant en évidence la région survolée.
Je vais définir deux objets définissant les états graphiques que peuvent prendre nos régions. Soit elle est normale, soit elle est en cours de survolage par la souris de l’utilisateur. On fait deux style différents afin de remonter l’information à l’utilisateur pour lui montrer sur quoi il pointe :
On va aller modifier la fonction qui ajoute notre layers de données de nos régions afin de lui affecter un style définit précédemment :
On en profite pour leur ajouter des listeners. Vous pouvez voir que sur mon layer des régions, j’ajoute deux listener :
mouseover : quand l’utilisateur passe la souris sur une région
mouseout : quand l’utilisateur enlève la souris d’une région
click : quand un utilisateur clique sur une région, mais je ne l’utiliserais pas pour ce tutoriel ci
On affecte à nos deux listener deux fonctions qui seront appelé à chaque fois qu’un event sera exécuté.
L’event pour mettre en surbrillance une région :
L’event pour rétablir les styles par défaut :
Notez la syntaxe qui diffère entre les deux, mais réalise la même action. a vous de choisir celle que vous préférez.
Coloriser la région en fonction des data
On va pouvoir passer au cœur du projet, à savoir créer nos gradients de couleurs sur nos différentes régions. On va créer une nouvelle méthode qui va être appeler lors de la lecture de notre fichier de donnée GeoJSON, juste après que l’on ait mis nos listener sur l’ensemble de nos régions :
On reviendra un peu plus tard sur l’action qu’effectue l’appel à la méthode updateLegendValues().
On ajoute deux nouveaux attributs à notre classe :
Le premier correspond à un tableau rempli de nombre. Il va nous définir plus tard les intervalles de valeurs, permettant des comparaisons afin de décider si telle région appartient à tel ou tel intervalle selon sa valeur de cas confirmés. Quant au second, il va contenir des string de code hexadécimal de couleur, il en aura autant que d’intervalle défini dans le tableau précédent.
On va les initialiser dans notre constructeur de notre classe :
J’ai crée le gradient de couleur à la main, vous avez des sites sur le net pour vous aider à les faire selon vos couleurs. Je suis parti dans mon exemple autour d’un gradient de rouge.
Pour la suite, on va simplement re-parser notre layer contenant l’ensemble de nos régions, et changer leur style. En parcourant nos régions, on va récupérer notre attribut confirmed représentant le nombre de cas confirmé au Covid19. On souhaite en fonction de leur valeur affecter une couleur différente. On va donc pour l’attribut fillColor, lui passer une fonction qui prendre en entrée l’attribut ‘confirmed‘ :
Cette fonction renvoi en fonction de son entrée, un code hexadécimal de couleur. Je pense que la fonction peut être optimisé. En effet je fais à la main les comparaisons entre 6 intervalles de valeurs, correspondant chacune d’entre elle à 6 couleurs d’intensités différentes.
Il nous manque juste une seule chose, vous vous souvenez de ma fonction updateLegendValues() ? Que j’ai parlé un poil plus haut, et qui est appelé au début de ma fonction updateStyleMap(). Celle-ci va nous permettre de remplir notre tableau des intervalles, que l’on utilise dans la fonction getColor() pour assigner une couleur du tableau selectedLegendColorGradient en comparant aux intervalles de selectedLegendInfos.
On va encore une fois parser notre layer des régions, pour y récupérer la valeur max de l’attribut confirmed. Celle fonction aussi peut être grandement optimisé mais j’ai opté pour la simplicité pour ce tutoriel. Une fois la valeur max récupéré, je vais créer autant d’intervalle que je souhaite pour faire autant de gradient que je souhaite. Je suis partie sur 6 gradients de Rouge différent. Je créer ces intervalles en fonction de ma valeur maximale de cas crée auparavant, de façon linéaire. A vous de choisir quel algorithme vous souhaitez pour créer vos gradients, si vous voulez des intervalles avec autant d’écarts entre eux comme j’ai souhaité le faire ou en fonction d’autre chose. C’est selon vos souhaits selon comment vous souhaitez mettre en valeur vos données une fois sur la carte.
Affichage d’une légende
On vient de coloriser notre carte, mais on ne sait guerre comment elles sont exposées avec des chiffres précis. C’est pour cela que je vous proposer d’ajouter une légende pour préciser à quoi correspond chacun de nos gradients de couleur.
Je commence par ajouter une nouvelle division dans notre vue pour cette légende :
J’y ajoute un titre.
J’y ajoute une première boucle pour itérer sur l’ensemble de nos gradients de couleur, que j’inclus sous forme de petits carrés.
J’y ajout une seconde boucle pour itérer sur l’ensemble de nos intervalles de valeurs.
Vous pouvez voir que j’ai des appels de type fx dans mes balises. C’est du à l’utilisation d’une bibliothèque disponible dans Angular, FlexLayout, permettant de manier les flexbox directement dans le fichier HTML plutôt que de style CSS, je trouve cela un poil plus clair, mais ce n’est que mon opinion. Vous pouvez l’installer via npm (npm i -s @angular/flex-layout @angular/cdk).
Pour finaliser ma légende, et avoir cet effet de superposition de ma légende sur ma carte, on va parler d’index. Pour cela on va ajouter attribuer des classes à nos division dans notre fichier HTML :
Ajout d’une classe lastPlan pour notre carte
Ajout d’une première classe firstPlan et d’une seconde classe legend, pour notre légende
On y ajouter le SCSS suivant :
La classe firstPlan permet de mettre en premier plan notre légende
La classe lastPlan permet de mettre notre carte en second plan. Vous pouvez jouer avec les index de façon infini pour créer autant de plan que vous souhaitez utiliser plus de deux plans.
La classe squareLegend permet de définir la taille des carrés contenant nos couleurs.
La classe legend permet de définir le conteneur de l’ensemble de notre légende, de sa position sur l’écran ainsi que sa taille.
Affichage de data via popup
Je vous propose d’ajouter sur notre carte un popup, qui s’affiche au survol d’une région en affichant le nombre de cas confirmé au Covid19 qu’elle a.
On commence par créer deux nouvelles variables qui seront affiché dans notre vue :
Comme les noms qu’elles portent, la première pour afficher le nom de la région et la seconde pour afficher le nombre de cas. On les initialise à null dans le constructeur de notre composant.
On modifie notre fonction lors de l’événement mouseover, afin qu’elle affecte la valeur de la région et du nombre de cas à nos deux variables précédentes :
On utilise la classe ChangeDetectorRef dans la dernière ligne de notre fonction, qui offre des possibilités pour forcer les mises à jour de l’interface. On n’oublie pas de l’instancier en privée dans le constructeur du composant :
On modifie la fonction concernant l’événement mouseout, afin qu’elle supprime nos deux valeurs lorsque l’on sort d’une région :
Nous venons de modifier la partie du contrôleur, passons à la vue. On va créer une nouvelle division contenant notre popup :
Celle-ci ne s’affiche que si regionName contient une valeur. Vous pouvez voir que l’on a attribuer la classe css firstPlan pour qu’elle s’affiche dessus notre carte Leafleat, ainsi que la classe legendTop, définit dans notre fichier de style scss :
L’article touche à sa fin, vous devriez avoir le résultat suivant 😎
Conclusion
Vous avez donc accès pleinement à la librairie Leaflet.js dans votre application Angular.
Rien de bien complexe sur son intégration donc, juste un zeste déroutant d’utiliser du Javascript dans du Typescript, on mélange du typage fort avec des objets que l’on remplit d’attributs à la volé.
Vous pouvez faire des choses bien plus pousser. Réaliser une multitude de layers, que vous pouvez contrôler leur affichage ou non, ajouter une multitude de données dans vos GeoJSON pour binder avec des éléments dans Angular, pour réaliser par exemple un suivi du Covid19 mais sur plusieurs jours pour réaliser quelque chose de plus dynamique. Ou encore dessiner tout une multitude de polygone complexes, rendre leur affichage dynamique au sein même de la carte pour faire bouger automatiquement des marqueurs par exemple.
Dans cet article je vais introduire le sujet autour de la data visualisation. Je ne ferrais qu’introduire et je n’irais pas plus loin car c’est un domaine dans laquelle les formations en mathématiques, statistiques et probabilités sont le cœur du métier et qui forme les data scientist. Ce sont des personnes qui vont analyser des bases de données et extraire des informations, afin de trouver d’éventuelle corrélations entres elle. En effet, il se peut que certains facteurs puissent influer les autres. Connaître ces informations peut ajouter de la plus-value aux entreprises. Mieux vous connaîtrez votre marché et les besoins des clients, plus vous optimiserez les ventes de vos produits.
Prenons un exemple simple. Il y a fort à parier que les ventes de parapluie soient plus importantes en hiver, qu’en été. Et cela peut venir d’un principal facteur, qu’est la météo.
Le secteur étant large, je vais me consacrer qu’a une infime partie du domaine. Je vais me servir de plusieurs bibliothèques, tout cela en python :
Matplotlib : la plus connue de toute, elle permet de faire beaucoup, beaucoup de chose. Pour rapidement résumer, elle permet de réaliser très simplement des graphiques 2D.
Seaborn : elle se base sur la librairie précédente, et propose des graphiques encore plus détaillés pour pousser d’avantage l’aspect data visualisation.
Comme d’habitude, les sources du dataset utilisé et l’ensemble de mon code est disponible sur mon profil Github.
C’est parti ! 😎
Origine du dataset
Nous avons comme jeu de donnée diverses mesures médicales prises sur 768 patients. Celui-ci va vous permettre via 7 attributs différents nous permettre de déceler une quelconque relation si une personne est diabétique ou non. Nous avons les attributs suivants :
Pregnancies : nombre d’enfant
Glucose : taux de glucose dans le plasma sanguin (mm Hg)
Blood pressure : pression artérielle
Skin thinckness : épaisseur de peau au niveau du triceps (mm)
Insulin : taux d’insuline (mu U/mL)
BMI : indice de masse corporel (IMC, poids en kg / ( taille en m)²)
Diabete pedigree function
Age (année)
Outcome : la sortie qui sera 0 pour non diabétique, et 1 pour diabétique
On peut noter que le dataset n’est franchement pas équilibré :
Premiers pas
On comparant les 3 graphiques précédent, on peut réaliser déjà quelques observations qui pourraient influencer ou non le fait d’être diabétique. Cela reste purement statistique et donc n’en fait pas une science absolue :
Glucose : il semblerait qu’un taux plus important de glucose dans le sang soit perçue chez une personne diabétique
Skin thickness : on peut voir qu’une personne diabétique à une peau plus fine en moyenne
Insulin : une valeur légèrement plus forte serait présente chez une personne diabétique
Sur certains attributs, on ne peut trouver d’informations concluantes :