Databricksのノートブックは、一つのノートブックの中にPython、SQL、シェルなどを混在させることができます。大規模開発であればジョブ化したり、言語ごとのノートブックを作成して連携させることも可能ですが、それほど規模が大きくなく、かつ、ある箇所はSQL、別の箇所ではPythonで処理をしたいといったケースでは便利な機能です。
そして、複数言語が混在しているノートブックがある際に必要になってくるのが変数の共有です。Pythonの処理結果をSQLやシェルから参照したい場合にどうしたらいいのでしょうか。過去に以下の記事を書いていますが、今回はPythonからSQLの変数へのアクセスやシェルも含めてウォークスルーしていきます。
なお、こちらの記事ではRなど他の言語との変数の共有方法についてもカバーされています。
この記事で言いたいことを図にまとめると以下のようになります。
SQLからPython変数へのアクセス
Sparkパラメーターによるアクセス
いくつかアプローチがありますが、推奨はspark.conf.set
を用いたSparkパラメーターによるものとなります。
# Pythonセル
import datetime
x = str(datetime.date.today())
x
'2024-12-23'
spark.conf.set('v.max_date', x)
%sql
-- SQLセル
SELECT "${v.max_date}" AS max_date
SQL変数経由でのアクセス
SQL変数経由でも引き渡し可能です。
# Pythonセル
my_var = 5
spark.sql("DECLARE OR REPLACE my_var INT")
spark.sql(f"SET VAR my_var = {my_var}")
%sql
-- SQLセル
SELECT my_var
ウィジェット経由でのアクセス
ウィジェット経由でもアクセスできますが、こちらの制限事項にあるように、RUN ALLした際やジョブにした際、期待した通りに動作しないリスクがあります。インタラクティブに実行する際にはこちらでも問題ありません。
dbutils.widgets.text("max_date", x)
%sql
-- SQLセル
select getArgument("max_date") AS max_date -- SQLのgetArgument関数は廃止され、DBR 17.0以降では削除されます。
select "${max_date}" AS max_date -- パラメーターマーカーへの移行が推奨されています
select :max_date AS max_date -- パラメーターマーカーによるウィジェット変数へのアクセス
SQLからPythonのSparkデータフレームへのアクセス
これは一時ビューを使うのが簡単です。
# Pythonセル
df = spark.createDataFrame([(1, 'Alice'), (2, 'Bob')], ['id', 'name'])
df.createOrReplaceTempView("people")
%sql
-- SQLセル
SELECT * FROM people
PythonからSQLへのアクセス
ノートブックでSQLを実行すると、その結果は暗黙的に作成されるデータフレーム_sqldf
に格納されますので、そちらにアクセスします。
%sql
SELECT
count(*) as count
FROM
users.takaaki_yayoi.covid_cases;
# Pythonセル
pdf = _sqldf.toPandas()
pdf["count"][0]
23171
シェルからPython変数へのアクセス
Databricksノートブックでは、%sh
マジックコマンドで、クラスターのドライバーノード上のシェルにアクセスすることができます。こちらで説明されているように、環境変数経由でシェルからPythonの変数にアクセスできます。
# Pythonセル
import os
l =['A','B','C','D']
os.environ['LIST']=' '.join(l)
print(os.getenv('LIST'))
%sh
# シェルのセル
for i in $LIST
do
echo $i
done
A
B
C
D
Pythonからシェル変数へのアクセス
逆方向での直接的な方法はサポートされていないので、こちらにあるようにファイル経由となります。ドライバーノードのローカルストレージに変数を書き込んだファイルを出力します。
%sh
# シェルのセル
echo "my_value" >/tmp/foo
# Pythonセル
from pathlib import Path
my_var = Path("/tmp/foo").read_text()
print(my_var)
my_value