特徴量エンジンKaskadaの宣言型クエリ言語 Fenl 〜離散式と連続式

# はじめに

先日、以下の記事を発表しました。

https://qiita.com/yoshiyuki_kono/items/7dc7bdad5e02ff986f5d

新しいテクノロジーに触れる時はいつでも楽しいものです。
中でも新しいプログラミング言語（のパラダイム）を学ぶことは、特別に楽しいことです。

ということで、まだまだ勉強中ですが、公式ドキュメントの学習過程の記録として、以下の記事をまとめてみました。

本稿情報のソースとして、下記ドキュメントを参照ください。


https://kaskada.io/docs-site/kaskada/main/fenl/continuity.html

# Fenl式の二つのタイプ
Kaskadaで扱うデータは、時間の流れを含んでいます。

統計学を学ばれている方にとって、馴染み深い概念として、質的変数と量的変数というカテゴリーがあり、さらに変数が量的変数の場合、、離散型変数(discrete variable)と、連続型変数(continuous variable)に分類することができます。
離散型変数とは、サイコロの目のように、「１」や「２」の値を取るが、「１.５」というような値は決して取らない変数です。連続型変数とは、温度や、身長のように、小数点以下の桁のように、連続した数値の範囲を持つものです。

ここでは、「連続」と「離散」という概念のイメージを持ってもらうために、統計学の概念から始めました。

KadkadaのFenl 式は**離散式**または**連続式**のいずれかです。

急いで付け加えると、上記の統計学状の連続/離散変数の例や、数値のイメージを直接対応することはできない、ことに気をつける必要があります。

それというのも、ここで扱われているのは「式」であって、「変数」ではないからです。

とはいえ、「連続」と「離散」という概念の対については、類比して考えることができます。

Fenl式の**離散式**と**連続式**について、以下のように整理できます。

 * 離散式は、特定の時点でのみ意味を持つ（特定の時点にのみ関係している）値を記述します。
 * 連続式は、時間の範囲にわたって意味を持つ（時間のある特定の範囲に対する）値を記述します。

あえていえば、離散式の値は、連続型変数であることができます。

## 離散式
離散式は有限の複数の時間の集合で値を生成します。それ以外の時間ではその値は`null`になります。

テーブルには有限のイベントのセットが含まれており、各イベントは特定の時刻に関連付けられています。

テーブルには、イベントが存在する一連の時間における意味のある値のみが含まれます。
フィールド参照などの単純な操作を不連続な式に適用すると、不連続な結果が生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/7d8d4711-9060-71d1-0305-082ec2e25880.png)


離散式は、（上の図のように複数の異なる変数の値を）加算するなど、さまざまな方法で組み合わせることができます。

ほとんどの場合、操作は入力式の 1 つが値を生成するたびに出力値を生成します。

この例では、式 `Purchase.amount` が `Review.stars` とは異なる時点で定義されており、一方の数値が `null`の場合に加算が行われると `null` が生成されるため、生成される値の多くが `null` となることがわかります。



私見ですが、この例のユースケースでは、nullではなく、片方の値のみが結果として生成されてほしい気がしますが（そもそも、購入量と、レビューのスター数を単純に足すことに無理があると思いますが）

## 連続式
連続式は、あらゆる時点で意味のある値を持ちます。連続式は、その値に寄与するたびに値を生成します。通常、集計では連続値が生成されます。

たとえば、任意の時点で、これまでに確認されたすべての値の合計を記述することができます 。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/866dedd3-249c-3329-0e88-6d5138e123ce.png)


連続式は常に値 を「持ち」ますが、特定の時点で値を「生成」します（値が変化すると捉えた方がわかりやすいかもしれません）。そのような時点を上の図では丸で示しています。連続式は、更新のストリームを生成すると考えることができます。ストリームは常に最新の更新の値を取ります

連続値が集計される場合、値が生成されると集計が更新されます。たとえば、`count`中間集計がある場合とない場合の離散式では同じ結果が生成されます。

```
Purchase.amount | count() == Purchase.amount | sum() | count()
```
### 設計についてのノート

値を「持つ」ことと値を「生成する」ことの区別により、連続式と離散式の両方に同じ方法で集計を適用できます。連続値に合計などの演算を適用することは、 (数学的な意味で) 値を積分することに相当します。

連続値の集計を積分として解釈するのは、特に、`count`のような操作の場合、直感的にはそぐわないと感じられるかもしれません（求めたい結果に合わせて利用することが重要になります）。

## 連続式と離散式の相互作用

複数の式を含む演算の結果の連続性は、演算の入力の連続性に依存します。
時間 A と B のセットで定義された離散式と連続式について、それぞれの任意のペア間の二項演算の結果を次の表に示します。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/24aec530-ddb9-5cbe-f1d1-2508d85a981c.png)


離散式を連続式と組み合わせると、離散式に存在する各時点の値が、連続式がその時点で持つ値と結合されて結果が生成されます。(「一方の数値が nullの場合に加算が行われると null が生成される」という先に見た動作とは異なった特徴です)

これを指して、離散性は「貪欲(greedy)」であると言われもします。

これについては、言葉で説明するよりも図を見て理解するのが確実でしょう。
式の結果に値を含めるためには、離散式に対して、連続式を足すことで実現できることがわかります。

この例が実際のユースケースに対して、意味を持つとは、個人的には思えませんが、技術的には理解できるかと思います。

## 値のフィルタリング

前述したように、式間のほとんどの演算は、入力式が値を生成するときに値を生成します 。これにより、望ましくないタイミングで値が生成される可能性があります。そのような状況を回避するために、`when`のような述語式の操作により、式が値を生成する時間をフィルタリングすることができます。これにより、述語式が`true`の値を取る時にのみ式の値を生成することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/f830872f-c91e-6832-b426-0470edc98dfb.png)

# 関連情報

https://ai-trend.jp/basic-study/basic/qualitative-variable-and-quantitative-variable/


https://ai-trend.jp/basic-study/basic/discrete-continuous-variable/