Tableau でクリスマスツリーを作ってみた

この記事は、[Ateam LifeDesign Advent Calendar 2022](https://qiita.com/advent-calendar/2022/ateam-life-design) シリーズ2の19日目の記事です。

# これはなに

Tableau を使っておもしろいものを作ってみよう、というお遊びです。
世界中の様々な Viz（Tableauでビジュアライズされたグラフ等のこと） が投稿されている [Tableau Public](https://www.tableau.com/ja-jp/community/public) を眺めていると、Tableau でこんな表現ができるだ、こういう表現はこんなやり方してたんだ、といった新しい発見があったりして楽しいです。

そんなノリで見ていただければと思います。
この記事が Viz力のアップに役立つかどうかは分からないです…

# 背景

2022年は、4月の終わりくらいに初めて Tableau を触ってから、8ヶ月ほど Tableau にどっぷりつかっていた年だと言えるので、アドベントカレンダーは Tableau でなんかおもろいことをやろう、と思い、
そうだ、12月だし X'mas ツリーを Tableau で描こう！と思い立ちました。

しかし、ぐぐってみると、なんと 2020年の冬に開催された JTUG（日本のTableauユーザー会） の総会の目玉イベントである「VizつくりまSHOW!」の決勝のお題がなんとクリスマスツリー・・・
[Tableau Public での Viz はこちら](https://public.tableau.com/app/profile/jtug/viz/VizShow_tournament20201211_final_happymerrychristmas/FinalRound)
![2020Viz決勝戦.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/c5041525-f070-a83a-3eb6-03ae0d9dfd87.png)
そして、2021年はクリスマスケーキ・・・
2022年はリアルタイムで総会を見ていましたが、まさか過去にそんなお題が出ていたとは…
もう2年前にこんな~~ヘンタイな~~楽しいことをしていたとは… JTUGすごい！


2020年VizつくりまSHOW！決勝問題のクリスマスツリーは初見で何から手を付けてよいか全く分からなかったのですが、上記の Tableau Public で公開されているワークブックをダウンロードして中身を見たり、JTUG総会の[動画](https://www.youtube.com/watch?v=ZKlsc5fTKdE)での解説を見たりすることで仕組みが分かり、とても勉強になりました。

で、何をやろう、と思っていたんですが、JTUGがその決勝問題のボーナス問題として進化版を Tableau Public にて中身のダウンロードは不可で、ビューの表示だけを載せてくれていたので、それを実装してみることにしました。

# お題

2022年 JTUG VizつくりまSHOW!決勝戦Bonus問題

https://public.tableau.com/app/profile/jtug/viz/VizShow_20201211Bonus/Merry_Christmas

![お題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/6822bd9b-44d9-f713-fcad-eda6f31bf41a.png)

ちなみに、データソースは、Tableau にデフォルトで付いてくる「サンプル - スーパーストア」という架空のお店の製品、顧客、オーダー、売上などが入ったデータを用います。

決勝戦の問題から進化している点。
- ツリーの輪郭線が滑らかになっている
- 背景で星が瞬いている
- オーナメントがバラバラの位置にある

あたりがキモでしょうか。

# 自分なりの解

最初に今回の自分なりの最終形をさらしておきます。

![christmastree_anime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/50341ee9-4c2a-94b3-4936-6f38b5a48ae2.gif)


おい、お題とちげーじゃん、というところなんですが…

はい、輪郭を滑らかにするのがどうしても分かりませんでした泣
ギザギザの粒度を細かくすれば（決勝戦のお題では四半期だったので、それを日付まで細かくすれば）それなりになりますが、どうしてもジャギー感が残ってしまう。。。たぶんこうじゃない…
![XmasTree_daily.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/993d174c-8d8e-754f-ea9d-69dd5b6611b0.png)


全然違うアプローチを取らないといけないのでは、と思うのですが、いろんなやり方を試してみたものの、どうにもうまくいかず…
分かる方がいらっしゃったら是非ご教示いただけるととてもうれしいです。

というわけで、発想を変えて、よりイラストっぽいクリスマスツリーにしよう、ということで、よくイラストであるノコギリ状の形にしてみました。
ジャギーが少しあったほうがより木っぽいのでは、と思い、あえて残してあります。（言い訳）

# 中身の解説

では、作る過程でハマった点も含めて、中身を解説していきます。

## 星空
まずは一番簡単な星空から。
![星空.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/7ea5182a-90a6-ae94-6246-835b3a3e8828.png)

列：オーダーID
行：ランダム値 の 平均
グラフ種類：マーク（円）

オーダーID が 3,000個近くあり、IDのネーミングルールが年ごとに分かれているものの、
月はバラバラだったため、オーダー日の月でページにするのにちょうど良かった。
「JP-2017-1000099」といった感じ。
（多少、月によってオーダーの多い少ないがあって、星の数にばらつきはある…）

カーディナリティが高いオーダーIDを列にすることを決めたので、あとは、縦に散らばせたいんですが、オーダー日や他の指標では値の種類が全然たりず、グリッド感がでてしまう。
そこで乱数を使うことにしました。

実際のデータ分析で乱数を使うことはあまり想像はできないですが、Tableau には `random()` という乱数を生成する関数があるのでそちらを利用します。
`random()` は 0〜1 の間の小数を返します。オーダーIDごとに複数レコードある場合があるので平均にしています。

ちなみに、`random()` は Tableau の関数のヘルプには存在していません。
非公式の関数で、将来非推奨になる可能性がある、とのこと。
これで生成される乱数は、再現性がないために毎回異なる値になってしまうようです。
シードがいらないのでお手軽である反面、値に再現性・一貫性がないので何度も検証が必要なものには向かなさそう。

公式サポートとしては、

https://kb.tableau.com/articles/issue/random-function-produces-inconsistent-results?lang=ja-jp

ランダム、かつ、再現性のある擬似乱数が必要であれば、コミュニティに投稿されたこちらの方法を参考にせよ、とのこと。

https://community.tableau.com/s/question/0D54T00000G552JSAR/random-number-generation


次に、表示形式をマーク（円）にして、サイズを適当に調整して、色を設定したら画面いっぱいに星がバラけます。
背景は、「書式設定」の「網掛け」の「シート」→「ワークシート」で色が選択できます。

他にも、枠線を消すのにそこそこ苦労します。
書式設定で、いろんな箇所で枠線を「なし」にしてもなかなか消えてくれなかったりして、どのメニューのどの箇所をいじるとどこが反映されるかは確認しながらいろいろいじると勉強になります。

最後に、「ページ」にオーダー日を入れて「月」とすることで、各月のオーダーのみが表示され、再生ボタンを押すことで順に表示され、星が瞬いているのを表現しています。

## オーナメント
続いてオーナメントです。
![オーナメント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/1cb54778-5e97-45d9-560b-03536384992a.png)

列：数量 の 平均
行：利益率 の 集計
グラフ種類：形状

決勝戦のお題では、列がオーダー日の四半期で分けられていて、オーナメントが5つの列で並んでいましたが、今回はバラバラです。
バラバラときたらまた乱数でやっちゃえ、と思って最初は `random()` を使ってやったんですが、問題がありました。

利益率とランダムな値で散らすと画面いっぱいにオーナメントが散らばりますが、ツリーからはみ出たところは「除外」する必要があります。
決勝戦の解説動画でもあるように、「ビューツールバー」の「投げ縄選択ツール」ではみ出たところを除外していくわけですが、
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/59a4bf26-4c99-2aa7-9e6e-02643404f53b.png"> <img width="155" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/f29bbbaf-d891-d5d8-210d-da02b7e01add.png">

除外しても除外しても、新たにはみ出る点がでてきてしまう。
軸の範囲が自動だからかなと思って、軸の範囲を固定してみたけどだめ。
どうやら、除外するたびに毎回 random() の値が変わって改めて全体に散らばってしまう様子…
再現性のない random() の罠か、ということで、上記で説明した再現性のある疑似乱数の計算フィールドを作成して、そちらを利用してみたけど、同様の現象…

疑似乱数も `PREVIOUS_VALUE()` を使って、前の値を入力値として次の乱数を求めているので、「除外」をして、リスト（や順序）が変わってしまうと、レコードに割り当てられる乱数値も変わってしまうということなんだろうか。
実際の各レコードに依存した値なわけではないので、そりゃそうか…
ビューの時点ではなく、DBの方か抽出時にランダムな値を入れておかないとこういうケースでは使えなさそう。

:::note info
ちなみに、疑似乱数を作る際に、上記の Tableau コミュニティのページにあるように、
乱数発生の計算フィールド
`((PREVIOUS_VALUE(<シード値>) * 1140671485 + 12820163) % (2^24))`
と、乱数の整数化の計算フィールド
`INT(<上記の乱数> / (2^24) * <欲しい乱数の数>) + <乱数の最小値>`
（ここで、`<シード値>` は好きな数値、`<欲しい乱数の数>`、`<乱数の最小値>` は、例えば、1〜10 の乱数が欲しければ、それぞれ 10, 1 となる）
は別々の計算フィールドにしないとうまく動かない。
いっぺんに計算すればええやんと思って、
`INT(((PREVIOUS_VALUE(<シード値>) * 1140671485 + 12820163) % (2^24)) / (2^24) * <欲しい乱数の数>) + <乱数の最小値>`
とすると、ランダムにならずに、同じ値がずっと続く現象が起きてしまった。
おそらく PREVIOUS_VALUE() が <欲しい乱数の数> の種類しかないことになり、同じ値の入力→出力に収れんしてしまうのではないかと思う。
そんなことをするのは僕だけですね。。。
:::


というわけで、乱数をあきらめ、他に小数でバラけさせるものがないか探し、（オーダーの）数量の平均がそこそこバラけていたのでそちらの使うことにした。

あとは、色にカテゴリを入れ、出荷モードで形状を分け、顧客区分でサイズを変えています。
ただ、それだけだとちょっとオーナメントの数が少なくて、曜日でページに分けると寂しくなるので、詳細にサブカテゴリも入れて数を増やしています。

ページングもお題は曜日になっていましたが、四半期にして１ページあたりのオーナメントの数を増やしています。

## ツリー

難関のツリー。

![ツリー（日）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/4c42b6be-5ecf-9c30-6ff5-bf851363683a.png)

元々は、行に売上の合計を入れ、それを出荷日で内訳にしてサイズにすることで、横幅は新しいものほど大きくなり、縦幅はそれぞれの期間の売上という棒グラフになっていいました。

これを滑らか（というか折れ線）にする方法がどうにも分からず、発想を変え、以下のようにノコギリ状にしてみました。

![ツリー（ノコギリ）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/eb3d3736-3deb-271e-f41e-d02c550c00e0.png)


列：（なし）
行：利益 の 合計 ｘ 6個
グラフ種類：棒

（元々売上でしたが、範囲の絞り込みでうまくいかなかったので利益に変えています。深い意味はないです）
上記のスクショは分かりやすいように、縦軸を表示してあります。

同じ指標を6個並べ、それぞれの縦軸の範囲を変えることで、元々三角形というか山の形になっているものを切り取り、それぞれの箇所の上部と下部の幅を調整しています。
ポイントは、上部から下にいくにつれ、横幅を大きくしているところ。
6つの軸の表示域のそれぞれの高さも変えられるともうちょいそれっぽくなるかもですが、それはできないようでした。

６つの軸の一つ一つをビューとして、ダッシュボードでフローティングで重ねればもっと柔軟に形を作れると思うのですが、なんかそれは邪道な気がしてやってないです。（ここまでやっておいて邪道もくそもないですが笑）

ジャギーの大きさはサイズの出荷日の粒度で変えられます。四半期だとドット絵すぎるし、日だとほぼ滑らかなので、個人的には月がちょうどいいのでは、と。

## おまけ

最後に、ツリーのてっぺんがちょっとさみしいなと思ったので、星の形のオーナメントを置きました。
これは星の形状マークを作って、ダッシュボードでフローティングで置いただけです。
![スター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/f95676c1-f3ce-75d6-1d23-5a0a1dc1da40.png)
![スター_ダッシュボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81599/087404d7-3a83-7b14-4bfe-00f2b2d2cb62.png)


# まとめ

今回のクリスマスツリー作成を通じて、
- 棒グラフの内訳をサイズで変えて見せる
- 投げ縄ツールで除外する
- ページでアニメーション
- random()、擬似乱数の作り方
- 罫線の徹底的な消し方

といったことが学べたかなと思います。
業務上で使うタイミングがあるかは分かりませんが…

普段、Tableau でビューを作っていると、どうしてもクロス表、棒グラフ、折れ線グラフといった表現ばかりになってしまうので、頭を柔軟にするためにも、たまにはこういった遊びで Tableau の表現方法を探して使い倒す機会を作るのは良いなと思いました。

読んでいただけた方がいらっしゃったら、何かのきっかけになれば幸いです。
では、よいクリスマスを。