UDFs Python vectorisées¶
Cette rubrique présente les UDFs Python vectorisées.
Dans ce chapitre :
Vue d’ensemble¶
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. Vous appelez des UDFs Python vectorisées de la même manière que vous appelez les autres UDFs Python.
Les avantages de l’utilisation des UDFs Python vectorisées par rapport au modèle de traitement ligne par ligne par défaut sont les suivants :
Le potentiel de meilleures performances si votre code Python fonctionne efficacement sur des lots de lignes.
Moins de logique de transformation requise si vous faites appel à des bibliothèques qui fonctionnent sur des DataFrames Pandas ou des tableaux Pandas.
Lorsque vous utilisez des UDFs Python vectorisées :
Vous n’avez pas besoin de modifier la façon dont vous écrivez les requêtes en utilisant des UDFs Python. Tous les lots sont gérés par le framework UDF plutôt que par votre propre code.
Comme pour les UDFs qui ne sont pas vectorisées, il n’y a aucune garantie quant aux instances de votre code de gestionnaire qui verront les lots d’entrée.
Premiers pas avec les UDFs Python vectorisées¶
Pour créer une UDF Python vectorisée, utilisez l’un des mécanismes pris en charge pour annoter votre fonction de gestionnaire.
Utilisation du décorateur vectorized
¶
Le module _snowflake
est exposé à des UDFs Python qui s’exécutent dans Snowflake. Dans votre code Python, importez le module _snowflake
et utilisez le décorateur vectorized
pour spécifier que votre gestionnaire s’attend à recevoir un DataFrame Pandas en définissant le paramètre pandas.DataFrame
sur input
.
create function add_one_to_inputs(x number(10, 0), y number(10, 0))
returns number(10, 0)
language python
runtime_version = 3.8
packages = ('pandas')
handler = 'add_one_to_inputs'
as $$
import pandas
from _snowflake import vectorized
@vectorized(input=pandas.DataFrame)
def add_one_to_inputs(df):
return df[0] + df[1] + 1
$$;
Utilisation d’un attribut de fonction¶
Plutôt que d’importer le module _snowflake et d’utiliser le décorateur vectorized
, vous pouvez définir l’attribut spécial _sf_vectorized_input
sur votre fonction de gestionnaire.
create function add_one_to_inputs(x number(10, 0), y number(10, 0))
returns number(10, 0)
language python
runtime_version = 3.8
packages = ('pandas')
handler = 'add_one_to_inputs'
as $$
import pandas
def add_one_to_inputs(df):
return df[0] + df[1] + 1
add_one_to_inputs._sf_vectorized_input = pandas.DataFrame
$$;
Définition d’une taille de lot cible¶
Les appels à la fonction de gestionnaire Python doivent s’exécuter dans un délai limité, qui est de 180 secondes, et chaque DataFrame transmis en entrée à la fonction de gestionnaire peut actuellement contenir jusqu’à quelques milliers de lignes. Afin de respecter la limite de temps, vous pouvez définir la taille de lot cible pour votre fonction de gestionnaire, qui impose un nombre maximal de lignes par DataFrame d’entrée. Notez que la définition d’une valeur plus grande ne garantit pas que Snowflake codera les lots avec le nombre de lignes spécifié. Vous pouvez définir la taille du lot cible en utilisant soit le décorateur vectorized
soit un attribut de la fonction.
Note
L’utilisation de max_batch_size
est uniquement destinée à limiter le nombre de lignes que l’UDF peut traiter par lot. Par exemple, si l’UDF est écrite de manière à ne pouvoir traiter que 100 lignes au maximum à la fois, alors max_batch_size
doit être défini comme 100. Le paramètre max_batch_size
n’est pas destiné à être utilisé comme un mécanisme permettant de spécifier des tailles de lot arbitraires et importantes. Si l’UDF est capable de traiter des lots de n’importe quelle taille, il est recommandé de ne pas définir ce paramètre.
Utilisation du décorateur vectorized
¶
Pour définir la taille de lot cible à l’aide du décorateur vectorized
passez une valeur entière positive pour l’argument nommé max_batch_size
.
Par exemple, cette instruction crée une UDF Python vectorisée et limite chaque dataframe à un maximum de 100 lignes :
create function add_one_to_inputs(x number(10, 0), y number(10, 0))
returns number(10, 0)
language python
runtime_version = 3.8
packages = ('pandas')
handler = 'add_one_to_inputs'
as $$
import pandas
from _snowflake import vectorized
@vectorized(input=pandas.DataFrame, max_batch_size=100)
def add_one_to_inputs(df):
return df[0] + df[1] + 1
$$;
Utilisation d’un attribut de fonction¶
Pour définir la taille de lot cible à l’aide d’un attribut de fonction, définissez une valeur entière positive pour l’attribut _sf_max_batch_size
de votre fonction de gestionnaire.
Par exemple, cette instruction crée une UDF Python vectorisée et limite chaque DataFrame à un maximum de 100 lignes :
create function add_one_to_inputs(x number(10, 0), y number(10, 0))
returns number(10, 0)
language python
runtime_version = 3.8
packages = ('pandas')
handler = 'add_one_to_inputs'
as $$
import pandas
def add_one_to_inputs(df):
return df[0] + df[1] + 1
add_one_to_inputs._sf_vectorized_input = pandas.DataFrame
add_one_to_inputs._sf_max_batch_size = 100
$$;
Encodage DataFrame¶
Les lots d’arguments de l’UDF sont codés sous forme de tableaux dans les DataFrames Pandas d’entrée, et le nombre de lignes dans chaque DataFrame peut varier. Pour plus d’informations, voir Définir une taille de lot cible. Les arguments sont accessibles dans le DataFrame par leur index, c’est-à-dire que le premier argument a un index de 0, le second a un index de 1, et ainsi de suite. Le tableau ou la série Pandas que le gestionnaire UDF renvoie doit avoir la même longueur que le DataFrame d’entrée.
Pour illustrer ce propos, supposons que vous définissiez une UDF Python vectorisée comme suit :
create or replace function add_inputs(x int, y float)
returns float
language python
runtime_version = 3.8
packages = ('pandas')
handler = 'add_inputs'
as $$
import pandas
from _snowflake import vectorized
@vectorized(input=pandas.DataFrame)
def add_inputs(df):
return df[0] + df[1]
$$;
Cette UDF utilise df[0]
pour accéder au tableau Pandas pour le premier argument et df[1]
pour le second. df[0] + df[1]
donne un tableau Pandas contenant les sommes par paires des éléments correspondants des deux tableaux. Après avoir créé l’UDF, vous pouvez l’appeler avec quelques lignes d’entrée :
select add_inputs(x, y)
from (
select 1 as x, 3.14::float as y union all
select 2, 1.59 union all
select 3, -0.5
);
+------------------+
| ADD_INPUTS(X, Y) |
|------------------|
| 4.14 |
| 3.59 |
| 2.5 |
+------------------+
Ici, la fonction Python add_inputs
reçoit un DataFrame analogue à celui créé avec le code Python suivant :
>>> import pandas
>>> df = pandas.DataFrame({0: pandas.array([1, 2, 3]), 1: pandas.array([3.14, 1.59, -0.5])})
>>> df
0 1
0 1 3.14
1 2 1.59
2 3 -0.50
La ligne return df[0] + df[1]
de la fonction du gestionnaire donne un tableau similaire au code Python suivant :
>>> df[0] + df[1]
0 4.14
1 3.59
2 2.50
dtype: float64
Prise en charge du type¶
Les UDFs Python vectorisées prennent en charge les types SQL suivants pour les arguments et les valeurs de retour. Le tableau reflète la manière dont chaque argument SQL est codé en tant que tableau Pandas d’un dtype particulier.
Type SQL |
Dtype Pandas |
Remarques |
---|---|---|
NUMBER |
|
Pour s’assurer qu’un argument d’entrée d’une UDF est interprété comme ne pouvant pas être null, validez une colonne d’une table créée à l’aide de la contrainte de colonne |
FLOAT |
|
Les valeurs NULL sont codées comme des valeurs NaN. Dans la sortie, les valeurs NaN sont interprétées comme NULLs. |
BOOLEAN |
|
|
VARCHAR |
|
Snowflake SQL et Pandas représentent tous deux les chaînes qui utilisent l’encodage UTF-8. |
BINARY |
|
|
DATE |
|
Chaque valeur est codée comme un |
VARIANT |
|
Chaque ligne de variante est convertie dynamiquement en un type Python pour les arguments et vice versa pour les valeurs de retour. Les types suivants sont convertis en chaînes plutôt qu’en types Python natifs : |
OBJECT |
|
|
ARRAY |
|
|
TIME |
|
Chaque valeur est encodée comme un décalage par rapport à minuit. Les valeurs NULL sont encodées en tant que |
TIMESTAMP_LTZ |
|
Utilise le fuseau horaire local pour encoder chaque valeur sous forme de nanoseconde |
TIMESTAMP_NTZ |
|
Encode chaque valeur comme un |
TIMESTAMP_TZ |
|
Encode chaque valeur comme un |
GEOGRAPHY |
|
Formate chaque valeur en GeoJSON et la convertit ensuite en un |
Les types suivants sont acceptés en sortie : Pandas Series
ou array
, NumPy array
, Python ordinaire list
, et toute séquence itérable contenant les types attendus décrits dans Prise en charge du type. Il est efficace d’utiliser Pandas Series
et array
et NumPy array
lorsque le dtype est bool
, boolean
, int16
, int32
, int64
, Int16
, Int32
, Int64
ou float64
car ces formats exposent leur contenu comme memoryviews
. Cela signifie que le contenu peut être copié plutôt que chaque valeur soit lue de manière séquentielle.