[プレビュー]BigQueryでpythonでUDFが作成可能になりました

[プレビュー]BigQueryでpythonでUDFが作成可能になりました

Clock Icon2025.04.03

2025/4/2のアップデートPython user-defined functions(以下Python UDF)がプレビューとなりました。
https://cloud.google.com/bigquery/docs/user-defined-functions-python

気になった機能なので早速触れてみました。

概要

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.createbigquery.routines.update UDF の作成・更新に必要
roles/bigquery.jobUser bigquery.jobs.create CREATE FUNCTION 実行時に必要
roles/bigquery.connectionAdmin bigquery.connections.createbigquery.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を作成してみました。
https://cloud.google.com/bigquery/docs/user-defined-functions-python#create-python-udf
作成時には以下の点を守る必要があります。

  • 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分程度でデプロイが終わり関数が作成されました。
スクリーンショット 2025-04-03 9.24.26.png
スクリーンショット 2025-04-03 8.41.16.png

実行してみます。

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;

スクリーンショット 2025-04-03 8.45.56.png
初回実行では結果が出力されるまで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分以内に作成されるはずです。

このように記載があるので初回はコンテナイメージの作成で時間がかかる様子です。
再度実行してみたところ数秒でクエリが完了しました。
スクリーンショット 2025-04-03 8.46.09.png

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;

デプロイ時間、実行時間ともに先ほど変わりありませんでした。
スクリーンショット 2025-04-03 8.49.12.png

バッチ処理の行数を、max_batching_rowsで制御するという点はリモート関数と同様の仕組みを用いている香りがしますね。
以下のブログ記事のような注意点が同様にありそうです。
https://dev.classmethod.jp/articles/gcp-bigquery-remote-functions-caution/

データ型のマッピング

BigQuery、Pandas、Python、PyArrowで各データ型がそれぞれ若干異なりますのでの型のマッピングがあります。
マッピング表は以下を参照ください。
https://cloud.google.com/bigquery/docs/user-defined-functions-python#supported-python-data-types

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()

スクリーンショット 2025-04-03 9.13.44.png
ダミーデータが出力されました。PyPIからパッケージがインストールできるのは嬉しいですね!

Google Cloudのリソースと接続してみる

Cloudリソース接続を用いてPythonUDFはCloud StorageなどGoogle Cloudの他のサービスへ接続ができます。
https://cloud.google.com/bigquery/docs/user-defined-functions-python#use-online-service

所感

リモート関数に比べ、Cloud Runのリソース作成や管理の手間がなくなるためお手軽だなと感じました。一方で、Cloud Runは同時実行インスタンスを増やすことでスケーリングを図ることができ、強大なパワーがあります。PythonUDFのマネージド環境で、どの程度パフォーマンスを設定できるのか・発揮できできるのかが気になりました。
手軽にさくっとpythonで書いて処理したい、というときにはとっても良いので今後活用シーンはたくさんあるのではないかと思います。

それでは。

参考

https://cloud.google.com/bigquery/docs/user-defined-functions-python

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.