Article original : How to Embed Interactive Python Visualizations on Your Website with Python and Matplotlib

Par Nick McCullum

Dans un précédent tutoriel freeCodeCamp, j'ai expliqué comment créer des visualisations de données à mise à jour automatique en Python avec Matplotlib et AWS.

Certains lecteurs m'ont contacté pour demander s'il existait un moyen de rendre les visualisations interactives. Heureusement, une solution simple est déjà disponible !

Dans ce tutoriel, je vais vous apprendre comment créer des visualisations de données interactives en Python. Ces visualisations sont d'excellentes candidates pour être intégrées à votre blog ou à votre site web.

La visualisation de données spécifique sur laquelle nous allons travailler

Au lieu de construire une visualisation de données complète à partir de zéro dans cet article, nous allons travailler avec la visualisation que nous avons créée dans mon dernier tutoriel.

La visualisation utilise pandas, matplotlib, et Python pour présenter divers points de données des 5 plus grandes banques cotées en bourse aux États-Unis.

Voici une image statique de la visualisation que nous avons créée :

Image

Le code réel de la visualisation est inclus ci-dessous. Nous avons abordé cela dans le dernier tutoriel, mais veuillez noter que vous devrez générer votre propre clé API IEX Cloud et l'inclure dans la variable IEX_API_Key pour que le script fonctionne.

########################
# Importer les dépendances
########################

import pandas as pd
import matplotlib.pyplot as plt

########################
# Importer et nettoyer les données
########################

IEX_API_Key = ''

tickers = [
            'JPM',
            'BAC',
            'C',
            'WFC',
            'GS',
            ]

# Créer une chaîne vide nommée 'ticker_string' à laquelle nous ajouterons les tickers et des virgules
ticker_string = ''

# Parcourir chaque élément de 'tickers' et les ajouter avec une virgule à ticker_string
for ticker in tickers:
    ticker_string += ticker
    ticker_string += ','

# Supprimer la dernière virgule de 'ticker_string'
ticker_string = ticker_string[:-1]

# Créer les chaînes pour les endpoints et les années
endpoints = 'chart'
years = '5'

# Interpoler les chaînes d'endpoint dans la chaîne HTTP_request
HTTP_request = f'https://cloud.iexapis.com/stable/stock/market/batch?symbols={ticker_string}&types={endpoints}&range={years}y&cache=true&token={IEX_API_Key}'

# Envoyer la requête HTTP à l'API IEX Cloud et stocker la réponse dans un DataFrame pandas
bank_data = pd.read_json(HTTP_request)

# Créer une liste vide dans laquelle nous ajouterons les Series pandas des données de prix boursiers
series_list = []

# Parcourir chacun de nos tickers et analyser une Series pandas de leurs prix de clôture sur les 5 dernières années
for ticker in tickers:
    series_list.append(pd.DataFrame(bank_data[ticker]['chart'])['close'])

# Ajouter une colonne de dates
series_list.append(pd.DataFrame(bank_data['JPM']['chart'])['date'])

# Copier la liste 'tickers' définie plus haut et ajouter un nouvel élément nommé 'Date'. 
# Ces éléments seront les noms de colonnes de notre DataFrame pandas plus tard.
column_names = tickers.copy()
column_names.append('Date')

# Concaténer les Series pandas ensemble dans un seul DataFrame
bank_data = pd.concat(series_list, axis=1)

# Nommer les colonnes du DataFrame et définir la colonne 'Date' comme index
bank_data.columns = column_names
bank_data.set_index('Date', inplace = True)

########################
# Créer la figure Python
########################

# Définir la taille du canevas matplotlib
fig = plt.figure(figsize = (18,8))

################################################
################################################
# Créer des sous-graphiques (subplots) en Python
################################################
################################################

########################
# Sous-graphique 1
########################
plt.subplot(2,2,1)

# Générer le boxplot
plt.boxplot(bank_data.transpose())

# Ajouter des titres au graphique et aux axes
plt.title('Boxplot of Bank Stock Prices (5Y Lookback)')
plt.xlabel('Bank')
plt.ylabel('Stock Prices')

# Ajouter des étiquettes à chaque boxplot individuel sur le canevas
ticks = range(1, len(bank_data.columns)+1)
labels = list(bank_data.columns)
plt.xticks(ticks,labels)

########################
# Sous-graphique 2
########################
plt.subplot(2,2,2)

# Créer les données de l'axe x
dates = bank_data.index.to_series()
dates = [pd.to_datetime(d) for d in dates]

# Créer les données de l'axe y
WFC_stock_prices =  bank_data['WFC']

# Générer le nuage de points (scatterplot)
plt.scatter(dates, WFC_stock_prices)

# Ajouter des titres au graphique et aux axes
plt.title("Wells Fargo Stock Price (5Y Lookback)")
plt.ylabel("Stock Price")
plt.xlabel("Date")

########################
# Sous-graphique 3
########################
plt.subplot(2,2,3)

# Créer les données de l'axe x
dates = bank_data.index.to_series()
dates = [pd.to_datetime(d) for d in dates]

# Créer les données de l'axe y
BAC_stock_prices =  bank_data['BAC']

# Générer le nuage de points (scatterplot)
plt.scatter(dates, BAC_stock_prices)

# Ajouter des titres au graphique et aux axes
plt.title("Bank of America Stock Price (5Y Lookback)")
plt.ylabel("Stock Price")
plt.xlabel("Date")

########################
# Sous-graphique 4
########################
plt.subplot(2,2,4)

# Générer l'histogramme
plt.hist(bank_data.transpose(), bins = 50)

# Ajouter une légende à l'histogramme
plt.legend(bank_data.columns,fontsize=10)

# Ajouter des titres au graphique et aux axes
plt.title("A Histogram of Daily Closing Stock Prices for the 5 Largest Banks in the US (5Y Lookback)")
plt.ylabel("Observations")
plt.xlabel("Stock Prices")

plt.tight_layout()

################################################
# Sauvegarder la figure sur notre machine locale
################################################

plt.savefig('bank_data.png')

Maintenant que nous avons une compréhension de la visualisation spécifique sur laquelle nous allons travailler, parlons de ce que signifie l'interactivité pour une visualisation.

Que signifie l'interactivité pour une visualisation ?

Il existe deux types de visualisations de données qu'il est utile d'intégrer sur votre site web.

Le premier type est une visualisation statique. Il s'agit essentiellement d'une image - pensez aux fichiers .png ou .jpg.

Le deuxième type est une visualisation dynamique. Ces visualisations changent en réponse au comportement de l'utilisateur, généralement par panoramique ou zoom. Les visualisations dynamiques ne sont pas stockées dans des fichiers .png ou .jpg, mais généralement dans des balises svg ou iframe.

Ce tutoriel porte sur la création de visualisations de données dynamiques. Plus précisément, la visualisation que nous voulons créer présentera les caractéristiques suivantes :

  1. Vous cliquerez sur un bouton en bas à gauche pour activer le mode dynamique.
  2. Une fois le mode dynamique activé, vous pouvez zoomer et vous déplacer dans la visualisation avec votre souris.
  3. Vous pouvez également recadrer et zoomer sur une section spécifique de la visualisation.

Dans la section suivante de ce tutoriel, vous apprendrez comment installer et importer la bibliothèque mpld3, qui est la dépendance Python que nous utiliserons pour créer nos graphiques interactifs.

Comment importer la bibliothèque mpld3

Pour utiliser la bibliothèque mpld3 dans notre application Python, nous devons d'abord accomplir deux étapes :

  1. Installer la bibliothèque mpld3 sur la machine sur laquelle nous travaillons.
  2. Importer la bibliothèque mpld3 dans notre script Python.

Tout d'abord, installons mpld3 sur notre machine locale.

Le moyen le plus simple de le faire est d'utiliser le gestionnaire de paquets pip pour Python3. Si pip est déjà installé sur votre machine, vous pouvez le faire en exécutant l'instruction suivante depuis votre ligne de commande :

pip3 install mpld3

Maintenant que mpld3 est installé sur notre machine, nous pouvons l'importer dans notre script Python avec l'instruction suivante :

import mpld3

Pour des raisons de lisibilité, il est considéré comme une bonne pratique d'inclure cet import avec le reste de vos imports en haut de votre script. Cela signifie que votre section d'importation ressemblera désormais à ceci :

########################
# Importer les dépendances
########################

import pandas as pd
import matplotlib.pyplot as plt
import mpld3

Comment transformer des visualisations matplotlib statiques en visualisations de données interactives

La fonctionnalité principale de la bibliothèque mpld3 est de prendre une visualisation matplotlib existante et de la transformer en code HTML que vous pouvez intégrer sur votre site web.

L'outil que nous utilisons pour cela est la méthode fig_to_html de mpld3, qui accepte un objet figure de matplotlib comme seul argument et renvoie du HTML.

Pour utiliser la méthode fig_to_html à cette fin, ajoutez simplement le code suivant à la fin de notre script Python :

html_str = mpld3.fig_to_html(fig)
Html_file= open("index.html","w")
Html_file.write(html_str)
Html_file.close()

Ce code génère le HTML et le sauvegarde sous le nom de fichier index.html dans votre répertoire de travail actuel. Voici à quoi cela ressemble lorsqu'il est rendu sur une page web :

Lorsque vous survolez cette visualisation, trois icônes apparaissent en bas à gauche. L'icône de gauche ramène la visualisation à son apparence par défaut. L'icône du milieu active le mode dynamique. L'icône de droite vous permet de recadrer et de zoomer sur un point spécifique de la visualisation.

Une erreur courante lors de l'utilisation de pandas et mpld3

Lors de la création de la visualisation interactive dans ce tutoriel, vous pourriez rencontrer l'erreur suivante générée par mpld3 :

TypeError: array([ 1.]) is not JSON serializable

Heureusement, il existe une solution bien documentée à cette erreur sur GitHub.

Vous devez modifier le fichier _display.py situé dans Lib\site-packages\mpld3 et remplacer la classe NumpyEncoder par celle-ci :

class NumpyEncoder(json.JSONEncoder):
    """ Encodeur JSON spécial pour les types numpy """

    def default(self, obj):
        if isinstance(obj, (numpy.int_, numpy.intc, numpy.intp, numpy.int8,
            numpy.int16, numpy.int32, numpy.int64, numpy.uint8,
            numpy.uint16,numpy.uint32, numpy.uint64)):
            return int(obj)
        elif isinstance(obj, (numpy.float_, numpy.float16, numpy.float32, 
            numpy.float64)):
            return float(obj)
        try: # Ajouté par ceprio 2018-04-25
            iterable = iter(obj)
        except TypeError:
            pass
        else:
            return list(iterable)
         # Laisser la méthode par défaut de la classe de base lever l'erreur TypeError
        return json.JSONEncoder.default(self, obj)

Une fois ce remplacement effectué, votre code devrait fonctionner correctement et votre fichier index.html devrait être généré avec succès.

Dernières pensées

Dans ce tutoriel, vous avez appris à créer des visualisations de données interactives en Python à l'aide des bibliothèques matplotlib et mpld3. Voici un résumé spécifique de ce que nous avons abordé :

  1. La définition d'une visualisation de données dynamique
  2. Comment installer et importer la bibliothèque mpld3 pour Python
  3. Comment utiliser la bibliothèque mpld3 pour transformer une visualisation matplotlib en une visualisation dynamique que vous pouvez intégrer sur votre site web
  4. Comment corriger une erreur courante rencontrée par les utilisateurs de la bibliothèque mpld3

Ce tutoriel a été écrit par Nick McCullum, qui enseigne le développement Python et JavaScript sur son site web.