表計算。それはTableauの超強力なツールです。
累計や前年比などを簡単に実行出来たり、表計算によって作成が可能なチャートは数々あります。表計算の理解により、Tableauの表現力は格段に向上します。
一方で、感覚的に表計算の動作は掴めていたものの、その深淵や動作について理解はできていませんでした。雰囲気理解というやつですね。
インターネットで動作原理を解説した資料を探しても、本当の理解にはたどり着けませんでした(そもそも簡易表計算の記事が多い印象)。
今回の記事は、表計算の真理に辿り着こうという試みです。
データはおなじみSample SuperStoreを使用しました。
今回の記事に使用したWorkbookは以下からダウンロードできます。
まずはViz-LOD
表計算に入る前に、Vizの詳細レベル「Viz-LOD」について解説します。
まずは、以下のVizを見てください。
シンプルですね。利益と売上の散布図です。
次に、マークにYEAR(オーダー日)を入れました。
Viz-LODの真髄は「いま表示されているVizにおいて、データは何で分けられているか」です。
1枚目では、データは全く分けられておらず
全データを持ってきて
全データの利益と売上と合計を集計した
という形になっていました。
2枚目では、データはYEAR(オーダー日)で分けられ
2015年、2016年、2017年、2018年それぞれでデータを分けて
それぞれの分けられたデータにおける、利益と売上の合計を集計した
という形になっています。
ここでのViz-LODはYEAR(オーダー日)です。
この散布図では「マーク」内のディメンションによって、データが分けられていることを理解してください。
ちなみに、ディメンションを増やすと以下のようになります。
(見慣れた散布図だと思います。一方で「この散布図のViz-LODは何か」を意識的に考えてみて下さい)
3枚目のViz-LODは「YEAR(オーダー日)」×「顧客区分」、
4枚目のViz-LODは「YEAR(オーダー日)」×「顧客区分」×「カテゴリ」です。
ちょっと複雑になってきたので、少し言い換えると、以下のようになります。
3枚目の散布図は「YEAR(オーダー日)、顧客区分ごとでデータが分けられている。そして、それぞれの売上と利益の合計を計算している」
4枚目の散布図は「YEAR(オーダー日)、顧客区分、カテゴリごとでデータが分けられている。そして、それぞれの売上と利益の合計を計算している」
それぞれの散布図で「データはどう分けられているのか」「どのディメンションごとのデータになっているのか」を意識してください。
INDEX関数で理解する表計算
ここから先、TableauのVizにおいて「データはどう分けられているか」を意識しながら読み進めて下さい。
これが今回の要点です。
先にお断りしておきます。
今回の記事では、終始「特定のディメンション(Specific Dimensions)」を使用します。
表(横)やペイン(横から下)のような、Tableauが用意してくれたオプションは一切使用しません。
「特定のディメンション」で問題なく設定できるようになること
「特定のディメンション」の順番を変えたときの、表計算の動作が理解できること
この2点が今回のゴールです。
さて、表計算関数は様々なものがありますが、今回は「表計算はどのように動作しているか」がテーマのため、INDEX関数を主に使用します。
INDEX関数は文字通りインデックスを返すものですが、要は「順番に番号を与えていく関数」です。
「順番」って、どのように与えられているのでしょうか。
シンプルな例から見ていきます。
上のVizでは、データは「YEAR(オーダー日)」「カテゴリ」ごとに分けられています。
言い換えれば、Viz-LODが「YEAR(オーダー日)」×「カテゴリ」です。
ここで、Specific Dimensionsは「Year of オーダー日」が上、「カテゴリ」が下になっています。
次の場合はどうでしょうか。
Specific Dimensionsは「カテゴリ」が上、「Year of オーダー日」が下になりました。
ここで、INDEXの値が大きく変わりました。
表計算の設定はそのままに、Rowsシェルフ(行シェルフ)の順番を変更します。
すると、きれいにINDEXが並びました。
さて、何が起きているのか。
この秘密を、Viz-LODの考えと一緒に解き明かしていきます。
表計算の本質
以下の例を使用します。
ここでのViz-LODは「YEAR(オーダー日)」×「カテゴリ」でした。
ところで、Viz-LOD自体はデータの粒度に関するものなので、ディメンションの「順番」は関係ありません。
一方で、INDEX関数は「順番」を与えます。その順番は「Specific Dimensions」欄で制御されます。
どういうことかというと
行シェルフや列シェルフでのディメンションの並びを完全無視して
Viz-LODにしたがってデータを分割した上で
Specific Dimensionsのディメンションの並びに従って、分割データを並び替えてINDEXを振る
という動作が行われています。
例えば上の場合、INDEX自体は以下のように計算されています。
そして、この計算されたINDEXが、最終的にTableauで以下のような形でTableauに表示されます。
表計算の「表」は、Tableau上で「見えている」または「作られた」表と、一切関係ないんじゃないかなと。
英語だとTable Calculationですが、これは計算を作る上での(中間)Tableを意味しているんじゃないかなと思います。
この辺りはTableau社員さんに聞いてみたいところです。
というわけで、今回の大きなポイントとして、表計算を作る際は
Viz-LOD
Specific Dimensionsの順序
上記二つによって、Vizの裏で作られているテーブルの形
に思いを馳せて頂ければいいんじゃないかなと思います。
例1:すべてのディメンションを使わない場合
こういう場合はどうでしょうか。
まず、起きている事象として「表の中で、カテゴリに対応してINDEXが振られている」状態です。
これも先ほど同じように、裏側のテーブルを想像すると、以下のようになっています。
INDEX自体は「カテゴリ」のみを使用して計算されています。
しかし、Viz-LODに「Year of オーダー日」を含むので、実際には「Year of オーダー日」と「カテゴリ」それぞれの組み合わせを加味しつつ、INDEXは「カテゴリ」に対して計算されています。
例2:Restating everyを使用した場合
例1と同じ結果を返している一方、計算のコンセプトが異なっている例です。
ここで重要なのは「Restarting every」の設定です。
これは「表計算を、指定されたディメンションで最初からやり直す」設定をします。
どういうことかというと、下図のようになっています。
「Year of オーダー日」の各値でINDEXが1から再スタートしていることに注目してください。
先程との大きな違いとして「表計算に『Year of オーダー日』を使用する」ことを指定しています。
したがって、出てくる結果が同じでも、コンセプトが異なります。
例1では表計算は「カテゴリ」で行われていた一方、Viz-LODに「Year of オーダー日」が含まれているので、結果的に「Year of オーダー日」ごとにINDEXが計算されていました。
例2では、明示的に表計算に「Year of オーダー日」を使用しており、また「Year of オーダー日」で表計算をリスタートさせています。
繰り返しになりますが、結果が同じでも、根柢のコンセプトが異なります。
それぞれの場合において
どのようなコンセプトで表計算が組み立てられているか
Viz-LODも考慮して、どのような表計算が最終的に行われるのか
この2点を意識的に考えてください。
例3:行シェルフと列シェルフを両方使う
クロス集計表での表計算がどうなっているか、という話です。
正直ここまでの話で、全て完結しています。
Viz-LODに思いを馳せて、見えているものの裏側で作られている表に思いを馳せれば、要はその表の数値をそれぞれの固まりにアサインしているだけです。
ここまで理解して、やっと表(横)オプションが理解できる
繰り返しますが、すべてはViz-LODとSpecific Dimensionsです。
追加でRestarting everyオプションで挙動をさらに制御できます。
上記3点によって、Tableauがどのようなテーブルを裏で作っているのか、それに思いを馳せてください。本当にそれだけです。
ところで、表計算って以下のような項目がありますよね。
このTable (across)等々は、一応公式にヘルプがあります。
ですが、本質はViz-LOD、Specific Dimensions、Restarting everyです。この3つによる表計算の理解をもって初めて、以下のオンラインヘルプが分かってくるのかなと。
逆に、表計算が本質的に何をしているのかの資料があまり見当たらず…
英語資料ではPartitioningとAddressingという言葉を用いて説明しているものが多い印象ですが、自分は裏で作られるテーブルを考えて、対応するViz-LODの組み合わせで紐づける、という理解が一番しっくり来ました。
RUNNING_SUMを見てみる
上記までのINDEXを用いた議論から、代表的な表計算を見ていきます。
まずは累計から。おなじみRUNNING_SUMです。
以降、INDEXとRUNNING_SUMは同様のSpecific DimensionとRestarting everyの設定がされています。
INDEXに注目してください。
先ほどまでの議論と同様で、この「順番」に沿ってRUNNING_SUMを計算しています。
INDEXでの議論と同じように、Specific Dimensionsを入れ替えてみます。
INDEXに注目してください。
Viz上では「Year of オーダー日」→「カテゴリ」となっていますが、
表計算では「カテゴリ」→「Year of オーダー日」で計算されていて、それを対応するViz-LODの組み合わせにアサインしているだけです。
最後に、カテゴリだけ使用した場合です。
【例1】の話を復習してください。
表計算の行われている「順番」は「カテゴリ」に沿って振られていますが、Viz-LODに「オーダー日」が含まれているので、各「Year of オーダー日」でRUNNING_SUMが独立して実行されます。
言い換えれば、Viz-LODまたはRestarting everyで「表計算における独立した固まりを作っている」と言えるかもしれません。
この「固まり」について、更に深めていこうと思います。
WINDOW_SUMを見てみる
もう一つ、WINDOW_SUM関数を見てみましょう。
WINDOW_SUMなどのWINDOW系(と便宜上呼ぶことにします)は「固まり」の理解が大切になってきます。
まず、上図では全てのディメンションが表計算に使われています。
したがって表計算を計算する「裏側のテーブル」において、独立した「固まり」がありません。
したがって、上図でのWINDOW_SUMの値は
各「Year of オーダー日」×「カテゴリ」でのSUM(売上)すべてを合計した値
になっています。
そして、INDEXがリスタートするように設定すると、どうなるか。
以下を見てください。Year of オーダー日でリスタートするように設定しました。
WINDOW_SUMが返しているのは、リスタートするまでのSUM(売上)の合計値です。
「Year of オーダー日」でリスタートさせているので、
各「Year of オーダー日」で明示的に「(カテゴリの)固まり」を作成し
その「固まり」内の「カテゴリ」ごとのSUM(売上)を用いてWINDOW_SUMを計算
しています。
「カテゴリ」だけ使用した場合も同様に、「固まり」の議論で理解できます。
ここでは表計算自体は「カテゴリ」で行われますが、Viz-LODに「Year of オーダー日」を含むので
Viz-LODにより各「Year of オーダー日」で「(カテゴリの)固まり」が作成され
その「固まり」内の「カテゴリ」ごとのSUM(売上)を用いてWINDOW_SUMを計算
しています。
繰り返しになりますが、結果が同じでも、そのコンセプトは異なることを抑えてください。
やや難解な話をしていることは承知ですが、理解に困った場合には
INDEXの話のセクションまで戻って、表計算がどのような動作をしているか理解する
Tableauを開いて、自分で挙動を確認する
ことをオススメします。
自分もこれを書いている最中、自分の理解が甘かったことを思い知りました。
表計算は深淵でした。今でも底が見えません。
まとめ
今回のポイントを列挙します。
Viz-LOD
Specific Dimensionsの順序
上記二つによって、Vizの裏で作られているテーブルの形
Viz-LODまたはRestarting everyによって作られる「固まり」
この4点さえ押さえておけば、表計算は理解して使えるのではないかなと思います。
その他細かい話や、それぞれの表計算関数は、この基礎の話をもとにした実践と応用で十分理解できるのかなとも思います。
表計算を組み立てるときに「裏側で作られる表」に、必ず思いを馳せて下さい。
それだけです。
今回はあくまでも「表計算を理解するための考察」です。
技術的側面やTableauの仕様との齟齬はあるかもしれませんが、むしろ技術的アプローチから、表計算の動作原理や「目に見えないもの」への議論や解説が進めば、それはとても幸せなことだと思います。
ご質問等はTwitterまたはLinkedinまでよろしくお願いします。 それでは。