[Power BI] 2022年12月に追加された新DAX関数３つを考える🤔（INDEX、OFFSET、WINDOW）

# はじめに

2022年12月に、Power BI のDAX関数に新たに3つ関数が加わりました。

- INDEX
- OFFSET
- WINDOW

# 触ってみる💪

## INDEX関数

### 公式ドキュメント及びDAX Guide

https://learn.microsoft.com/ja-jp/dax/index-function-dax

https://dax.guide/index/

## 説明

> 指定されたパーティションを指定された順序で並べ替えて、絶対位置 (position パラメーターで指定) にある行を返します。 現在の位置から 1 つの位置を導けない場合は、複数の行が返される可能性があります。

位置を指定して、その値をもってこれるようだ🤔

### 構文

```
INDEX(<position>[, <relation>][, <orderBy>][, <blanks>][, <partitionBy>])
```

`relation`は元となるテーブル。

絶対位置を指定する`position`は必須。`orderBy`を指定していれば、`relation`は省略可能。

`relation`が省略されている場合は`orderBy`と`partitionBy`で指定されている列を`ALLSELECTED`で囲ったものが指定される。

ORDERBY関数とPARTITIONBY関数は今回追加された新関数の中だけで使える補助的な関数。

`blanks`は、現状`KEEP`しか指定できない。今後アップデートがあるみたい。

わけが分からなくなるので、なるべく引数は省略せずに書いていく。

### 基本のかたち

```
INDEX_1 =
CALCULATE(
    [Sales Amount],
    INDEX(
        1,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名]),
        KEEP
    )
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/403dff46-6035-d0ff-3f42-510a8c39c76b.png)


INDEX番号は1行目から順番に1,2,3...が割り当てられていて、ORDERBY関数で指定した並び順にそって、`position`で指定した位置の行にアクセス可能。合計行も指定した行にアクセスしていることに注意。

### INDEX番号を変えてみる。

```
INDEX_1 =
CALCULATE(
    [Sales Amount],
    INDEX(
        3,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名]),
        KEEP
    )
)
```

INDEX関数の`position`引数で**3**を指定したので、結果はこうなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/7707912b-5b1e-3651-607d-ff89dbc54543.png)

### ORDERBY関数に並び順DESCを指定

```
CALCULATE(
    [Sales Amount],
    INDEX(
        1,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名],DESC),
        KEEP
    )
)
```

DESCを指定すると、内部で区分名が降順で並び替えられINDEXが付与されるため、ビジュアルで区分名を昇順に並べていた場合は一番下の行の数値をとってくるように見える。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/a3b79196-f614-9e11-bce8-bd33a71d88e9.png)

### 合計値に要注意

合計値が、純粋な合計ではなく、ここも３行目の数値をもってきていることに注意💪

### PARTITIONBY関数の追加

ディメンジョンが複数ある場合は、PARTITIONBY関数でグループ化した上でORDERBY関数で並び替えをすることができる。
ビジュアルには区分名と商品名の2つのディメンジョンを作成。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/12fb13cd-fa82-e4d5-dc6c-36c7275ae3bd.png)

PARTITIONBY関数で、区分名ごとにINDEXを付与するように指定してみる。

```
CALCULATE(
    [Sales Amount],
    INDEX(
        1,
        ALLSELECTED(Product[区分名],Product[商品名]),
        ORDERBY( 'Product'[区分名],ASC,Product[商品名]),
        KEEP,
        PARTITIONBY('Product'[区分名])
    )
)
```

結果はこう。区分名ごとに`position`が1の`Sales Amount`が選ばれているのがわかる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/02ad95c3-909f-1f69-7252-34c87d305337.png)

## OFFSET関数

### 公式ドキュメント及びDAX Guide

https://learn.microsoft.com/ja-jp/dax/offset-function-dax

https://dax.guide/offset/

### 説明

>同じテーブル内で、指定されたオフセットだけ "現在の行" より前または後にある 1行を返します。 現在の行から 1つの行を求められない場合は、複数の行が返される可能性があります。

現在の行を基準に、ずらした行の数字をとれそう🤔

### 構文

```
OFFSET ( <delta>[, <relation>][, <orderBy>][, <blanks>][, <partitionBy>] )
```

相対位置を指定する`delta`が必須。`orderBy`を指定していれば、`relation`は省略可能。

### 基本のかたち

```
CALCULATE(
    [Sales Amount],
    OFFSET(
        -1,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名] ),
        KEEP
    )
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/87aa919f-6ec7-3d42-e6c0-fbd89cfeb989.png)

今いる行が常に０となり、そこを基準に`delta`で指定した分だけずらした値をとることができます。上の例では-1を指定しているので、一つ上の行の値を取得しています。

### 合計値に要注意

合計値はOFFSETをすることでBLANKになった数字分少なくなっていることに注意👍

### PARTITIONBY関数を追加

今度も、商品名列を追加した上で、PARTITIONBY関数で区分名を囲ってみます。

```
OFFSET_3 = CALCULATE(
    [Sales Amount],
    OFFSET(
        -1,
        ALLSELECTED(Product[区分名], Product[商品名]),
        ORDERBY( 'Product'[商品名] ),
        KEEP,
        PARTITIONBY('Product'[区分名])
    )
)
```

すると期待したとおり、区分名ごとに商品名が昇順で並び、そのうえで１行上から数字を持ってくることができています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/f80a4b6e-a5f3-2258-3e5a-7b541b4d935e.png)

### スライサービジュアルを追加してみる

INDEX関数もOFFSET関数も、元となるテーブル（`relation`）はALLSELECTED関数で囲っています。元となるテーブルを明示的に指定をしなかった場合は、ORDERBYとPARTITIONBYで指定した列をALLSELECTEDで囲うというのがデフォルトの動きになります。今回は明示的にそれを書いてあげています。

ALLSELECTED関数で囲われているため、スライサービジュアルで商品名をしぼってもこのDAXは動作します。今回の場合は同区分に商品が２つ以上ある場合にだけ、ひとつ上の行にある数値を返せます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/187a2776-0f0a-7f83-7cdf-7309d1514d89.png)

## WINDOW関数

### 公式ドキュメント及びDAX Guide

https://learn.microsoft.com/ja-jp/dax/window-function-dax

https://dax.guide/window/

### 説明

>指定された間隔内に配置されている複数の行を返します。

ドキュメントの説明はシンプル。

### 構文

```
WINDOW ( from[, from_type], to[, to_type][, <relation>][, <orderBy>][, <blanks>][, <partitionBy>] )
```

`relation`以下はOFFSETやINDEXと同じ。違うのは冒頭。

指定する必要がある`from`~`to`と`from_type`や`to_type`。

相対位置（relative）を使用する場合は`from_type`や`to_type`に**REL**と書く。省略した場合は**REL**がデフォルト。

絶対位置（absolute）を使用する場合は`ABS`と書く。

ここまでわかればあとは実践。

### 基本のかたち

```
WINDOW_1 = 
CALCULATE(
    [Sales Amount],
    WINDOW(
        -1,
        0,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名]),
        KEEP
    )
)
```

`from_type`や`to_type`を指定していないため、**REL**が適用されている。相対位置を使用するため、自分がいる行が**0**となる。

from → -1

to → 0

としているため、一つ上の行と自分の行の合計値がでているはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/943cb1fc-901e-1234-2725-d96a806215b8.png)

### **REL**と**ABS**を使いこなす

ABSを明示し、`from`と`to`に絶対位置を指定する。
この場合はORDERBYで並び替えた順に一番上の行から1,2,3...と番号が振られている。

下記DAX式では`ABS`で1から2と指定しているため、１行目と２行目の合計がすべての行で表示されます。

```
WINDOW_2 =
CALCULATE(
    [Sales Amount],
    WINDOW(
        1,ABS,
        2,ABS,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名]),
        KEEP
    )
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/723e4e1d-348a-5924-0d61-b1b00bffd57a.png)

### ABSとRELを組み合わせてもOK

**REL**と**ABS**は組み合わせても使えます。

下記のDAXでは、

`from`に **1,ABS** → ORDERBYで指定した列の１行目

`to`に **0,REL** → 相対位置の場合、**0**は自分の位置

と指定しています。

```
WINDOW_3 = 
CALCULATE(
    [Sales Amount],
    WINDOW(
        1,ABS,
        0,REL,
        ALLSELECTED(Product[区分名]),
        ORDERBY( 'Product'[区分名]),
        KEEP
    )
)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/46ee8116-70f9-d8fa-729d-5baf25b9ae04.png)

絶対位置１行目（INDEXの１）から、自分の位置までの合計なので、結果的に累計値が表示されています。

## ビジュアルで並び替える🤔

今日ここまで紹介した関数は、ビジュアル上でずらした値を持ってこれたり（OFFSET関数）、ビジュアル上で位置を指定した値をもってこれたり（INDEX関数）、ビジュアル上で範囲を指定した値をもってこれたり（WINDOW関数）していました。

ただこれは、ビジュアルの並び順と、それぞれの関数内にあるORDERBY関数で指定した列の順番が一致していたからそのように見えていただけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80467/6c743f86-1b8f-7268-2e97-3f8414739235.png)

上図のように、さきほどまで累計を計算していたように見えていたテーブルも、 **[Sales Amount]** 列で並び替えると**計算結果は変わっていない**ですが、もはや累計にはまったく見えないようになりました。

ビジュアル上で行を操作しているように見えてしまいますが、内部のテーブル、列で計算していることを理解😁

# 考察

ということで、2022年12月に追加された3つの関数と、2つのヘルパー関数を触ってみました。便利そうだけど、どこで使うべきなのか。考慮しないといけないことも多く、使い所が難しいなと感じました。TwitterやLinkedIn上でさまざまな人達が議論をしています。Power BI Blogの記事からも将来的な機能追加の足がかりという表現もあるので、今後これらの関数がどのように活かされてくるのか注目ですね💪

## SQLBIのマルコさん

Twitterで以下のようなやりとりをしていました。

**Kevinさん**
新しい DAX 関数 INDEX () 、 OFFSET () 、および WINDOW ()に関する優れた包括的なビデオを引き続き探しています。それらをいじった。彼らは必ずしも私が期待したように振る舞っているとは言えません。

↓

**Marco Russoさん（SQLBI）**
1 月まで待ってください。まだ調査中です。明らかではありません。ただし、それらの主な用途は**visual calcs** ( #powerbiではまだ利用できません) ですが、他の#dax計算の使用例もいくつかあります。

https://twitter.com/marcorus/status/1603725770526003200

#### visual calcs🤔

どんな機能なのでしょうか。楽しみですね。

## Power BI Blogではこんな記述が🤔

https://powerbi.microsoft.com/en-us/blog/power-bi-december-2022-feature-summary/

>これらの新しい機能は非常に強力で柔軟性がありますが、正しく機能させるにはかなりの複雑さが必要です。これは、これらの機能に高い柔軟性を選択したためです。柔軟性の一部を犠牲にして、より簡単な DAX を優先する、より使いやすい関数が必要であることは認識しています。本日リリースする関数は、DAX を簡単にするという目標に向けた足がかりであり、ビルディングブロックであり、今後の基盤となります。このセクションを読んだ後、これらの関数が複雑なために自分に合わないと感じた場合は、DAX を簡単にするためにも DAX を簡単にするために作業していますのでご安心ください。

#### DAX を簡単にするという目標🤔

さきほどの**visual calcs** とは関係あるのでしょうか。楽しみですね。

### 2022-12-19 追記

@marshal さんより、**visual calcs**について語られているYouTubeを教えてもらいました。

私もこれから見てみます😀

https://twitter.com/marshal_dabao/status/1604654313254817792?s=20&t=xjhulXNHzPouVrJwftcHUg

# まとめ

本記事は2022年12月のPower BI勉強会 DAX Boot Campで  主宰のかがたさん（@PowerBIxyz）中心に勉強・研究した内容です。というかほぼ教えてもらった内容です。記事にできたのは勉強した内容のごく一部です。Power BI、DAX、Power Queryの勉強をしている方、ぜひ勉強会に参加してみてください💪

https://powerbi.connpass.com/event/267933/

結局、こうやって記事にして一番勉強になっているのは自分なんですよね。最後までお読みいただきありがとうございました。イイねお願いします～😁