Vektorisierte Python-UDFs¶
Dieses Thema bietet eine Einführung in vektorisierte Python-UDFs.
Unter diesem Thema:
Übersicht¶
Mit vektorisierten Python-UDFs können Sie Python-Funktionen definieren, mit denen Batches von Eingabezeilen als Pandas DataFrames empfangen und Batches von Ergebnissen als Pandas-Arrays oder Pandas Series zurückgeben werden. Vektorisierte Python-UDFs werden genauso aufgerufen wie andere Python-UDFs.
Die Verwendung von vektorisierten Python-UDFs bietet im Vergleich zur zeilenweisen Standardverarbeitung folgende Vorteile:
Potenziell bessere Performance, wenn der Python-Code effizient auf Batches von Zeilen ausgeführt wird
Weniger Transformationslogik durch den Aufruf von Bibliotheken, die Pandas DataFrames oder Pandas-Arrays verarbeiten.
Vektorisierte Python-UDFs bieten folgende Vorteile:
Das Schreiben von Abfragen muss für Python-UDFs nicht geändert werden. Die gesamte Batchverarbeitung wird vom UDF-Framework und nicht von Ihrem eigenen Code übernommen.
Wie bei nicht vektorisierten UDFs gibt es keine Garantie dafür, welche Instanzen Ihres Handler-Codes welche Batches von Eingaben verarbeiten werden.
Erste Schritte mit vektorisierten Python-UDFs¶
Um eine Python-UDF zu erstellen, verwenden Sie eines der unterstützten Verfahren zum Kommentieren Ihrer Handler-Funktion.
Verwenden des Decorator-Elements vectorized
¶
Das Modul _snowflake
ist für Python-UDFs zugänglich, die in Snowflake ausgeführt werden. Importieren Sie in Ihrem Python-Code das Modul _snowflake
, und verwenden Sie das Decorator-Element vectorized
, um anzugeben, dass Ihr Handler den Empfang eines Pandas DataFrame erwartet, indem Sie den Parameter input
auf pandas.DataFrame
setzen.
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
$$;
Verwenden von Funktionsattributen¶
Anstatt das _snowflake-Modul zu importieren und das Decorator-Element vectorized
zu verwenden, können Sie das spezielle Attribut _sf_vectorized_input
für Ihre Handler-Funktion festlegen.
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
$$;
Festlegen einer Zielbatchgröße¶
Aufrufe der Python-Handler-Funktion müssen innerhalb eines Zeitlimits von 180 Sekunden ausgeführt werden, wobei aber jeder DataFrame, der als Eingabe an die Handler-Funktion übergeben wird, derzeit bis zu einige tausend Zeilen enthalten. Um das Zeitlimit einzuhalten, sollten Sie für Ihre Handler-Funktion eine Zielbatchgröße festlegen, die eine maximale Anzahl von Zeilen pro Eingabe-DataFrame vorgibt. Beachten Sie, dass die Einstellung eines größeren Wertes nicht garantiert, dass Snowflake Batches mit der angegebenen Anzahl von Zeilen codieren wird. Sie können die Zielbatchgröße entweder mit dem Decorator-Element vectorized
oder mit einem Attribut der Funktion festlegen.
Bemerkung
Die Verwendung von max_batch_size
ist nur als Mechanismus zur Begrenzung der Anzahl der Zeilen gedacht, die eine UDF pro Batch verarbeiten kann. Wenn die UDF beispielsweise so geschrieben ist, dass nur maximal 100 Zeilen auf einmal verarbeitet werden können, dann muss max_batch_size
auf 100 gesetzt werden. Die Einstellung max_batch_size
ist nicht als Mechanismus zur Angabe beliebig großer Batchgrößen gedacht. Wenn die UDF in der Lage ist, Batches beliebiger Größe zu verarbeiten, empfiehlt es sich, diesen Parameter nicht zu setzen.
Verwenden des Decorator-Elements vectorized
¶
Um die Zielbatchgröße mit dem Decorator-Element vectorized
festzulegen, übergeben Sie einen positiven Ganzzahl-Wert für das Argument max_batch_size
.
Mit der folgenden Anweisung wird beispielsweise eine vektorisierte Python-UDF erstellt, die jeden Datenframe auf maximal 100 Zeilen begrenzt:
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
$$;
Verwenden von Funktionsattributen¶
Um die Zielbatchgröße mit einem Funktionsattribut festzulegen, legen Sie einen positiven Ganzzahl-Wert für das Attribut _sf_max_batch_size
der Handler-Funktion fest.
Mit der folgenden Anweisung wird beispielsweise eine vektoriesierte Python-UDF erstellt, die jeden DataFrame auf maximal 100 Zeilen begrenzt:
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
$$;
DataFrame-Codierung¶
Batches von Argumenten für die UDF werden als Arrays in der Pandas DataFrames-Eingabe codiert, wobei die Anzahl der Zeilen in jedem DataFrame variieren kann. Weitere Informationen dazu finden Sie unter Festlegen einer Zielbatchgröße. Auf die Argumente kann in dem DataFrame über deren Index zugegriffen werden, d. h. das erste Argument hat den Index 0, das zweite den Index 1 und so weiter. Das Pandas-Array oder die Pandas Series, die der UDF-Handler zurückgibt, muss die gleiche Länge haben wie die der Eingabe-DataFrame.
Angenommen, Sie definieren eine vektorisierte Python-UDF wie folgt:
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]
$$;
Dieses UDF verwendet df[0]
für den Zugriff auf das erste Argument des Pandas-Arrays und df[1]
für das zweite. df[0] + df[1]
ergibt ein Pandas-Array mit den paarweisen Summen der entsprechenden Elemente aus beiden Arrays. Nachdem Sie die UDF erstellt haben, können Sie diese mit einigen Eingabezeilen aufrufen:
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 |
+------------------+
m folgenden Beispiel erhält die Python-Funktion add_inputs
einen DataFrame analog zu einem Datenframe, der mit dem folgenden Python-Code erstellt wurde:
>>> 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
Die Zeile return df[0] + df[1]
in der Handler-Funktion ergibt ein Array ähnlich dem folgenden Python-Code:
>>> df[0] + df[1]
0 4.14
1 3.59
2 2.50
dtype: float64
Typunterstützung¶
Vektorisierte Python-UDFs unterstützen die folgenden SQL-Typen für Argumente und Rückgabewerte. Die Tabelle zeigt, wie jedes SQL-Argument als Pandas-Array eines bestimmten dtype kodiert wird.
SQL-Typ |
Pandas-dtype |
Anmerkungen |
---|---|---|
NUMBER |
|
Um sicherzustellen, dass ein UDF-Eingabeargument als nullwertfähig interpretiert wird, übergeben Sie eine Spalte aus einer Tabelle, die mit der Spalteneinschränkung |
FLOAT |
|
NULL-Werte werden als NaN-Werte codiert. In der Ausgabe werden die NaN-Werte als NULL-Werte interpretiert. |
BOOLEAN |
|
|
VARCHAR |
|
Sowohl Snowflake SQL als auch Pandas stellen Zeichenfolgen mit UTF-8-Codierung dar. |
BINARY |
|
|
DATE |
|
Jeder Wert wird als |
VARIANT |
|
Jede Variant-Zeile wird für Argumente dynamisch in einen Python-Typ konvertiert und umgekehrt für Rückgabewerte. Die folgenden Typen werden in Zeichenfolgen und nicht in native Python-Typen konvertiert: |
OBJECT |
|
|
ARRAY |
|
|
TIME |
|
Jeder Wert wird als Offset von Mitternacht codiert. NULL-Werte werden als |
TIMESTAMP_LTZ |
|
Verwendet die lokale Zeitzone, um jeden Wert als |
TIMESTAMP_NTZ |
|
Codiert jeden Wert als |
TIMESTAMP_TZ |
|
Codiert jeden Wert als |
GEOGRAPHY |
|
Formatiert jeden Wert als GeoJSON und wandelt ihn dann in einen Python-Wert vom Typ |
Die folgenden Typen werden als Ausgabe akzeptiert: Pandas Series
oder array
, NumPy array
, regulärer Python-list
-Typ und jede iterierbare Sequenz, die die in Typunterstützung beschriebenen erwarteten Typen enthält. Es ist effizient, Pandas Series
und array
und NumPy array
zu verwenden, wenn der dtype bool
, boolean
, int16
, int32
, int64
, Int16
, Int32
, Int64
oder float64
ist, da sie ihren Inhalt als memoryviews
ausgeben. Das bedeutet, dass der Inhalt kopiert werden kann und nicht jeder Wert nacheinander gelesen werden muss.