【DAX】RANKX() — 順位をつける

DAX関数

このページはよく使う事例を交えながら使い方を解説します。

用途

ある計算結果に順位をつけたい時に使います。

    戻り値

    整数

    前提条件

    特になし

    構文

    =RANKX(<テーブル名>,<計算式>,※<値>,※<順序>,※<つながり>)

    ※は省略可能

    <省略可能なパラメータについて>

    現在のコンテキストで評価される計算式。
    順序DESCまたはASCを指定できます。
    つながりSKIPまたはDENSEを指定できます。

      

    例1:テーブル‘Pokemon’の列<こうげき>,<とくこう>の合計値が高い順に並び替えする。

    こうげき+とくこう_RANKX = RANKX(ALL('Pokemon'),[こうげき+とくこう],,DESC,Dense)

    <使用テーブル’Pokemon’>

    <手順>

    1.メジャーでこうげき+とくこうの合計値をだす

    こうげき+とくこう = SUM('Pokemon'[こうげき])+SUM('Pokemon'[とくこう])

    2.あとはRANKXを用いて上記に記載したメジャーを書くだけ。

    SkipとDenseの違い

    詳細を説明するより、データを見ると明らかな違いが分かります。

    下記の通りSkipとDenseの部分だけ書き換えたメジャーを2つ用意し、テーブルに表示させます。

    こうげき+とくこう_RANKX_Dense = RANKX(ALL('Pokemon'),[こうげき+とくこう],,DESC,Dense)
    こうげき+とくこう_RANKX_Skip = RANKX(ALL('Pokemon'),[こうげき+とくこう],,DESC,Skip)

    ご覧の通り、DenseはN順位だった際、次の順位はN+1で始めます

    一方でSkipは、次の順位はN+(Nのレコード数)、という計算方法でSkipの名のとおり飛ばし飛ばしで順位をつけています

    合計値”1″を消すには?

    RANKX関数を使うと下記のスクショのように合計値が1になってしまいます。
    確かにRANKXを降順にした時、合計値が最大値なので1位になってしまうのは分かるんですが…
    誤解を生みそうなので消したいですよね。

    そんな時はIF()とISINSCOPE()を使うと合計値をnullにすることができます。

    IF(ISINSCOPE(Pokemon[id]),RANKX(ALL('Pokemon'),[こうげき+とくこう],,DESC,Dense))

    メジャーをテーブルビジュアルに差し込むと以下の通りになります。

    ISINSCOPEは指定した列のレベル階層でなかった場合、FALSEを返すDAX関数です。その役割を利用して以下の例では、合計行は列[id]のレベルより1階層上なため、指定した計算式(RANKX)を計算せずnullが入る仕組みになっています。

    ISINSCOPEの詳細については以下の記事で紹介しています。

    RANKとの違い・使い分け

    違い1:RANKXは計算式(主にメジャーなど)に対して順位を付けますが、RANKは列自体に順位を付けます。

    ※RANKの使い方は以下のサイトを参照ください。

    RANK 関数 (DAX) - DAX
    詳細情報: RANK

    以下の例は[こうげき]列に対する順位づけです。特にSUM関数などの計算式を入れずに表現できました。

    こうげき_順位 = 
    RANK(
        SKIP,
        SUMMARIZE(ALL('Pokemon'),[id],[名前],[こうげき]),
        ORDERBY('Pokemon'[こうげき], DESC)
        ,,,,
    )

    違い2:ぶっちゃけ、RANKでやりたいことはRANKXでも表現することができますが、あえてRANKを使うのはパフォーマンスの速さかと思います。

    RANKXで[こうげき]の合計値に対して順位付けをし、テーブルに差し込みました。
    一目でわかる通り、ちゃーんと正確な順位付けができています。

    こうげき_順位_X = RANKX(ALLSELECTED('Pokemon'),CALCULATE(SUM(Pokemon[こうげき])),,DESC,Skip)

    ではパフォーマンスアナライザーでどちらの表の計算がはやいか見てみましょう。
    若干ではありますが上のRANKXの方が遅く、RANKの方が早く処理が終わっています

    違い3:RANKは計算のもととなる列がnullだった場合に、返す値を決めることができます。

    下記メジャーをつくり、表に差し込みました。

    こうげき_順位 = 
       RANK(
          SKIP,
          SUMMARIZE(ALLSELECTED('Pokemon'),[id],[名前],[こうげき]),
          ORDERBY('Pokemon'[こうげき], DESC),
          FIRST,
          ,,
       )
    こうげき_順位_X = RANKX(ALLSELECTED('Pokemon'),CALCULATE(SUM(Pokemon[こうげき])),,DESC,Skip)

    RANKは計算式の結果が空白だった時に返す順位をきめることができ、上記の例では空白時は一番最初にする(FIRST)という設定をしています。

    なぜALL/ALLSELECTEDを指定しないといけないのか?

    DAXの<テーブル名>はALLでくくっていますよね?それはなぜなのか。

    試しにALLを外してテーブルビジュアルに差し込んでみましょう。

    順位_ALLなし = RANKX('Pokemon',[こうげき+とくこう],,DESC,Dense)

    ??

    1ばっかりになってしまいました。

    なぜ1ばっかりになってしまったのか?テーブル各行の“裏側にあるテーブル”を想像してみるとわかりやすいです。

    まず前提として、[こうげき+とくこう]というメジャーがちゃんと合計でているのは、各行ごと(各ポケモンごと)に[こうげき]と[とくこう]を認識しているためです。

    同様に、[順位_ALLなし]メジャーも各行ごとに持っている[こうげき+とくこう]メジャーの結果に沿って順位をつけることになります。

    ただし各行ごとに絞られているため1件中の〇位となり、当然すべて1位になるわけです。

    言い換えると以下のように、各行ごとに”裏側のテーブル”をもっていると考えることもでき、この裏側のテーブルがもっている行が1件だけなのが原因です。

    本当は「各行ごとにすべての行をいれて、そのうち何位なのか?」をだしたいですよね。

    そこで使うのがALLです。

    ALLは平たくいうと、各行ごとにフィルターをしないように、返り値として全部のテーブルを返す、という関数です

    以下のようにALLを使って裏側のテーブルにすべての行を読み込ませることで、全件中の〇位が表現できるわけです。

    ちなみに、先ほどから言っている”裏側のテーブル”はPowerBIでいう”コンテキスト”と呼ばれるものであり、そのうち”行コンテキスト”というものに分類されます。

    ご興味がある方はこちらの公式ドキュメントを参照してみてください。

    DAX の数式のコンテキスト - Microsoft サポート
    コンテキストを使用すると、現在の行またはセルの選択と関連データを反映するように数式の結果を変更できる動的分析を実行できます。 コンテキストを理解し、コンテキストを効果的に使用することは、高性能な数式、動的分析、および数式の問題のトラブルシュ...

    また、ALLの代わりにALLSELECTEDという関数も用いるときがあります。その違いについては近日紹介しようと思います。

    参考