最近コンテキストとメジャーをやっと理解できてきた気がする…。それでもまだもやもやしているのは、DAX式中にコンテキストが明示的に現れてこないからだ。コンテキストとメジャーの関係をつまびらかにすれば、きっと自分の理解が進むし、初心者のためにもなるはずだ!
そんなふうに考えていた時期が俺にもありました。いや…こんな読む人を選ぶ記事になるなんて思っていなかったです。何というか、すみません。
コンテキスト
$\mathbb{R}^N$の部分集合$T$を列数$N$のテーブルと考える。テーブルの行$t \in T$を
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$の部分集合を
{c^{[n]}}^{-1}(1):=\{t\in T \ | \ \ c^{[n]}(t[n])=1\}
とする。これの意味は$c^{[n]}$で定義された抽出条件を満たす$T$のサブセット。
$c^{[1]},\cdots ,c^{[N]}$を元に、$T$から$\{0, 1\}$への写像$c$を次のように作る:
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$の集合を
T \underset{rel}{\times} S := \{(t, s_i) \in T \times S \ | \quad t \in T, \ i={\rm ix}(t[n_S]) \}
と書く。
フィルター関数$c$も拡張し、
c:=c^{[1]}_T \cdots c^{[N]}_T \cdot c^{[1]}_S \cdots c^{[M]}_S
とする。けど、めんどくさいし、一般性は損なわれないので、以降$S$は明示的には表さない。
メジャー
イテレーター関数(SUMX関数など名前がXで終わる関数)ではない集約関数(SUM関数など)はイテレーター関数の特別な場合とみなせるため、以下、イテレーター関数を前提にメジャーを説明する。
${\rm expr}$を${\mathbb R^N}$から${\mathbb R}$への写像とする。テーブル$T$の各行$t$から実数を計算する計算式という意味。また、${\mathbb R}$の部分集合全体から${\mathbb R}$への写像${\rm agg}$を集約関数(ドキュメントでは「集計」であるが、足す以外のケースもあるため「集約」とする)と呼ぶ。具体的にはSUMX関数やCOUNTROWS関数など。
計算式${\rm expr}$でテーブル$T$の部分集合から実数の集合を得て、${\rm agg}$でその実数の集合から何らかの実数を計算する。これにコンテキストを組み合わせればメジャーになる。
メジャー$m$を、集約関数${\rm agg}$全体×計算式${\rm expr}$全体×フィルター関数全体$C$から${\mathbb R}$への写像として以下のように定義する:
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式で定義されたメジャーを解釈しよう:
= SUM(T[k])
ピボットテーブルを作成すれば、フィルター関数$c$は自然に生成される。つまり、$n$列を行見出しに使用して、その値が$d$の場合、
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}$はそれぞれ以下であるから、
\begin{eqnarray}
{\rm expr}(t) &=& t[k]\\
{\rm agg}(U) &=& \sum_{u \in U} u
\end{eqnarray}
フィルター関数が上記の$c^{[n]}$だけの場合、
\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関数を使えば、自然に生成されたフィルター関数$c$を元に、新しいフィルター関数を自分で作成することができる。
フィルター関数からフィルター関数($\{0, 1\}^T$から$\{0, 1\}^T$)への写像$f^{[n]}$を$N$個集めた、$C$から$C$への写像$f$を次で定義する:
f(c):=f^{[1]}(c^{[1]})\cdot f^{[2]}(c^{[2]})\cdots f^{[N]}(c^{[N]})
CALCURATE関数の第2引数以降はこの$f$を定義している。$f^{[n]}$を作るのが条件式やタイムインテリジェンス関数など。
具体例: PREVIOUSDAY関数
タイムインテリジェンス関数のPREVIOUSDAY関数で説明してみる。$c^{[n]}$を特定の日付$d$かどうかを判定するフィルター関数、すなわち、
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}
とする。
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関数を説明