벡터화된 Python UDF

이 항목에서는 벡터화된 Python UDF를 소개합니다.

이 항목의 내용:

개요

벡터화된 Python UDF를 사용하면 입력 행 배치를 Pandas DataFrames 로 수신하고 결과 배치를 Pandas 배열 또는 Series 로 반환하는 Python 함수를 정의할 수 있습니다. 다른 Python UDF를 호출하는 것과 같은 방식으로 벡터화된 Python UDF를 호출합니다.

기본 행 단위 처리 패턴에 비해 벡터화된 Python UDF를 사용할 때의 이점은 다음과 같습니다.

  • Python 코드가 행 배치에서 효율적으로 작동할 경우 성능이 향상될 가능성이 있습니다.

  • Pandas DataFrame 또는 Pandas 배열에서 작동하는 라이브러리로 호출하는 경우 변환 논리가 덜 필요합니다.

벡터화된 Python UDF를 사용하는 경우:

  • Python UDF를 사용하여 쿼리를 작성하는 방법을 변경할 필요가 없습니다. 모든 일괄 처리는 자체 코드가 아니라 UDF 프레임워크로 처리됩니다.

  • 벡터화되지 않은 UDF와 마찬가지로, 처리기 코드의 어떤 인스턴스에서 어떤 입력 배치가 나타날지 보장할 수 없습니다.

벡터화된 Python UDF 시작하기

벡터화된 Python UDF를 만들려면 처리기 함수에 주석을 추가하기 위해 지원되는 메커니즘 중 하나를 사용하십시오.

vectorized 데코레이터 사용하기

_snowflake 모듈은 Snowflake 내에서 실행되는 Python UDF에 노출됩니다. Python 코드에서 _snowflake 모듈을 가져오고 vectorized 데코레이터를 사용하여 input 매개 변수를 pandas.DataFrame 으로 설정함으로써 핸들러가 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
$$;
Copy

함수 속성 사용하기

_snowflake 모듈을 가져오고 vectorized 데코레이터를 사용하는 대신, 처리기 함수에 특수한 _sf_vectorized_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

def add_one_to_inputs(df):
  return df[0] + df[1] + 1

add_one_to_inputs._sf_vectorized_input = pandas.DataFrame
$$;
Copy

대상 배치 크기 설정하기

Python 처리기 함수에 대한 호출은 180초의 시간 제한 내에서 실행해야 하며 처리기 함수에 대한 입력으로 전달된 각 DataFrame에는 현재 최대 수천 개의 행이 포함될 수 있습니다. 시간 제한 내로 유지하기 위해 핸들러 함수의 대상 배치 크기를 설정하고 싶을 수도 있는데, 그러면 입력 DataFrame당 최대 행 개수가 지정됩니다. 더 큰 값을 설정한다고 해서 Snowflake가 지정된 행 개수로 배치를 인코딩하리라는 보장은 없습니다. vectorized 데코레이터 또는 함수의 속성을 사용하여 대상 배치 크기를 설정할 수 있습니다.

참고

max_batch_size 를 사용하는 것은 UDF가 단일 배치당 처리할 수 있는 행 수를 제한하는 메커니즘의 의미가 있습니다. 예를 들어, 한 번에 최대 100개의 행만 처리할 수 있는 방식으로 UDF가 작성된 경우 max_batch_size 는 100로 설정해야 합니다. max_batch_size 설정은 임의의 큰 배치 크기를 지정하는 메커니즘으로 사용하기 위한 것은 아닙니다. UDF가 모든 크기의 배치를 처리할 수 있는 경우 이 매개 변수를 설정하지 않은 상태로 두는 것이 좋습니다.

vectorized 데코레이터 사용하기

vectorized 데코레이터를 사용하여 대상 배치 크기를 설정하려면 max_batch_size 로 명명된 인자용으로 양의 정수 값을 전달하십시오.

한 예로서, 이 문을 실행하면 각 Dataframe을 최대 100개의 행으로 제한하는 벡터화된 Python UDF가 생성됩니다.

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
$$;
Copy

함수 속성 사용하기

함수 속성을 사용하여 대상 배치 크기를 설정하려면 핸들러 함수에서 _sf_max_batch_size 속성에 대해 양의 정수 값을 설정하십시오.

한 예로서, 이 문을 실행하면 각 DataFrame을 최대 100개의 행으로 제한하는 벡터화된 Python UDF가 생성됩니다.

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
$$;
Copy

DataFrame 인코딩

UDF에 대한 인자의 배치는 입력 Pandas DataFrame의 배열로 인코딩되며 각 DataFrame의 행 개수는 다를 수 있습니다. 자세한 내용은 대상 배치 크기 설정하기 를 참조하십시오. 인자는 인덱스로 DataFrame에서 액세스할 수 있습니다. 즉, 첫 번째 인자의 인덱스는 0이고 두 번째 인자의 1인 식입니다. UDF 핸들러가 반환하는 Pandas 배열 또는 Series는 입력 DataFrame의 Pandas 배열 또는 Series와 길이와 같아야 합니다.

설명을 위해 다음과 같이 벡터화된 Python UDF를 정의한다고 해보겠습니다.

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]
$$;
Copy

이 UDF는 첫 번째 인자에 대해서는 df[0] 를 사용하여 Pandas 배열에 액세스하고, 두 번째 인자에 대해서는 df[1] 을 사용합니다. df[0] + df[1] 은 두 배열의 해당 요소를 쌍으로 합한 Pandas 배열을 생성합니다. UDF를 만든 후, 일부 입력 행으로 이를 호출할 수 있습니다.

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  |
+------------------+
Copy

여기서 add_inputs Python 함수는 다음 Python 코드로 생성된 것과 유사한 DataFrame을 수신합니다.

>>> 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
Copy

핸들러 함수의 return df[0] + df[1] 행은 다음 Python 코드와 유사한 배열을 생성합니다.

>>> df[0] + df[1]
0    4.14
1    3.59
2    2.50
dtype: float64
Copy

타입 지원

벡터화된 Python UDF는 인자 및 반환 값에 대해 다음 SQL 타입 을 지원합니다. 이 표는 각 SQL 인자가 특정 dtype 의 Pandas 배열로 인코딩되는 방식을 반영합니다.

SQL 형식

Pandas dtype

참고

NUMBER

NUMBER 인자에 대한 Int16, Int32 또는 Int64 는 소수 자릿수가 0으로, 모두 64비트 또는 그보다 작은 정수 형식에 맞습니다. 인자가 null을 허용하지 않을 경우 int16, int32 또는 int64 가 대신 사용됩니다. (UDTF의 경우 Int16, Int32 또는 Int64 가 항상 사용됩니다.) . . 소수 자릿수가 0이 아닌 인자 또는 배열 요소가 decimal.Decimal 값으로 인코딩되는 64비트 정수 이내로 맞지 않는 인자의 경우 object 입니다. . . 16비트 dtype을 보장하려면 최대 NUMBER 4의 전체 자릿수를 사용하십시오. 32비트 dtype을 보장하려면 최대 9의 NUMBER 전체 자릿수를 사용하십시오. 64비트 dtype을 보장하려면 최대 18의 NUMBER 전체 자릿수를 사용하십시오.

UDF에 대한 입력 인자가 nullable이 아닌 것으로 해석되도록 보장하려면 NOT NULL 열 제약 조건을 사용하여 생성된 테이블의 열을 전달하거나 인자에 IFNULL 과 같은 함수를 사용하십시오.

FLOAT

float64

NULL 값은 NaN 값으로 인코딩됩니다. 출력에서는 NaN 값이 NULL로 해석됩니다.

BOOLEAN

Nullable 인자의 경우 boolean 또는 nullable이 아닌 인자의 경우 bool 입니다.

VARCHAR

string

Snowflake SQL과 Pandas는 모두 UTF-8 인코딩을 사용하여 문자열을 나타냅니다.

BINARY

bytes

DATE

datetime64

각각의 값은 시간 구성 요소가 없는 datetime64 로 인코딩됩니다. NULL 값은 numpy.timedelta('NaT') 로 인코딩됩니다.

VARIANT

object . . 각각의 값은 dict, list, int, float, str 또는 bool 로 인코딩됩니다.

각 베리언트 행은 인자에 대해 동적으로 Python 형식으로 변환되고 반환 값에 대해서는 그 반대로 변환됩니다. decimal, binary, date, time, timestamp_ltz, timestamp_ntz, timestamp_tz 형식은 기본 Python 형식이 아닌 문자열로 변환됩니다.

OBJECT

object . . 각 요소는 dict로 인코딩됩니다.

ARRAY

object . . 각 요소는 목록으로 인코딩됩니다.

TIME

timedelta64

각각의 값은 자정부터 오프셋으로 인코딩됩니다. NULL 값은 numpy.timedelta64('NaT') 로 인코딩됩니다. 반환 형식으로 사용 시, 출력의 요소는 [00:00:00, 23:59:59.999999999] 범위의 numpy.timedelta64 또는 datetime.time 값일 수 있습니다.

TIMESTAMP_LTZ

datetime64

현지 타임존을 사용하여 각각의 값을 UTC Unix Epoch에 상대적인 나노초 단위의 numpy.datetime64 로 인코딩합니다. NULL 값은 numpy.datetime64('NaT') 로 인코딩됩니다. 반환 형식으로 사용 시, 출력의 요소는 numpy.datetime64 또는 타임존 naive datetime.datetime 또는 pandas.Timestamp 값일 수 있습니다.

TIMESTAMP_NTZ

datetime64

각각의 값을 나노초 단위 numpy.datetime64 로 인코딩합니다. NULL 값은 numpy.datetime64('NaT') 로 인코딩됩니다. 반환 형식으로 사용 시, 출력의 요소는 numpy.datetime64 또는 타임존 naive datetime.datetime 또는 pandas.Timestamp 값일 수 있습니다.

TIMESTAMP_TZ

object

각각의 값을 나노초 단위 pandas.Timestamp 로 인코딩합니다. NULL 값은 pandas.NA 로 인코딩됩니다. 반환 형식으로 사용 시, 출력의 요소는 타임존을 인식하는 datetime.datetime 또는 pandas.Timestamp 값일 수 있습니다.

GEOGRAPHY

object

각각의 값을 GeoJSON 형식으로 지정한 다음, Python dict 로 변환합니다.

Pandas Series 또는 array, NumPy array, 일반 Python list, 타입 지원 에 설명된 예상 형식을 포함하는 반복 가능한 모든 시퀀스와 같은 형식이 출력으로 허용됩니다. bool, boolean, int16, int32, int64, Int16, Int32, Int64 또는 float64 는 내용을 memoryviews 로 노출하므로, dtype이 위와 같은 경우 Pandas Seriesarray 및 NumPy array 사용하는 것이 효율적입니다. 이는 각각의 값을 순차적으로 읽는 대신 내용을 복사할 수 있다는 뜻입니다.