Conception d’UDFs Python¶

Ce sujet vous aide Ă  concevoir des UDFs Python.

Dans ce chapitre :

Note

Les UDFs Python vectorisĂ©es vous permettent de dĂ©finir des fonctions Python qui reçoivent des lots de lignes d’entrĂ©e sous forme de DataFrames Pandas et renvoient des lots de rĂ©sultats sous forme de tableaux ou de sĂ©ries Pandas. L’interface lots permet d’obtenir de bien meilleures performances avec les scĂ©narios d’infĂ©rence de machine learning. Pour plus d’informations, voir UDFs Python vectorisĂ©es.

Choisir vos types de données¶

Avant d’écrire votre code :

  • Choisissez les types de donnĂ©es que votre fonction doit accepter comme arguments et le type de donnĂ©es que votre fonction doit retourner.

  • Tenez compte des problĂšmes liĂ©s aux fuseaux horaires.

  • DĂ©cidez comment traiter les valeurs NULL.

Pour plus d’informations sur la façon dont Snowflake fait correspondre les types de donnĂ©es Python et SQL, voir Mappages des types de donnĂ©es SQL-Python.

Valeurs et fuseaux horaires TIMESTAMP_LTZ¶

Une UDF Python est largement isolĂ©e de l’environnement dans laquelle elle est appelĂ©e. Cependant, le fuseau horaire est hĂ©ritĂ© de l’environnement d’appel. Si la session de l’appelant a dĂ©fini un fuseau horaire par dĂ©faut avant d’appeler l’UDF Python, alors l’UDF Python a le mĂȘme fuseau horaire par dĂ©faut. Pour plus d’informations sur les fuseaux horaires, voir TIMEZONE.

Valeurs NULL¶

Pour tous les types Snowflake, Ă  l’exception de Variant, un argument SQL NULL Ă  une UDF Python se traduit par la valeur None Python et une valeur None Python retournĂ©e se traduit Ă  nouveau par une valeur NULL en SQL.

Une valeur de type Variant peut ĂȘtre : SQL NULL ou un VARIANT JSON null. Pour des informations sur les donnĂ©es VARIANT NULL Snowflake, voir Valeurs NULL.

  • Un VARIANT JSON null est traduit en une valeur None en Python.

  • Une valeur NULL SQL est traduite en un objet Python, qui possĂšde l’attribut is_sql_null.

Pour un exemple, voir Traitement des NULL dans les UDFs Python.

Concevoir des UDFs Python qui respectent les contraintes imposées par Snowflake¶

Pour assurer la stabilitĂ© dans l’environnement Snowflake, Snowflake impose les contraintes suivantes aux UDFs Python. Sauf indication contraire, ces limitations sont appliquĂ©es lorsque l’UDF est exĂ©cutĂ©e, et non lors de sa crĂ©ation.

La formation de modĂšles de machine learning (ML) peut parfois ĂȘtre trĂšs exigeante en ressources. Les entrepĂŽts optimisĂ©s pour Snowpark sont un type d’entrepĂŽt virtuel Snowflake qui peut ĂȘtre utilisĂ© pour les charges de travail qui nĂ©cessitent une grande quantitĂ© de mĂ©moire et de ressources de calcul. Pour des informations sur les modĂšles de machine learning et sur Snowpark Python, voir Formation de modĂšles de machine learning avec Snowpark Python.

Mémoire¶

Évitez de consommer trop de mĂ©moire.

  • Les grandes valeurs de donnĂ©es peuvent consommer une grande quantitĂ© de mĂ©moire.

  • Une profondeur de pile excessive peut consommer une grande quantitĂ© de mĂ©moire.

Les UDFs renvoient une erreur si elles consomment trop de mémoire. La limite spécifique est sujette à changement.

Si des UDFs Ă©chouent parce qu’elles consomment trop de mĂ©moire, envisagez d’utiliser EntrepĂŽts optimisĂ©s par Snowpark.

Durée¶

Évitez les algorithmes qui prennent beaucoup de temps par appel.

Si une UDF prend trop de temps pour se terminer, Snowflake arrĂȘte l’instruction SQL et renvoie une erreur Ă  l’utilisateur. Cela limite l’impact et le coĂ»t d’erreurs telles que les boucles infinies.

Conception du module¶

Lorsqu’une instruction SQL appelle votre UDF Python, Snowflake appelle une fonction Python que vous avez Ă©crite. Votre fonction Python est appelĂ©e « fonction de gestionnaire Â» ou « gestionnaire Â» en abrĂ©gĂ©. Le gestionnaire est une fonction mise en Ɠuvre dans un module fourni par l’utilisateur.

Comme toute fonction Python, votre fonction doit ĂȘtre dĂ©clarĂ©e comme faisant partie d’un module.

Le gestionnaire est appelĂ© une fois pour chaque ligne transmise Ă  l’UDF Python. Le module qui contient la fonction n’est pas rĂ©importĂ© pour chaque ligne. Snowflake peut appeler la fonction de gestion du mĂȘme module plus d’une fois.

Pour optimiser l’exĂ©cution de votre code, Snowflake part du principe que l’initialisation peut ĂȘtre lente, alors que l’exĂ©cution de la fonction du gestionnaire est rapide. Snowflake fixe un dĂ©lai plus long pour l’exĂ©cution de l’initialisation (y compris le temps de chargement de votre UDF et le temps nĂ©cessaire Ă  l’initialisation du module) que pour l’exĂ©cution du gestionnaire (le temps d’appel de votre gestionnaire avec une ligne d’entrĂ©e).

Des informations supplĂ©mentaires sur la conception du module se trouvent dans CrĂ©ation d’UDFs Python.

Optimisation de l’initialisation et contrĂŽle de l’état global dans des UDFs scalaires¶

La plupart des UDFs scalaires doivent suivre les directives ci-dessous :

  • Si vous devez initialiser un Ă©tat partagĂ© qui ne change pas d’une ligne Ă  l’autre, initialisez-le dans le module plutĂŽt que dans la fonction du gestionnaire.

  • RĂ©digez votre fonction de gestionnaire de maniĂšre Ă  la sĂ©curiser des threads.

  • Évitez de stocker et de partager l’état dynamique entre les lignes.

Si vos UDF ne peuvent pas suivre ces directives, sachez que Snowflake s’attend Ă  ce que les UDFs scalaires soient traitĂ©es indĂ©pendamment. S’appuyer sur l’état partagĂ© entre les invocations peut entraĂźner un comportement inattendu, car le systĂšme peut traiter les lignes dans n’importe quel ordre et rĂ©partir ces invocations sur plusieurs instances. En outre, il peut y avoir plusieurs exĂ©cutions de la mĂȘme fonction de gestionnaire dans le mĂȘme interprĂ©teur Python sur plusieurs threads.

Les UDFs doivent Ă©viter de s’appuyer sur un Ă©tat partagĂ© entre les appels vers la fonction du gestionnaire. Cependant, il existe deux situations dans lesquelles vous pourriez vouloir qu’une UDF stocke un Ă©tat partagĂ© :

  • Du code qui contient une logique d’initialisation coĂ»teuse que vous ne voulez pas rĂ©pĂ©ter pour chaque ligne.

  • Du code qui exploite l’état partagĂ© entre les lignes, comme un cache.

Lorsqu’il est nĂ©cessaire de maintenir un Ă©tat global qui sera partagĂ© entre les invocations de gestionnaires, vous devez protĂ©ger l’état global contre les courses de donnĂ©es en utilisant les primitives de synchronisation dĂ©crites dans threading - Thread-based parallelism.

Optimisation de l’échelle et des performances¶

Utilisation des UDFs Python vectorisées avec les bibliothÚques de science des données¶

Lorsque votre code utilisera des bibliothĂšques de machine learning ou de Data Science, utilisez les UDFs Python vectorisĂ©es pour dĂ©finir des fonctions Python qui reçoivent des lignes d’entrĂ©e par lots sur lesquels ces bibliothĂšques sont optimisĂ©es pour fonctionner.

Pour plus d’informations, voir UDFs Python vectorisĂ©es.

Écrire des gestionnaires d’UDF Ă  thread unique¶

Écrivez des gestionnaires UDF qui sont Ă  thread unique. Snowflake se chargera de partitionner les donnĂ©es et de mettre Ă  l’échelle les UDF sur les ressources informatiques de l’entrepĂŽt virtuel.

Placer l’initialisation coĂ»teuse dans le module¶

Placez le code d’initialisation coĂ»teuse dans la portĂ©e du module. LĂ , il sera exĂ©cutĂ© une fois lors de l’initialisation de l’UDF. Évitez de rĂ©exĂ©cuter le code d’initialisation coĂ»teuse Ă  chaque invocation du gestionnaire d’UDF.

Gestion des erreurs¶

Une fonction Python utilisée comme UDF peut utiliser les techniques normales de gestion des exceptions de Python pour récupérer les erreurs dans la fonction.

Si une exception se produit Ă  l’intĂ©rieur de la fonction et n’est pas rĂ©cupĂ©rĂ©e par celle-ci, Snowflake gĂ©nĂšre une erreur qui inclut la trace de la pile pour l’exception. Lorsque l’enregistrement des exceptions non gĂ©rĂ©es est activĂ©, Snowflake enregistre les donnĂ©es relatives aux exceptions non gĂ©rĂ©es dans une table d’évĂ©nements.

Vous pouvez explicitement lancer une erreur sans la capturer afin de terminer la requĂȘte et de produire une erreur SQL. Par exemple :

if (x < 0):
  raise ValueError("x must be non-negative.");
Copy

Lors du dĂ©bogage, vous pouvez inclure des valeurs dans le texte du message d’erreur SQL. Pour ce faire, placez un corps de fonction Python entier dans un bloc try-catch ; ajoutez des valeurs d’argument au message de l’erreur capturĂ©e ; et lancez une erreur avec le message Ă©tendu. Pour Ă©viter de rĂ©vĂ©ler des donnĂ©es sensibles, supprimez les valeurs des arguments avant dĂ©ploiement dans un environnement de production.

Respecter les bonnes pratiques de sécurité¶

Pour vous assurer que votre gestionnaire fonctionne de maniÚre sécurisée, consultez les meilleures pratiques décrites dans Pratiques de sécurité pour UDFs et procédures.