Power BI レポート上の スライサ ビジュアル で "選んだもの" と "そうでないもの" っていうのをどう表現するかって話 #PBIJP

スライサで選択したものとそれ以外でそれぞれ集計するにはどうするかっていう話が、先般開催した PBIJP DAX Boot Camp #2 でありまして、メジャーだけで何とかするよりモデリングから考慮するとよいかもしれないねという話をしたのです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">DAX難しいですか？何度も言いますけど、勉強しなければできるようになりませんから、安心して勉強してください📢 <a href="https://t.co/wlKqXjYekp">https://t.co/wlKqXjYekp</a> <a href="https://twitter.com/hashtag/PBIJP?src=hash&amp;ref_src=twsrc%5Etfw">#PBIJP</a></p>&mdash; Takeshi Kagata / Power Platform 部 Power BI 科 🛏💤 (@PowerBIxyz) <a href="https://twitter.com/PowerBIxyz/status/1303542262496862208?ref_src=twsrc%5Etfw">September 9, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# こういうこと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/c90578ae-bcfd-dbdd-120b-a5adbd9b2911.png)
スライサ ビジュアルで選択された商品の"区分名"のうち、選択されたものとそれ以外でそれぞれ"受注額"を集計するみたいな。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/d3cb0298-8a4c-7a3a-fc0c-020bcfb6b4d8.png)
データモデルはシンプル。

# どうするか
## ビジュアルをサポートするテーブルが必要
例にあるビジュアルに限らずで、見出し / 軸として投影する値を持つ列が含まれるテーブルが存在しなけれればならないから用意する。

```text:CalculatedTable
Selected_Others = 
DATATABLE(
    "ID", INTEGER,
    "Index", STRING,
    {
        { 1, "Selected" },
        { 2, "Others" }
    }
)
```
|ID|Index|
|:--:|:--:|
|1|Selected|
|2|Others|
計算テーブル / Calculated table で用意した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/005a3060-34ad-5f9d-c08c-1e04a2f45825.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/c3a24fc3-d837-bf7c-8988-c054d4883acf.png)
こんな感じのテーブルをこさえることができるのです。メジャーを記述するときの経過を表示しながらなのでテーブル ビジュアルががちょうどよいというだけ。

## すべての "区分名" とは
スライサ ビジュアルの選択に関わらないすべての Products[区分名] 

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
RETURN
    CONCATENATEX(
        AllCategories,
        Products[区分名], "|"
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/50dbb0ee-1b4d-82ae-7f9b-bc7e35e676ef.png)
まぁこうなるよね。ALL ですから。すべての行でおなじ結果を得ているのはリレーションシップによる評価コンテキストがないからだし。

## 選択された "区分名" とは
```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = ALLSELECTED( Products[区分名] )
RETURN
    CONCATENATEX(
        SelectedCategories,
        Products[区分名], "|"
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/19b98055-9d54-cef6-911e-a19a843b547d.png)
**ALLSELECTED 関数の効果**
ALLSELECTED 関数の使い方としてはよくあるもので、ALLSELECTED 関数で スライサ ビジュアルで選択済みの Products[区分名] を参照することができた(ようにみえる)。
テーブル ビジュアルで ALLSELECTED 関数が含まれる該当のメジャーが評価されるとき、外部にあるビジュアル (ここでいうとスライサ ビジュアル) の結果を取得しているものではなく、**たまたまそうなっている**という認識が必要。
ビジュアルの相互作用で スライサ ビジュアル の選択した状況は テーブル ビジュアル に伝搬しているだけであって、ALLSELECTED 関数が スライサ ビジュアルの結果をスキャンしているわけではない。**たまたまそうなる**っていうだけ。
ここでは VALUES 関数でも同じ結果を得ることができる。

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = VALUES( Products[区分名] ) // 🤪ココ
RETURN
    CONCATENATEX(
        SelectedCategories,
        Products[区分名], "|"
    )
```

## 選択されていない "区分名" とは
すべての Products[区分名] から 選択された Products[区分名] を除外すればよい。EXCEPT 関数を使った。

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = ALLSELECTED( Products[区分名] )
VAR UnselectedCategories = EXCEPT( AllCategories, SelectedCategories )
RETURN
    CONCATENATEX(
        UnselectedCategories,
        Products[区分名], "|"
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/70619a76-435e-4cfe-d446-e8312d609111.png)

## 行ごとの投影を実現する
そもそもリレーションシップがないから フィルタ コンテキストを用意してくれない。なので自力で実装する。イメージとしては、テーブルの行ごとの 'Selected_Others'[Index] を検証しながら結果を得るしかない。

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = ALLSELECTED( Products[区分名] )
VAR UnselectedCategories = EXCEPT( AllCategories, SelectedCategories )
VAR CurrentIndex = SELECTEDVALUE( 'Selected_Others'[Index] )
RETURN
    CurrentIndex
``` 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/5172d6ed-c3a5-9930-1c75-d43967155b45.png)
SELECTEDVALUE 関数は IF( HASONEVALUE( *\<column_name>* ), VALUES( *\<column_name>* ) ) と等価。合計列では、'Selected_Others'[Index] は単一の値を持たないので *blank*

SWITCH 関数で行ごとの投影を切り替えを実装

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = ALLSELECTED( Products[区分名] )
VAR UnselectedCategories = EXCEPT( AllCategories, SelectedCategories )
VAR CurrentIndex = SELECTEDVALUE( 'Selected_Others'[Index] )
RETURN
    SWITCH(
        CurrentIndex,
        "Selected",
            CONCATENATEX( SelectedCategories, Products[区分名], "|" ),
        "Others",
            CONCATENATEX( UnselectedCategories, Products[区分名], "|" ),
        CONCATENATEX( AllCategories, Products[区分名], "|" )
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/883af0d2-270f-0d7c-94ae-23be2bc94237.png)

あとはお好きなように。

```text:Measure
Selected / Others = 
VAR AllCategories = ALL( Products[区分名] )
VAR SelectedCategories = ALLSELECTED( Products[区分名] )
VAR UnselectedCategories = EXCEPT( AllCategories, SelectedCategories )
VAR CurrentIndex = SELECTEDVALUE( 'Selected_Others'[Index] )
RETURN
    SWITCH(
        CurrentIndex,
        "Selected",
            CALCULATE( [受注額], SelectedCategories ),
        "Others",
            CALCULATE( [受注額], UnselectedCategories ),
        CALCULATE( [受注額], AllCategories )
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193509/951cfae9-25e5-dff2-2739-ec25d1eff2f0.png)

# 思ったこと🙄
レポートの機能を重視するときになって初めて検討する手段ではあるから、常にパフォーマンスについての検証が重要。
レポート レベル フィルタ / ページ レベル フィルタ が関わると期待する動作をしないことがありますよ。なんで？と思ったら、評価コンテキストについてよーく勉強したらいい。

# その他
- [Power BI 勉強会 - connpass](https://powerbi.connpass.com/)
- [Power BI Desktop を使い始めるには](http://qiita.com/PowerBIxyz/items/6c4735cb5b5712e70981)
- [Excel で Power Query を使い始めるには](http://qiita.com/PowerBIxyz/items/52e563037b855a099ca9)