コンテキストとメジャーとCALCULATE関数を数学的に説明してみる

最近コンテキストとメジャーをやっと理解できてきた気がする…。それでもまだもやもやしているのは、DAX式中にコンテキストが明示的に現れてこないからだ。コンテキストとメジャーの関係をつまびらかにすれば、きっと自分の理解が進むし、初心者のためにもなるはずだ！

そんなふうに考えていた時期が俺にもありました。いや…こんな読む人を選ぶ記事になるなんて思っていなかったです。何というか、すみません。

# コンテキスト
$\mathbb{R}^N$の部分集合$T$を列数$N$のテーブルと考える。テーブルの行$t \in T$を

```math
t=(t[1], t[2], \cdots, t[N]) \quad {\rm where} \quad t[n] \in \mathbb{R}, \quad n=1,\cdots,N
```
と表す。また、テーブル$T$の$n$列を$T[n]$と表す。

$c^{[n]}$を$\mathbb{R}$から$\\{0, 1\\}$への写像とする。意味的には、テーブル$T$の$n$列が抽出条件を満たせば1、満たさなければ0を返す関数。$c^{[n]}$の逆像から得られる$T$の部分集合を

```math
{c^{[n]}}^{-1}(1):=\{t\in T \ | \ \ c^{[n]}(t[n])=1\}
```
とする。これの意味は$c^{[n]}$で定義された抽出条件を満たす$T$のサブセット。

$c^{[1]},\cdots ,c^{[N]}$を元に、$T$から$\\{0, 1\\}$への写像$c$を次のように作る:

```math
c(t):=c^{[1]}(t[1]) \cdot c^{[2]}(t[2])\cdots c^{[N]}(t[N])
```
このとき、$c$をフィルター関数（FILTER関数とは無関係）、$c^{-1}(1)=\\{t \in T \ | \ \ c(t)=1\\}$をコンテキストと呼ぶ。また、フィルター関数$c$全体を$C$と表す。$C \subset \\{0,1\\}^T$となっている。

# リレーションシップ
列数$M$のテーブル$S \subset \mathbb{R}^M$を$T$のディメンションテーブルとする。ある$n_S$で任意の$t \in T$に対してただ1つの$s \in S$が存在し、$t[n_S]=s$となるとき、$T$から$S$への多対1のリレーションシップが存在するという。

$S = \\{s_1, s_2, \cdots \\}$とし、$t[n_S]=s_i$となる$i$を$i:={\rm ix}(t[n_S])$と書く。

リレーションを考慮した$T$と$S$の集合を

```math
T \underset{rel}{\times} S := \{(t, s_i) \in T \times S \ | \quad t \in T, \ i={\rm ix}(t[n_S]) \}
```
と書く。

フィルター関数$c$も拡張し、

```math
c:=c^{[1]}_T \cdots c^{[N]}_T \cdot c^{[1]}_S \cdots c^{[M]}_S
```
とする。けど、めんどくさいし、一般性は損なわれないので、以降$S$は明示的には表さない。

# メジャー
イテレーター関数（[SUMX関数](https://docs.microsoft.com/ja-jp/dax/sumx-function-dax)など名前がXで終わる関数）ではない集約関数（[SUM関数](https://docs.microsoft.com/ja-jp/dax/sum-function-dax)など）はイテレーター関数の特別な場合とみなせるため、以下、イテレーター関数を前提にメジャーを説明する。

${\rm expr}$を${\mathbb R^N}$から${\mathbb R}$への写像とする。テーブル$T$の各行$t$から実数を計算する計算式という意味。また、${\mathbb R}$の部分集合全体から${\mathbb R}$への写像${\rm agg}$を[集約関数](https://docs.microsoft.com/ja-jp/dax/aggregation-functions-dax)（ドキュメントでは「集計」であるが、足す以外のケースもあるため「集約」とする）と呼ぶ。具体的にはSUMX関数や[COUNTROWS関数](https://docs.microsoft.com/ja-jp/dax/countrows-function-dax)など。

計算式${\rm expr}$でテーブル$T$の部分集合から実数の集合を得て、${\rm agg}$でその実数の集合から何らかの実数を計算する。これにコンテキストを組み合わせればメジャーになる。

メジャー$m$を、集約関数${\rm agg}$全体×計算式${\rm expr}$全体×フィルター関数全体$C$から${\mathbb R}$への写像として以下のように定義する:

```math
m:集約関数{\rm agg}全体 \times 計算式{\rm expr}全体 \times C \rightarrow {\mathbb R} \\
m({\rm agg}, {\rm expr}, c):={\rm agg}(\{{\rm expr}(t) \in {\mathbb R} \ | \ \ t \in c^{-1}(1)\})
```

メジャーはあらかじめ定義された集約関数と計算式に対して、フィルター関数を当てれば値を返す関数という意味。実はフィルター関数を引数としていることがDAX式を見ただけではわからない。

## 具体例: SUM関数
以下のDAX式で定義されたメジャーを解釈しよう:

```DAX
= SUM(T[k])
```
ピボットテーブルを作成すれば、フィルター関数$c$は自然に生成される。つまり、$n$列を行見出しに使用して、その値が$d$の場合、

```math
c^{[n]}(t[n]):=
\begin{cases}
1 & \quad {\rm if} \quad t[n]=d\\
0 & \quad {\rm if} \quad t[n] \neq d
\end{cases}
```
が生成されている。列見出しやスライサー、レポート フィルターについても同様。他の列、つまり、見出し等に使われない列は常に1を返すフィルター関数が生成されていると考えればよい。

SUM関数は自然に生成された$c$によるフィルターコンテキスト上の$t[k]$の和となる。${\rm expr}$、${\rm agg}$はそれぞれ以下であるから、

```math
\begin{eqnarray}
    {\rm expr}(t) &=& t[k]\\
    {\rm agg}(U)  &=& \sum_{u \in U} u
\end{eqnarray}
```

フィルター関数が上記の$c^{[n]}$だけの場合、

```math
\begin{eqnarray}
    m({\rm agg}, {\rm expr}, c) &=& {\rm agg}(\{t[k] \ | \ \ t \in T \ {\rm where}\  t[n]=d\}) \\
        &=& \sum_{\{t \in T \ | \ t[n]=d\}} t[k]
\end{eqnarray}
```
となっている。


# CALCURATE関数でやっていること
[CALCURATE関数](https://docs.microsoft.com/ja-jp/dax/calculate-function-dax)を使えば、自然に生成されたフィルター関数$c$を元に、新しいフィルター関数を自分で作成することができる。

フィルター関数からフィルター関数（$\\{0, 1\\}^T$から$\\{0, 1\\}^T$）への写像$f^{[n]}$を$N$個集めた、$C$から$C$への写像$f$を次で定義する: 

```math
f(c):=f^{[1]}(c^{[1]})\cdot f^{[2]}(c^{[2]})\cdots f^{[N]}(c^{[N]})
```
CALCURATE関数の第2引数以降はこの$f$を定義している。$f^{[n]}$を作るのが条件式や[タイムインテリジェンス関数](https://docs.microsoft.com/ja-jp/dax/time-intelligence-functions-dax)など。

## 具体例: PREVIOUSDAY関数
タイムインテリジェンス関数の[PREVIOUSDAY関数](https://docs.microsoft.com/ja-jp/dax/previousday-function-dax)で説明してみる。$c^{[n]}$を特定の日付$d$かどうかを判定するフィルター関数、すなわち、

```math
c^{[n]}(t[n]):=
\begin{cases}
1 & \quad {\rm if} \quad t[n]=d\\
0 & \quad {\rm if} \quad t[n] \neq d
\end{cases}
```

とする。

```math
f^{[n]}(c^{[n]})(t[n]):=
\begin{cases}
1 & \quad {\rm if} \quad t[n]=d-1\\
0 & \quad {\rm if} \quad t[n] \neq d-1
\end{cases}
```

とすれば、$f^{[n]}(c^{[n]})$は特定の日付$d-1$かどうかを判定するフィルター関数となる。$d={c^{[n]}}^{-1}(1)$として、明示的に$c^{[n]}$のみで記述しても良い（趣味の問題か）。この$f^{[n]}$がPREVIOUSDAY関数である。

# 感想
個人的にはスッキリした気がする。行コンテキストはそのうち追記するかも。ここまで書いたら、DAXをPythonで実装できそうな気がしてきた。もちろんやらないけど。

# TODO
以下やり残し

- 「写像」の意味を脚注で追加（わかりやすく）
- RANKX関数を説明