Posted at

U-SQL でヒストグラムを作りたい時のラムダ式

More than 1 year has passed since last update.

U-SQLでヒストグラムを作ろうとする場合、データポイントをBINごとに仕分けて、集計ということになります。

これは直接サポートされていないので、色々ステップを踏む必要がありました。

BINに仕分ける部分は、U-SQLで書けなくは無いようにも思うのですが、SWITCHとか使うのは嫌ですし、使うたびにコードを埋め込まないといけないので、クエリーが醜くなりそう。


ラムダ式をU-SQL変数に(名前付きラムダ)

今年出た機能のうち、U-SQL Spring 2018 Release Notes に書いてある、以下を使うと、C#関数を、変数に入れてインラインで手軽に使えるようになります。(コードビハインドやアセンブリ登録がいらない!!)

U-SQL adds C# Func<>-typed variables in DECLARE statements (named lambdas)

TVFやPROCEDUREに比べて、引数も自由だし、単純なROW-wideな関数を定義するには楽です。本当にインラインにしちゃうと、再利用できないし。

DECLARE @cbin = new SQL.ARRAY<double>{0,1000,20000};

DECLARE @binning Func<double,SqlArray<double>,double> =
(n,bin) => {
if(n < bin[0]) return double.NegativeInfinity;
if(n >= bin[2]) return double.PositiveInfinity;
return ((Math.Floor((n - bin[0]) / bin[1]) + 1) * bin[1]);

@hoge2 =
SELECT @binning(value, @bin) AS newValue
FROM @a;

release note を読むと、以下のようにパッケージに登録して再利用も容易になるようです。

 DROP PACKAGE IF EXISTS MyFunctions;

CREATE PACKAGE MyFunctions () AS
BEGIN
EXPORT @EnumerateToFloor Func<int,double,IEnumerable<int>> =
(x, y) => Enumerable.Range(x, (int) Math.Floor(y));

EXPORT @TryParseDateTime Func<string, DateTime?> =
(d) => {
DateTime dt;
var b = DateTime.TryParse(d, out dt);
return b ? (DateTime?) dt : (DateTime?) null;
};
END;

さらに、ラムダ同士で呼び合ったりすることも可能。

U-SQLでは、関数のようにパラメータ化された処理を使うことが難しかったのですが、これでインラインのROW-WIDEな処理は少なくともすっきりかけるようになりますね。