UDFs vetorizadas de Python¶
Este tópico apresenta as UDFs vetorizadas de Python.
Neste tópico:
Visão geral¶
As UDFs de Python vetorizadas permitem que você defina funções Python que recebem lotes de linhas de entrada como DataFrames do Pandas e retornam lotes de resultados como matrizes ou séries do Pandas. Você chama UDFs vetorizadas de Python da mesma forma que você chama outras UDFs de Python.
As vantagens de usar as UDFs vectorizadas Python em comparação com o padrão de processamento linha por linha incluem:
O potencial de melhor desempenho se seu código de Python operar eficientemente em lotes de linhas.
Menos lógica de transformação necessária se você estiver chamando bibliotecas que operam em DataFrames ou Arrays do Pandas.
Quando você utiliza UDFs vetorizadas de Python:
Você não precisa mudar a forma de escrever consultas usando UDFs de Python. Todos os lotes são tratados pelo framework da UDF e não pelo seu próprio código.
Como no caso de UDFs não vetorizadas, não há garantia de quais instâncias de seu código do manipulador verão quais lotes de entrada.
Introdução às UDFs vetorizadas de Python¶
Para criar uma UDF vetorizada de Python, use um dos mecanismos suportados para anotar sua função do manipulador.
Como usar o Decorator vectorized
¶
O módulo _snowflake
é exposto a UDFs de Python que são executadas dentro do Snowflake. Em seu código Python, importe o módulo _snowflake
e use o decorador vectorized
para especificar que seu manipulador espera receber um DataFrame do Pandas definindo o parâmetro input
como pandas.DataFrame
.
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
$$;
Como usar um atributo de função¶
Em vez de importar o módulo _snowflake e usar o decorador vectorized
, você pode definir o atributo especial _sf_vectorized_input
em sua função do manipulador.
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
$$;
Como definir um tamanho alvo de lote¶
Chamadas para a função do manipulador do Python devem ser executadas dentro de um limite de tempo, que é de 180 segundos, e cada DataFrame passado como entrada para a função do manipulador pode conter atualmente até alguns milhares de linhas. Para de permanecer dentro do limite de tempo, você pode querer definir o tamanho alvo do lote para sua função do manipulador, que impõe um número máximo de linhas por DataFrame de entrada. Observe que a definição de um valor maior não garante que o Snowflake codifique lotes com o número especificado de linhas. Você pode definir o tamanho alvo do lote usando o decorador vectorized
ou um atributo na função.
Nota
O uso de max_batch_size
destina-se apenas como um mecanismo para limitar o número de linhas que o UDF pode lidar por lote único. Por exemplo, se o UDF for escrito de uma forma que só possa processar no máximo 100 linhas de cada vez, então o max_batch_size
deve ser definido como 100. A configuração do max_batch_size
não se destina a ser usada como um mecanismo para especificar tamanhos arbitrários de grandes lotes. Se o UDF for capaz de processar lotes de qualquer tamanho, é recomendável deixar este parâmetro sem ajustes.
Como usar o Decorator vectorized
¶
Para definir o tamanho alvo do lote usando o decorador vectorized
, passe um valor inteiro positivo para o argumento chamado max_batch_size
.
Como exemplo, esta instrução cria uma UDF vetorizada de Python e limita cada Dataframe a um máximo de 100 linhas:
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
$$;
Como usar um atributo de função¶
Para definir o tamanho alvo do lote usando um atributo de função, defina um valor inteiro positivo para o atributo _sf_max_batch_size
em sua função do manipulador.
Como exemplo, esta instrução cria uma UDF vetorizada de Python e limita cada DataFrame a um máximo de 100 linhas:
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
$$;
Codificação do DataFrame¶
Lotes de argumentos para a UDF são codificados como arrays nos DataFramesde entrada do Pandas, e o número de linhas em cada DataFrame pode variar. Para obter mais informações, consulte Como definir um tamanho alvo de lote. Argumentos podem ser acessados no DataFrame pelo índice, ou seja, o primeiro argumento tem um índice de 0, o segundo tem um índice de 1, e assim por diante. O objeto Array ou Series do Pandas que o manipulador da UDF retorna deve ter o mesmo comprimento que o DataFrame de entrada.
Para ilustrar, suponha que você defina uma UDF vetorizada de Python da seguinte forma:
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]
$$;
Essa UDF usa df[0]
para acessar a array do Pandas para o primeiro argumento, e df[1]
para o segundo. O df[0] + df[1]
resulta em uma array do Pandas com as somas em pares dos elementos correspondentes das duas arrays. Depois de criar a UDF, você pode chamá-la com algumas linhas de entrada:
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 |
+------------------+
Aqui a função add_inputs
do Python recebe um DataFrame análogo a um criado com o seguinte código de Python:
>>> 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
A linha return df[0] + df[1]
na função do manipulador resulta em uma array semelhante ao seguinte código de Python:
>>> df[0] + df[1]
0 4.14
1 3.59
2 2.50
dtype: float64
Suporte a tipos¶
UDFs vetorizadas de Python oferecem suporte aos seguintes tipos SQL para argumentos e valores de retorno. A tabela reflete como cada argumento SQL é codificado como uma array Pandas de um determinado dtype.
Tipo de SQL |
dtype do Pandas |
Notas |
---|---|---|
NUMBER |
|
Para garantir que um argumento de entrada para uma UDF seja interpretado como não anulável, passe uma coluna de uma tabela criada usando a restrição de coluna |
FLOAT |
|
Valores NULL são codificados como valores NaN. Na saída, os valores NaN são interpretados como NULLs. |
BOOLEAN |
|
|
VARCHAR |
|
Tanto o SQL do Snowflake quanto o Pandas representam cadeias de caracteres usando a codificação UTF-8. |
BINARY |
|
|
DATE |
|
Cada valor é codificado como um |
VARIANT |
|
Cada linha de variante é convertida dinamicamente para um tipo do Python para argumentos e vice-versa para valores de retorno. Os seguintes tipos são convertidos em cadeias de caracteres em vez de tipos nativos do Python: |
OBJECT |
|
|
ARRAY |
|
|
TIME |
|
Cada valor é codificado como uma diferença de tempo em relação a meia-noite. Valores NULL são codificados como |
TIMESTAMP_LTZ |
|
Utiliza o fuso horário local para codificar cada valor como uma escala de nanossegundos |
TIMESTAMP_NTZ |
|
Codifica cada valor como uma escala de nanossegundos |
TIMESTAMP_TZ |
|
Codifica cada valor como uma escala de nanossegundos |
GEOGRAPHY |
|
Formata cada valor como GeoJSON e depois o converte para um |
Os seguintes tipos são aceitos como saída: Series
ou array
do Pandas, array
do NumPy, list
comum do Python e qualquer sequência iterável que contenha os tipos esperados descritos em Suporte a tipos. É eficiente usar Series
e array
do Pandas e array
do NumPy onde o dtype for bool
, boolean
, int16
, int32
, int64
, Int16
, Int32
, Int64
ou float64
, porque eles expõem seu conteúdo como memoryviews
. Isso significa que o conteúdo pode ser copiado em vez de cada valor ser lido sequencialmente.