22 juin 2021

Streamlit intermédiaire. Quelques trucs et astuces pendant que vous construisez… | par Peter Baumgartner

Par beasys


Quelques trucs et astuces pour construire une application en évolution

Peter Baumgartner
Il n’est jamais trop tôt pour commencer à échafauder votre application. Image parMichael Gaida de Pixabay.

Streamlit est un excellent outil pour donner à votre travail de science des données une interface. Je l’ai utilisé comme un outil de tableau de bord léger pour afficher des visualisations simples, envelopper des packages python dans une interface utilisateur et explorer des évaluations de modèles pour les modèles NLP (exemples en direct et ). Cela m’a permis de mieux comprendre mes données et de créer une interface qui peut aider à traduire entre le code/les données/l’analyse et la communication avec les parties prenantes et les experts en la matière.

Cependant, les prototypes d’aujourd’hui êtrecles applications de production de demain. Il y a un risque à rendre les choses trop faciles – avant de vous en rendre compte, vous avez défini des attentes selon lesquelles la prochaine itération se produira tout aussi rapidement, ou que votre application est robuste et bien testée, ou que son déploiement à l’échelle de l’entreprise est imminent. , ou que le nouveau stagiaire peut le prendre à partir d’ici pour économiser de l’argent.

Au fur et à mesure que votre application grandira, vous devrez gérer les douleurs de croissance. Dans cet article, je vais fournir quelques conseils et idées que j’ai trouvé utiles au fur et à mesure que mes applications streamit ont évolué. Je vais commencer par quelques conseils simples pour créer de meilleures applications et terminer par quelques idées pour rendre les applications modulaires et testables.

(En tant que note fonctionnelle, chaque fois que vous voyez un élément essentiel, vous devriez pouvoir l’exécuter en tant qu’application simplifiée et explorer vous-même. Si vous êtes prêt à faire un pip install streamlit pandas altair vega_datasets, vous pouvez exécuter n’importe lequel des éléments ci-dessous avec streamlit run <gist_url>).

Les noms de variables dans un DataFrame peuvent être en casse ou formatés d’une manière non appropriée pour les utilisateurs finaux, par exemple pointless_metric_my_boss_requested_and_i_reluctantly_included. La plupart des widgets simplifiés contiennent un format_func paramètre qui prend la fonction qui applique le formatage pour l’affichage aux valeurs d’option que vous fournissez au widget. À titre d’exemple simple, vous pouvez titrer la casse de chacun des noms de variables.

Vous pouvez également utiliser cette fonctionnalité, associée à un dictionnaire, pour gérer explicitement la mise en forme de vos valeurs. L’exemple ci-dessous nettoie les noms de colonne du birdstrikes ensemble de données à utiliser comme liste déroulante pour décrire chaque colonne.

Passer `dict.get` comme argument à `format_func` vous permet de contrôler explicitement l’affichage des valeurs du widget

Il peut être tentant de jeter ça à portée de main @st.cache décorateur sur tout et espérons le meilleur. Cependant, l’application inconsidérée de la mise en cache signifie que nous manquons une excellente occasion d’obtenir des méta et d’utiliser streamlit pour comprendre où la mise en cache aide le plus.

Plutôt que de décorer chaque fonction, créez deux versions de chaque fonction : une avec le décorateur et une sans. Effectuez ensuite une analyse comparative de base du temps nécessaire à l’exécution des versions mises en cache et non mises en cache de cette fonction.

Dans l’exemple ci-dessous, nous simulons le chargement d’un jeu de données volumineux en concaténant 100 copies du fichier airports jeu de données, puis en sélectionnant dynamiquement le premier n rangées et les décrire.

N’appliquez pas aveuglément `@st.cache` – faites une référence et appliquez le cas échéant

Étant donné que chaque étape (chargement des données, sélection des lignes, description de la sélection) est chronométrée, nous pouvons voir où la mise en cache fournit une accélération. D’après mon expérience avec cet exemple, mes heuristiques pour la mise en cache sont :

  • Toujours mettre en cache le chargement de l’ensemble de données
  • Probablement des fonctions de cache qui prennent plus d’une demi-seconde
  • Comparez tout le reste

Je pense que la mise en cache est l’une des fonctionnalités les plus importantes de streamlit et je sais qu’ils se concentrent dessus et l’améliorent. La mise en cache intelligente est également un problème complexe, c’est donc une bonne idée de se pencher davantage sur l’analyse comparative et de valider que la fonctionnalité de mise en cache agit comme prévu.

De nombreux exemples se concentrent sur la création de visualisations dynamiques, mais n’oubliez pas que vous pouvez également programmer des widgets dynamiques. L’exemple le plus simple de ce besoin est lorsque deux colonnes d’un ensemble de données ont une relation imbriquée et qu’il existe deux widgets pour sélectionner des valeurs dans ces deux colonnes. Lors de la création d’une application pour filtrer les données, la liste déroulante de la première colonne doit modifier les options disponibles dans la deuxième liste déroulante.

Lier le comportement de deux listes déroulantes est un cas d’utilisation courant. L’exemple ci-dessous crée un nuage de points avec le cars base de données. Nous avons besoin d’une liste déroulante dynamique ici car la variable que nous sélectionnons pour l’axe des x n’a pas besoin d’être disponible pour la sélection dans l’axe des y.

Nous pouvons également aller au-delà de cette fonctionnalité dynamique de base : et si nous triions les options disponibles sur l’axe des y selon leur corrélation avec la variable x sélectionnée ? Nous pouvons calculer les corrélations et les combiner avec le widget format_func pour afficher les variables et leurs corrélations dans un ordre trié.

Les widgets dynamiques sont un moyen puissant d’ajouter plus de contexte autour des fonctionnalités de votre application

Dans l’exemple ci-dessus, nous avons utilisé les f-strings de python pour interpoler les noms de variables et leurs valeurs de corrélation. Lors de la construction d’une interface autour de l’analyse, une grande partie de celle-ci nécessite la création ou la manipulation de chaînes dans les noms de variables, les valeurs de widget, les étiquettes d’axe, les étiquettes de widget ou la description narrative.

Si nous voulons afficher une analyse sous forme narrative et qu’il y a quelques variables particulières que nous voulons mettre en évidence, les f-strings et la démarque peuvent nous aider. Au-delà d’un moyen simple de remplir des chaînes avec des valeurs de variables spécifiques, c’est également un moyen simple de les formater en ligne. Par exemple, nous pouvons utiliser quelque chose comme ceci pour afficher des informations de base sur une colonne dans un ensemble de données et les mettre en évidence dans une chaîne de démarques.

mean = df["values"].mean()
n_rows = len(df)
md_results = f"The mean is **{mean:.2f}** and there are **{n_rows:,}**."st.markdown(md_results)

Nous avons utilisé ici deux formats : .2f arrondir un nombre flottant à deux décimales et , pour utiliser une virgule comme séparateur de milliers. Nous avons également utilisé la syntaxe Markdown pour mettre en gras les valeurs afin qu’elles soient visuellement proéminentes dans le texte.

Si vous avez créé des prototypes de visualisations avec une autre bibliothèque, envisagez de passer à Altair pour créer vos visualisations. D’après mon expérience, je pense qu’il y a trois raisons principales pour lesquelles un changement pourrait être bénéfique :

  1. Altair est probablement plus rapide (sauf si nous traçons beaucoup de données)
  2. Il fonctionne directement sur les pandas DataFrames
  3. Les visualisations interactives sont faciles à créer

Sur le premier point concernant la vitesse, nous pouvons voir une accélération drastique si nous avons prototypé en utilisant matplotlib. L’essentiel de cette accélération est simplement le fait qu’il faut plus de temps pour rendre une image statique et la placer dans l’application par rapport au rendu d’une visualisation javascript. Ceci est illustré dans l’exemple d’application ci-dessous, qui génère un nuage de points pour certaines données générées et affiche le moment de la création et du rendu pour chaque partie de la visualisation.

Altair peut être plus rapide que matplotlib si vous tracez moins de quelques milliers de points.

Travailler directement avec les DataFrames offre un autre avantage. Cela peut faciliter le processus de débogage : s’il y a un problème avec les données d’entrée, nous pouvons utiliser st.write(df) pour afficher le DataFrame dans une application simplifiée et l’inspecter. Cela rend la boucle de rétroaction pour le débogage des problèmes de données beaucoup plus courte. Le deuxième avantage est qu’il réduit la quantité de code de collage transformationnel parfois nécessaire pour créer des visualisations spécifiques. Pour les tracés de base, nous pourrions utiliser les méthodes de traçage d’un DataFrame, mais des visualisations plus complexes peuvent nous obliger à restructurer notre ensemble de données d’une manière qui a du sens avec l’API de visualisation. Ce code supplémentaire entre l’ensemble de données et la visualisation peut être la source d’une complexité supplémentaire et peut être un point douloureux à mesure que l’application se développe. Étant donné qu’Altair utilise la grammaire de visualisation Vega-Lite, les fonctions disponibles dans l’API de transformations peuvent être utilisées pour effectuer n’importe quelle visualisation des transformations appropriées.

Enfin, les visualisations interactives avec Altair sont faciles. Alors qu’une application peut commencer par utiliser des widgets simplifiés pour filtrer et sélectionner des données, une application peut également utiliser une visualisation comme mécanisme de sélection. Plutôt que de communiquer des informations sous forme de chaîne dans un widget ou un récit, les visualisations interactives permettent la communication visuelle des aspects des données dans une visualisation.

Il est facile de passer quelques heures avec streamlit et d’avoir une ligne 500 app.py fichier que personne d’autre que vous ne comprend. Si vous transmettez votre code, déployez votre application ou ajoutez une nouvelle fonctionnalité, il est maintenant possible que vous passiez beaucoup de temps à essayer de vous rappeler comment fonctionne votre code parce que vous avez négligé une bonne hygiène du code.

Si une application dépasse 100 lignes de code, elle peut probablement bénéficier d’un refactoring. Une bonne première étape consiste à créer des fonctions à partir du code et à mettre ces fonctions dans un helpers.py déposer. Cela facilite également le test et la comparaison de la mise en cache sur ces fonctions.

Il n’y a pas de manière précise de refactoriser le code, mais j’ai développé un exercice qui peut aider lors du démarrage d’un refactoring d’application.

Exercice de refactorisation

Dans app.py, essayez de :

  • importer uniquement les fonctions simplifiées et d’assistance (n’oubliez pas de comparer @st.cache sur ces fonctions d’assistance)
  • ne jamais créer une variable qui n’est pas entrée dans un objet simplifié, c’est-à-dire une visualisation ou un widget, dans la ligne de code suivante (à l’exception de la fonction de chargement de données)

Ce ne sont pas des règles strictes et rapides à toujours respecter : vous pouvez les suivre spécifiquement et avoir une application mal organisée parce que vous avez des fonctions volumineuses et complexes qui en font trop. Cependant, ce sont de bons objectifs pour commencer lorsque vous passez de tout dans app.py à une structure plus modulaire.

L’exemple ci-dessous met en évidence une application avant et après avoir effectué cet exercice.

Les fonctions sont excellentes et vous devriez les utiliser.

Un autre avantage de la réorganisation du code de cette manière est que les fonctions du fichier d’aide sont désormais plus faciles à écrire pour les tests. Parfois, j’ai du mal à trouver des idées sur ce qu’il faut tester, mais j’ai constaté qu’il est maintenant très facile de proposer des tests pour mes applications car je découvre plus rapidement les bogues et les cas limites maintenant que j’interagis plus étroitement avec les données et le code. Désormais, chaque fois que mon application affiche une trace, je corrige la fonction qui l’a provoquée et j’écris un test pour m’assurer que le nouveau comportement correspond à ce que j’attends.

J’ai apprécié mon temps avec streamlit – c’est un outil fantastique qui répond à un besoin clair dans le flux de travail de la science des données. Cependant, les prototypes d’aujourd’hui sont les applications de production de demain, et il est facile pour une application simple de devenir un cauchemar impossible à maintenir pour une équipe de science des données. Les idées abordées dans cet article m’ont aidé à gérer les difficultés associées au déplacement de mes applications au-delà d’un prototype et j’espère qu’elles feront de même pour vous.