Nous utilisons largement les graphiques Plotly dans l'entreprise pour laquelle je travaille. Ils facilitent la création de graphiques interactifs qui ont fière allure. L'expérience Python via la bibliothèque Plotly Express est excellente et la barre pour démarrer est basse.
Nous avons deux cas d'utilisation principaux pour les graphiques Plotly :
Pour un rapport PDF typique, nous utilisons 5 à 20 chiffres pour montrer l'évolution d'une métrique particulière au fil du temps, la répartition d'une valeur sur un certain nombre de catégories ou la comparaison de différentes catégories les unes à côté des autres.
Pour créer nos rapports PDF, nous utilisons une combinaison de graphiques Weasyprint, Jinja et Plotly. Pour rendre un rapport au format PDF, nous devons d'abord restituer tous les graphiques sous forme d'images.
Pour ce faire, nous utilisons le super package Kaleido. Il utilise un navigateur Chrome pour afficher le graphique et l'enregistrer sous forme d'image. L'API est simple à utiliser.
from kaleido.scopes.plotly import PlotlyScope scope = PlotlyScope() img_bytes = scope.transform( figure=figure, format="png", width=1000, height=1000, scale=4, )
Cela restitue la figure sous la forme d'une image avec une hauteur et une largeur de 1 000 px et une échelle de rendu de 4 (c'est-à-dire que l'image a en fait des dimensions de 4 000 px x 4 000 px). Plus l'échelle est élevée, plus l'image finale a de DPI, plus elle est belle et plus le PDF final est grand.
Le rendu des graphiques prend un peu de temps et si vous en rendez beaucoup (10-20), cela représentera une part importante du temps d'exécution de votre programme. Pour accélérer notre pipeline de rendu PDF, nous avons déployé la solution suivante.
En interne, Kaleido sous-traite simplement le problème du rendu du graphique sous forme d'image à un navigateur Web Chrome inclus. Cela signifie que pour Python lui-même, le rendu de cette image attend essentiellement les E/S.
Pour accélérer ce processus particulier et comme nous attendons juste les E/S, nous pouvons utiliser le multithreading.
Commençons par créer une figure aléatoire, comme ceci :
import pandas as pd import numpy as np import plotly.graph_objects as go def get_random_figure() -> go.Figure: n_bars = 50 dates = pd.date_range(start="2021-01-01", end="2021-12-31", freq="M") figure = go.Figure() for i in range(n_bars): values = np.random.rand(len(dates)) figure.add_trace(go.Bar(x=dates, y=values, name=f"Label {i+1}")) figure.update_layout( dict( barmode="group", legend=dict(orientation="h", yanchor="top", xanchor="left"), ) ) figure.update_layout(yaxis=dict(tickformat=".0%"), xaxis=dict(showgrid=False)) return figure
Maintenant, transformer une figure en image peut être fait en utilisant le code ci-dessus :
from kaleido.scopes.plotly import PlotlyScope import plotly.graph_objects as go def figure_to_bytes(figure: go.Figure) -> bytes: scope = PlotlyScope() return scope.transform(figure=figure, format="png", width=1000, height=1000, scale=4)
Et enfin on définit aussi pour plus tard :
def transform_random_figure() -> bytes: return figure_to_bytes(get_random_figure())
Vous savez peut-être ou non qu'en raison du GIL (verrouillage global de l'interpréteur) dans Python, un seul thread peut exécuter du code Python en même temps. Puisque la transformation du graphique en image n'est pas du code Python, nous pouvons utiliser des threads pour démarrer la transformation de plusieurs graphiques en même temps puis collecter les résultats.
Pour cela, nous définissons une classe d'assistance :
from kaleido.scopes.plotly import PlotlyScope scope = PlotlyScope() img_bytes = scope.transform( figure=figure, format="png", width=1000, height=1000, scale=4, )
Cette classe va nous aider à récupérer le résultat de la transformation (c'est-à-dire les octets de l'image).
La prochaine chose que nous devons faire est de suivre le modèle standard pour travailler avec des threads en Python :
Nos threads doivent chacun appeler transform_random_figure() puis renvoyer les octets. Nous démarrons 10 discussions dans ce cas.
import pandas as pd import numpy as np import plotly.graph_objects as go def get_random_figure() -> go.Figure: n_bars = 50 dates = pd.date_range(start="2021-01-01", end="2021-12-31", freq="M") figure = go.Figure() for i in range(n_bars): values = np.random.rand(len(dates)) figure.add_trace(go.Bar(x=dates, y=values, name=f"Label {i+1}")) figure.update_layout( dict( barmode="group", legend=dict(orientation="h", yanchor="top", xanchor="left"), ) ) figure.update_layout(yaxis=dict(tickformat=".0%"), xaxis=dict(showgrid=False)) return figure
La méthode start() appellera également la méthode run() du thread qui démarre la logique réelle (c'est-à-dire exécute la fonction donnée, ce qui dans notre cas signifie transform_random_figure()).
Pour collecter les résultats, nous utilisons la méthode join() des threads et écrivons le résultat dans des fichiers.
from kaleido.scopes.plotly import PlotlyScope import plotly.graph_objects as go def figure_to_bytes(figure: go.Figure) -> bytes: scope = PlotlyScope() return scope.transform(figure=figure, format="png", width=1000, height=1000, scale=4)
L'idée principale ici est que, chaque fois que l'on veut transformer un graphique en image, on démarre un fil et ce fil attendra que le graphique soit terminé en arrière-plan.
Une fois que nous avons rassemblé l'ensemble du rapport, nous appelons join() sur tous les fils de discussion et récupérons les images de tous les graphiques, puis les mettons dans le rapport.
De cette façon, nous pouvons déjà générer l'intégralité du rapport sans graphiques et gagner du temps en n'attendant pas que chaque graphique soit transformé individuellement.
En résumé, si vous souhaitez transformer plusieurs graphiques Plotly en images, utilisez le module multithreading de la bibliothèque standard Python pour accélérer votre processus de conversion.
Vous pouvez le faire très facilement en déplaçant simplement l'appel transform() dans un thread, puis en attendant que tous les threads se terminent.
def transform_random_figure() -> bytes: return figure_to_bytes(get_random_figure())
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!