ISFILTEREDとその応用(スライサーを利用してビジュアル上で動的にカテゴリを振り分ける)

この記事は[Microsoft Power BI Advent Calendar 2022](https://qiita.com/advent-calendar/2022/powerbi "Microsoft Power BI Advent Calendar 2022")に参加しています。
使用しているPower BIは2022/12/15時点の最新バージョンです。

# [ISFILTERED]
[ISFILTERED]は、指定されたテーブルまたは列が直接フィルター処理されている場合にTRUEを返します。
どういう動きをするか見ていきましょう。

# 検証で利用するデータセット
Power BIの神[Takeshi Kagata](https://qiita.com/PowerBIxyz)さんが作成したサンドボックスのデータを利用させていただきました。
ファクトが1つ、ディメンションが4つのスタースキーマです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/3bb8b9ed-7928-0e4b-3f7a-d5bd99595084.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/a9f82d69-03af-8c62-1f1c-d168447ac44d.png)

## [ISFILTERED]の検証
[ISFILTERED]の動作を検証するために、ディメンションテーブルを[ISFILTERED]の引数にしてみましょう。
今回利用する`Persons`テーブルは社員と所属のIDと、それぞれの表示名で構成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/dfc4eaf8-faae-7ea3-4e51-88c5b07f43cd.png)

メジャーは以下の通りです。
```DAX:ISFILTERED所属
ISFILTERED所属 = ISFILTERED('Persons'[所属])
```
`Persons`テーブルから`所属`の列を[ISFILTERED]の引数とします。
このメジャーをレポートページに置いただけでは`False`になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/dc413b30-6ac4-6047-e04a-50cbb45aa2c0.png)

### スライサーの場合
所属のスライサーをページに追加して`大阪支社`を選択してみます。
`大阪支社`が選択されるとメジャーは`True`になりました。

[ISFILTERED]は、指定されたテーブルまたは列が**直接フィルター処理されている場合**にTRUEを返します。

ここでは[ISFILTERED]の引数にした`所属`フィールドが直接フィルターされたため、`True`になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/c7f279e0-f2e6-4c9a-0729-c03cfdff6e47.png)
次に同じ`Persons`テーブルから`所属`に変わり`氏名`をスライサーに設定し、加藤さんを選択してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/ec5037b9-2c7a-ea07-957b-e0bcdf5a5417.png)
メジャーは`False`のままです。

[ISFILTERED]は、指定されたテーブルまたは列が**直接フィルター処理されている場合**にTRUEを返します。

この場合、加藤さんの`所属`は`東京支社`ですが、`氏名`のスライサーで`所属`列が間接的にフィルターされているためメジャーは`False`となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/0183796f-c48d-f04b-150f-7f371e602813.png)

### 階層化したスライサーの場合
`Persons`テーブルから`所属`と`氏名`をスライサーに階層化して配置します。
`所属`が上位、`氏名`が下位です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/a1da271e-fa12-ff64-d48b-3792642d558b.png)
`氏名`から松沢さんを選択してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/abff882f-d650-69d3-4449-e7443cb70e97.png)
メジャーは`True`になりました。
先ほどの加藤さんを選択した時とは異なる結果ですが、これは階層化したスライサーで下位の`氏名`を選択した結果、上位の`所属`も選択されているためです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/ae17dd58-8929-bea3-5de3-5067eb7ae6da.png)

### 所属にフィルターを掛けたメジャーと同じテーブルビジュアルに置いた場合
`[ISFILTERED所属]`を以下のメジャーと同じテーブルビジュアルに配置してみる。
```DAX:所属を大阪支社でフィルター
所属を大阪支社でフィルター = CALCULATE([数量合計],'Persons'[所属]="大阪支社")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/77f82f42-f97a-c7d5-9f42-859a0be0db9b.png)
`False`のままです。
[ISFILTERED]は同じビジュアルに配置された他のメジャー内で使われているフィルター式には反応しません。

### 他のビジュアルから相互作用でフィルターされた場合
他のビジュアルで所属を含む行が選択された場合を見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/c9cf98e9-bff8-1196-781d-c7cbcb00e89e.png)
`[ISFILTERED所属]`は`True`になりました。

`所属`を含むテーブルビジュアルで行が選択されているため、相互作用で直接`所属`列がフィルター処理されています。

### 同じテーブルビジュアルにISFILTEREDの引数で使った列を置いた場合
同じテーブルビジュアルの中にメジャーと`所属`列を配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/88a777fd-755a-9680-10ef-b2b51384e59c.png)
テーブル内は`所属`列でビジュアル内でフィルターが掛かっているため`True`になりますが、総計はテーブル内に配置された列のフィルターは関係無くなるため`False`となります。

スライサーでフィルターを掛けた場合、総計はどうなるでしょう？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/2fb5f00b-d10c-818d-5c5f-5b9976ea2280.png)
この場合、ビジュアルの相互作用で総計にも`所属`列がフィルター処理されるため、`[ISFILTERED所属]`は`True`になります。

# 応用編 : ISFILTEREDでスライサーを利用してビジュアル上で動的にカテゴリを振り分ける
ここでは以下の図で、右のスライサーで選択した項目のみを集計対象YESとして縦棒グラフで動的にカテゴライズしている方法を説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/9cc54a6d-c759-5e98-3be8-9fcb42aa0a0c.png)

## スライサーで選んだ項目だけを計算する
まずスライサーで選んだ項目だけを計算するメジャーを作る必要があります。
計算式は以下の通りです。
```DAX:集計対象YES
集計対象YES = IF(ISFILTERED('Persons'[所属])||ISFILTERED(Persons[氏名]),[数量合計],BLANK())
```
### メジャー集計対象YESの解説
`ISFILTERED('Persons'[所属])`と`ISFILTERED(Persons[氏名])`をOR条件の論理演算子`||`で繋いでいます。
こうする事で、`所属`と`氏名`のどちらかをスライサーで選ぶと`True`を返すようになります。

後は`True`であれば`[数量合計]`を、`False`であれば`BLANK()`を返せばスライサーで項目を選択した時のみ[数量合計]が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/87740103-b893-2395-2fb0-7bb11a6ca85a.png)

## スライサーで選んでいない項目だけを計算する
次は先ほど作成した`[集計対象YES]`のメジャーを利用して、スライサーで選んでいない項目だけを計算するメジャーを作ります。
計算式は以下の通りです。
```DAX:集計対象NO
集計対象NO = CALCULATE([数量合計],REMOVEFILTERS(Persons[所属]),REMOVEFILTERS(Persons[氏名]))-[集計対象YES]
```
### メジャー集計対象NOの解説
まず[REMOVEFILTERS]で`[数量合計]`から`Persons[所属]`と`Persons[氏名]`のフィルターを外します。
こうする事でスライサーで選択された`所属`と`氏名`のフィルターを無視して`[数量合計]`が算出されます。

後は算出された値をメジャー`[集計対象YES]`でマイナスすれば、`[集計対象YES]`を除いた差分が算出されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313502/59136e00-8960-8817-5b4d-823e3b20fd70.png)

# まとめ
### [ISFILTERED]は引数で指定したテーブルや列が直接フィルター処理されている必要がある
間接的なフィルターでは動作しません。
### 行コンテキストの範囲を考えよう
テーブルビジュアルでもグリッド内と合計ではフィルターの掛かり方が違います。
### やりたい事に対して想像力を膨らまそう
用意されていない機能でも工夫で何とか出来る場合もあります。
***
フィルターの状況に応じて動くDAX関数は[ISFILTERED]の他にも[FILTERS]、[HASONEFILTER]、[HASONEVALUE]、[ISCROSSFILTERED]、[ISINSCOPE]等があります。
一通り目を通して実際に触ってみると、実務で生かせる何かが見えてくるかもしれません。

[ISFILTERED]:https://learn.microsoft.com/ja-jp/dax/isfiltered-function-dax
[REMOVEFILTERS]:https://learn.microsoft.com/ja-jp/dax/removefilters-function-dax
[FILTERS]:https://learn.microsoft.com/ja-jp/dax/filters-function-dax
[HASONEFILTER]:https://learn.microsoft.com/ja-jp/dax/hasonefilter-function-dax
[HASONEVALUE]:https://learn.microsoft.com/ja-jp/dax/hasonevalue-function-dax
[ISCROSSFILTERED]:https://learn.microsoft.com/ja-jp/dax/iscrossfiltered-function-dax
[ISINSCOPE]:https://learn.microsoft.com/ja-jp/dax/isinscope-function-dax