[プレビュー]BigQueryでpythonでUDFが作成可能になりました
2025/4/2のアップデートでPython user-defined functions
(以下Python UDF)がプレビューとなりました。
気になった機能なので早速触れてみました。
概要
PythonUDFを使用するとSQL内でPythonを用いた処理が実装できます。PyPIからのサードパーティライブラリのインストールや、Cloud StorageなどGoogle Cloudの他のリソースへのアクセスが可能です。
またPython UDF はBigQuery のマネージドリソース上で構築・実行されます。リモート関数はCloud Run(Cloud Run FUnctions)上で実行されましたが、PythonUDFはBigQUeryのマネージドリソースで実行されるのでCloud Runのリソース管理が不要になるのは大きいなと思います。
料金
以下となっているようです。
- 現在のプレビュー中は料金なし
- 課金が有効になった場合
- Python UDF の実行は BigQuery Services SKU に基づいて課金
- コンテナイメージのビルドにも課金
- 外部ネットワークへのアクセスがある場合、Cloud Networking の Premium Tier egress 料金が発生
リージョン
東京リージョンは対象です。
プレビュー期間中、以下のリージョンを除くすべてのマルチリージョンおよびリージョンでサポートされているとのこと。
- メキシコ(northamerica-south1)
- ストックホルム(europe-north2)
Python UDFでサポートされているランタイムバージョン
PythonUDFは、以下のPython ランタイムバージョンをサポートしています。
ランタイムバージョン | Python バージョン | 事前インストール済みパッケージ | ランタイムベースイメージ |
---|---|---|---|
python-3.11 |
Python 3.11 | - numpy 1.26.3 - pyarrow 14.0.2 - pandas 2.1.4 - python-dateutil 2.8.2 |
google-22-full/python311 |
追加のパッケージが必要な場合は、packages
オプションで PyPI からインストール可能です。
権限
UDF の作成者(オーナー)
ロール名 | 必要な権限 | 備考 |
---|---|---|
roles/bigquery.dataEditor |
bigquery.routines.create 、bigquery.routines.update |
UDF の作成・更新に必要 |
roles/bigquery.jobUser |
bigquery.jobs.create |
CREATE FUNCTION 実行時に必要 |
roles/bigquery.connectionAdmin |
bigquery.connections.create 、bigquery.connections.delegate |
外部サービスに接続する場合のみ必要 |
UDF の利用者(ユーザー)
ロール名 | 必要な権限 | 備考 |
---|---|---|
roles/bigquery.user |
bigquery.jobs.create |
UDF を呼び出すクエリの実行に必要 |
roles/bigquery.dataViewer |
bigquery.routines.get |
他者が作成した UDF を使用する場合に必要 |
roles/bigquery.connectionUser |
bigquery.connections.use |
外部サービスに接続する UDF を使用する場合のみ必要 |
- Google Cloudの他のサービスを使用する場合は、接続先のサービスに対しても別途 IAM 権限が必要です(例:Cloud Translation API を使う場合は
roles/cloudtranslate.user
)
使ってみる
PythonUDFを作ってみる
まずはリファレンスに記載のPythonUDFを作成してみました。
作成時には以下の点を守る必要があります。- Python UDF の本体は、クォートされた文字列リテラルとして記述
- 本体には、
entry_point
で指定する Python 関数が含まれている必要がある runtime_version
オプションで Python の実行環境(python-3.11) を指定する必要があります(現在サポートされているのはpython-3.11
のみ)
CREATE FUNCTION `PROJECT_ID.DATASET_ID`.multiplyInputs(x FLOAT64, y FLOAT64)
RETURNS FLOAT64
LANGUAGE python
OPTIONS(runtime_version="python-3.11", entry_point="multiply")
AS r'''
def multiply(x, y):
return x * y
''';
上記を実行すると、2分程度でデプロイが終わり関数が作成されました。
実行してみます。
WITH numbers AS
(SELECT 1 AS x, 5 as y
UNION ALL
SELECT 2 AS x, 10 as y
UNION ALL
SELECT 3 as x, 15 as y)
SELECT x, y,
`PROJECT_ID.DATASET_ID`.multiplyInputs(x, y) AS product
FROM numbers;
初回実行では結果が出力されるまで3分弱かかりました。(なかなか終わらないのでどきどきしましたが)
The first query after you run the CREATE FUNCTION statement might automatically wait for the image to complete. Without any external dependencies, the container image should typically be created in less than a minute.
CREATE FUNCTION文を実行した後の最初のクエリは、自動的にイメージの完了を待つかもしれません。外部依存関係がなければ、コンテナ・イメージは通常1分以内に作成されるはずです。
このように記載があるので初回はコンテナイメージの作成で時間がかかる様子です。
再度実行してみたところ数秒でクエリが完了しました。
pandasを使用したベクトル化PythonUDFを作ってみる
PythonUDFではpandasを用いたベクトル処理が可能です。以下特徴をサマリしました。
- ベクトル化(Vectorization)を使うと、Python UDFで複数行を一括処理できます。
- バッチ処理の行数は、
max_batching_rows
オプションで上限を制御可能(指定しない場合は自動で決定) - ベクトル化 UDF の引数は
pandas.DataFrame
型で、列名はCREATE FUNCTION
で定義したパラメータ名と一致させる必要があります。 - 戻り値は、
pandas.Series
または 1 列のpandas.DataFrame
で、入力と同じ行数である必要があります。
以下はリファレンス記載のPythonUDF作成文です。まずはPythonUDFを作成します。
CREATE FUNCTION `project_id.datset_id`.multiplyVectorized(x FLOAT64, y FLOAT64)
RETURNS FLOAT64
LANGUAGE python
OPTIONS(runtime_version="python-3.11", entry_point="vectorized_multiply")
AS r'''
import pandas as pd
def vectorized_multiply(df: pd.DataFrame):
return df['x'] * df['y']
''';
実行してみます。
WITH numbers AS
(SELECT 1 AS x, 5 as y
UNION ALL
SELECT 2 AS x, 10 as y
UNION ALL
SELECT 3 as x, 15 as y)
SELECT x, y,
`project_id.datset_id`.multiplyVectorized(x, y) AS product
FROM numbers;
デプロイ時間、実行時間ともに先ほど変わりありませんでした。
バッチ処理の行数を、max_batching_rows
で制御するという点はリモート関数と同様の仕組みを用いている香りがしますね。
以下のブログ記事のような注意点が同様にありそうです。
データ型のマッピング
BigQuery、Pandas、Python、PyArrowで各データ型がそれぞれ若干異なりますのでの型のマッピングがあります。
マッピング表は以下を参照ください。
PyPIからパッケージをインポートしてみる
ダミーデータを作成できるFaker
をインポートして使ってみました。
CREATE FUNCTION `project_id.datset_id`.printFaker()
RETURNS STRING
LANGUAGE python
OPTIONS(runtime_version="python-3.11", entry_point="printFaker", packages=["faker"])
AS r'''
from faker import Faker
def printFaker():
return (Faker().name())
''';
Fakerはプリインストールされていないため、PyPIからインストールする必要があるためpackages=["faker"]
をOPTIONS
に設定します。
こちらで問題なく作成できました。実行してみます。
SELECT `project_id.datset_id`.printFaker()
ダミーデータが出力されました。PyPIからパッケージがインストールできるのは嬉しいですね!
Google Cloudのリソースと接続してみる
Cloudリソース接続を用いてPythonUDFはCloud StorageなどGoogle Cloudの他のサービスへ接続ができます。
所感
リモート関数に比べ、Cloud Runのリソース作成や管理の手間がなくなるためお手軽だなと感じました。一方で、Cloud Runは同時実行インスタンスを増やすことでスケーリングを図ることができ、強大なパワーがあります。PythonUDFのマネージド環境で、どの程度パフォーマンスを設定できるのか・発揮できできるのかが気になりました。
手軽にさくっとpythonで書いて処理したい、というときにはとっても良いので今後活用シーンはたくさんあるのではないかと思います。
それでは。
参考