Power BI: 実績と計画の比較方法の提案

**Microsoft Power BI Advent Calendar 2024** の16日目の記事です。

https://qiita.com/advent-calendar/2024/powerbi

（年末くらいにサンプル ファイルをアップしますので、しばしお待ちを m(_ _)m）

# これは何？
実績と計画（or 予算・目標？）のデータから、実績を計画または前年と比較したい。その際、メジャーとフィルター ウィンドウを上手く組み合わせるやり方もあるよという提案。

# モデルの説明
データは、国別日別の売上の実績と計画のファクト テーブルと、日付・国のディメンション テーブル。

![モデル ビュー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/01cb9ef0-e0ee-0242-ccf4-65f60a5b4389.png)

# やりたいこと
実績を計画または前年と比較するレポートを作成したい。

売上について以下の4つの比較を行いたい。
1. 直近月単月での実績と計画の比較
2. 直近月までの年度累計の実績と計画の比較
3. 直近月単月での実績と前年同期の比較
4. 直近月までの年度累計の実績と前年同期の比較

つまり、期間が単月か年度累計か、比較対象が計画か前年同期かの組み合わせの4パターンということ。

![やりたいこと.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/8fa59556-a1a2-231d-d061-f4c3176b7f83.png)


# とりあえずメジャーを用意してみると

まずはメジャーを書いてみる。

左上の3つの数字: 

```DAX:売上_実績;売上_計画;売上_計画比
売上_実績 = SUM('F_売上_実績'[_売上_実績])

売上_計画 = SUM('F_売上_計画'[_売上_計画])

売上_計画比 = [売上_実績] - [売上_計画]
```

左下の3つの数字: 
```DAX:売上_実績_年度累計;売上_計画_年度累計;売上_計画比_年度累計
売上_実績_年度累計 = CALCULATE([売上_実績], DATESYTD('C_日付'[日付], "3/31"))

売上_計画_年度累計 = CALCULATE([売上_計画], DATESYTD('C_日付'[日付], "3/31"))

売上_計画比_年度累計 = [売上_実績_年度累計] - [売上_計画_年度累計]
```

右上の3つの数字（再掲あり）:
```DAX:売上_実績;売上_前年同期;売上_前年同期比
売上_実績 = SUM('F_売上_実績'[_売上_実績])

売上_前年同期 = CALCULATE([売上_実績], SAMEPERIODLASTYEAR('C_日付'[日付]))

売上_前年同期比 = [売上_実績] - [売上_前年同期]
```

右下の3つの数字（再掲あり）:
```DAX:売上_実績_年度累計;売上_前年同期_年度累計;売上_前年同期比_年度累計
売上_実績_年度累計 = CALCULATE([売上_実績], DATESYTD('C_日付'[日付], "3/31"))

売上_前年同期_年度累計 = CALCULATE([売上_前年同期], DATESYTD('C_日付'[日付], "3/31"))

売上_前年同期比_年度累計 = [売上_実績_年度累計] - [売上_前年同期_年度累計]
```

カード ビジュアル（新しいほう）で表示してみるも、↓のようになってしまう。 `売上_実績` は24年7月の58になってほしいところ、24年の年度累計になっている。 `売上_計画_年度累計` も24年7月までの計画値の年度累計となってほしいところ、24年全体の計画値となっている。

![とりあえずメジャーを用意してみると.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/afdb56a5-aaa2-b7a1-941f-60f9916a6e07.png)



まあ、そうなるのは当たり前なんだけど。

ただ、ビジュアルのデータ ポイントをクリックしてクロス フィルターを効かせれば、欲しい数字が得られる。

![クロスフィルターが効けば欲しい数字に.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/9f26a60e-be31-4391-7ac2-abe95bf71e07.png)


問題はクロス フィルターが効いていない場合ということ。

# こう直す

良い感じでフィルター コンテキストを効かせられるように、メジャーとフィルター ウィンドウを活用する。

`直近フィルター用` メジャーを用意: 
```DAX:直近フィルター用
直近フィルター用 = IF(ISEMPTY('F_売上_実績'), BLANK(), SELECTEDVALUE('C_日付'[日付]))
```

このメジャーは、 `F_売上_実績` テーブルにデータがない時には空白、そうでないときには選択している日付を返す。

このメジャーをフィルター ウィンドウで使おう。カード ビジュアルにフィルターをかける。日付テーブルの `日付` 列を設定し、上位Nとし、値フィールドに先ほどの `直近フィルター用` メジャーを設定する。

![フィルター ウィンドウの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/e6c42dcb-19e6-463b-447d-2b3fb316bb57.png)

# 何でそうなるの？
日付でフィルターをかけているが、上位Nにメジャーを設定すると、そのメジャーが日付別のフィルター コンテキストで評価される。右下のビジュアルはそれを理解するためのもの。
![上位Nでフィルター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239172/7ea935a9-af9c-5ee3-8eac-719509467d07.png)

で、得られた結果が最大となる日付で絞っているため、日付=24/7/1でフィルターされるというわけ。


# 他の方法
他にもやり方はいろいろある。
1. **〇: 月スライサーを追加**: フィルター コンテキストを上手く効かせたいのなら、これが最もシンプル。月が選択されていないのだから、月を選択するスライサーを用意すれば良いだけ。
2. **△: メジャーで頑張る**: メジャーの中で、実績のデータの最新日付をとってきてフィルター コンテキストを作る方法。できるけど、メジャーが複雑になりすぎる/増えすぎるので、あまりお勧めしない。
3. **（×）: ファクト側日付でフィルター**: フィルター ウィンドウでファクト テーブルの日付を使って、フィルターをかける。メジャーを定義せずに上手くやれそうな気がするけど、研究不足のため、できなかった。（今回のように、実績と計画でファクト テーブルを分けているとできないかもだけど）

ページ内のスライサーは少ない方が見た目がすっきりするので、そこはビジュアルのクロス フィルター機能で代替してみたというのが今回の提案手法。ただ、このやり方はレポート閲覧者がクロス フィルター機能を知っていることが前提になるし、メジャーが増えるのでそこはデメリットかも。

まずは1.の方法で試すのが良さそう。

また、メジャーの数が多くなければ、メジャーで済ませる方がスマートなことも。 @AkioSaitoh さんの記事も参考に。

https://qiita.com/AkioSaitoh/items/49a7f5c0f57208a1b0bb

# まとめ
いろんな方法があるので、引き出しをたくさん用意しておくことが大事。フィルター ウィンドウを使うか、スライサーを使うか、メジャーやモデリングで頑張るかのどれかが毎回正解というわけではない。その時々でパフォーマンス、ユーザビリティ、シンプルさを比較し、最適なものを選ぶこと。

いろいろ知っていることが前提なので、勉強ってやっぱ大事だ。