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 valeurNone
en Python.Une valeur
NULL
SQL est traduite en un objet Python, qui possĂšde lâattributis_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.");
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.