DAXのCount系関数で混乱したのでまとめた

# はじめに
はじめまして。ATLのヴィリアスです。

Power BI初心者の私が、DAXのCount系関数で悩みまくり、
Microsoft Docsを読んでもよく分からなかったため、色々検証してみました。
備忘録として、私が学んだことをここに残します。

**※この記事は、Power BIでの動作がメインです。**

**※免責事項(必ず、ご一読ください)**
　　本記事の情報により生じた、いかなる損害や損失についても、当社は一切の責任を負いかねます。
　　また、誤情報が入り込んだり、情報が古くなったりすることもありますので、
　　必ずしも正確性を保証するものではありません。ご了承ください。



# 検証に使うテーブル
Count系関数の検証にあたって、次の2つのテーブルをExcelで用意しました。

テーブル名: **各社の在庫**

| 会社 | 製品 | 在庫数 |
|:--|:--|--:|
| 〇〇社  | マウス  | 1  |
| 〇〇社  | モニター  | 3  |
| ✕✕社  | マウス  | 10  |
| ○✕社  | (空文字)  | 1  |
| △△社  | キーボード  | 3  |
| △△社  | ヘッドホン  |  |
| □□社  |   | 0  |
| □□社  | マウス  | 3000  |
| □□社  | モニター  |   |

テーブル名: **ブール**

| No | ブール値 |
|--:|:-:|
| 1  | True|
| 2  | False  |
| 3  | False  |
| 4  | True  |
| 5  | True  |



# Count系関数の一覧
まず最初に、Count系関数は**8種類**あります。

| 関数 | 説明 |
|:--|:--|
| [Count](#count)  | **値 (数値, 日付, 文字列)** が入っている行数をカウント。<br>**空文字**もカウントする。<br>空白はカウントしない。<br>**ブール値はカウントできない。**<br><br>**実はCountXの簡易版。** |
| [CountA](#counta-count-all) | **Count All**<br><br>**空白ではない**行数をすべてカウント。<br>空文字、**ブール値**もカウントする。<br> |
| [CountX](#countx-count-expression) | **Count Expression**<br><br>**Countの色々できる版。**<br>**値 (数値, 日付, 文字列)** が入っている行数をカウント。<br>第1引数に**テーブル**を取る。(フィルターしたテーブルなどを使うのが基本)<br>第2引数に**式**(列)を取る。<br>空白はカウントしない。<br>**ブール値はカウントできない。** |
| [CountAX](#countax-count-all-expression) | **Count All Expression**<br><br>**CountAとCountXが合体したもの。** |
| [CountRows](#countrows) | テーブルの行数を数える。空白もカウント。 |
| [CountBlank](#countblank) | 列の空白をカウント。**空文字もカウント。** |
| [DistinctCount](#distinctcount) | 重複した値は加算せずにカウント。<br>**空白と空文字は別としてカウント。** |
| [DistinctCountNoBlank](#distinctcountnoblank) | 重複した値は加算せずにカウント。<br>空白はカウントしない。<br>**空文字はカウント。** |

**Count, CountA, CountX, CountAX の4つの違いが理解できれば、もう何も怖くないです。**
では、それぞれ見ていきましょう。



# Count
https://docs.microsoft.com/ja-jp/dax/count-function-dax

基本中の基本、`Count`です。

これは、 **値 (数値, 日付, 文字列(空文字も含む))** をカウントします。
空白はカウントしません。
また、**ブール値に対して`Count`を使うとエラーになります。**

まず **[在庫数]** をカウントしたい場合は、次のように記述します。


```:DAX
/* COUNT(<列>) */

Count = COUNT('各社の在庫'[在庫数])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/df25dd3b-a630-11f7-3e26-ba4ae9fa495b.png">
全9行のうち、空白はカウントせずに在庫数が記入されている **7行** をしっかりカウントできていますね。

では次に **空文字**がカウントされるか、**[製品]** で確認します。

```:DAX
Count = COUNT('各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/6ddd4c59-86ec-2a22-28e2-2710f9fa5486.png">
**○✕社の [製品] は空文字**ですが、しっかりカウントされて結果は **8** になりました。

では、**ブール値**に`Count`を使うとどうなるのか。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/13e9673e-90b7-fcf5-8059-7329f947a4b9.png">
**エラーが出ます。**

以下、エラーメッセージの一部です。
> メジャー 'ブール'[Count] での計算エラー: 関数 COUNT は、数値または日付に評価される引数を受け取ります。型 Boolean の値は操作できません。

「**ブール値をカウントしたいよ！**」という場合は、次の`CountA`を使います。



# CountA (Count All)
https://docs.microsoft.com/ja-jp/dax/counta-function-dax

`CountA`は **Count All** です。**すべて(All)をカウント**します。

`CountA`は、`Count`とほぼ変わりません。**(Power BIでは)**
ただ先ほども言ったように、`CountA`は **True や False のブール値をカウントすることができます。**

```:DAX
/* COUNTA(<列>) */

CountA = COUNTA('ブール'[ブール値])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/59a7e456-d9bb-9545-c6b8-cc0763e6e251.png">
このように、ブール値がカウントできます。

##CountAの補足
ちなみに、**Excel**に絡めて`Count`と`CountA`の違いを厳密に言うと、
まず`CountA`は**対象列のセルの型に、数値やら文字列やらが混ざっていても、何でもカウントできます**。
一方で`Count`は数値のカウントがメインとなるため、**文字列が混ざるとそれはカウントされません。**

次のExcelのスクリーンショットを見てください。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/2117a907-aa4a-6541-2b4c-54bdb3c1970d.png">
 C4 と D4 にはそれぞれ、`Count`と`CountA`を使って A2:A7 をカウントした値が入っています。
そして、 A2:A7 には「1, 2, 3, 6」の**数値**と「四, 五」の**文字列**が入っています。

`Count`は**数値のカウントがメイン**であるため、「四, 五」の文字列はカウントしません。したがって結果は **4** となります。
`CountA`は**何でもカウントする**ため、数値だろうが文字列だろうが関係ありません。結果は **6** です。

ではなぜ冒頭で私は、

>`CountA`は、`Count`とほぼ変わりません。**(Power BIでは)**

と言ったのか。
それはPower BIでは、**列の型は1つに決定されるからです。**

Excelでは、列の各セルに対して任意の型を設定できます。(例: A列の1行目の型は文字列、2行目は数値、3行目は文字列)
しかし、Power BIではそれができないのです。
1列目の型を数値とすれば1列目の全行の型が数値になりますし、文字列とすれば文字列になります。

つまりPower BIでは1列の中に色々な型が存在することがないため、
**ブール値を除けば`CountA`も`Count`も同じように値をカウントできます。**

そのため、「**`CountA`と`Count`はほぼ変わらない**」としています。



# CountX (Count Expression)
https://docs.microsoft.com/ja-jp/dax/countx-function-dax

`CountX`は **Count Expression** です。**式(Expression)を使ってカウント**します。

`CountX`は、**第1引数にテーブル**を、**第2引数に式**(というか列)を取ります。
例えば、**'各社の在庫'** テーブルの **[在庫数]** をカウントする場合、次のように記述します。

```:DAX
/* COUNTX(<テーブル>, <式>) */

CountX = COUNTX('各社の在庫', '各社の在庫'[在庫数])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/65b186c9-df40-b786-178c-ef596fcf2c05.png">
結果は **7** になります。

この結果、ついさっき見た覚えがありませんか？
実はこれ、**`Count`で [在庫数] をカウントしているのと変わらない**んですよね。

「じゃあ、`Count`でいいじゃん。なんで`CountX`が存在するの？」
その答えは`CountX`の **第1引数は、[FILTER](https://docs.microsoft.com/ja-jp/dax/filter-function-dax)などで加工したテーブルを取ることができる**からです。

例えば「**'各社の在庫' テーブルの中で、在庫数が10以上の製品をカウントしたいよ！**」というときは、次のように記述します。

```:DAX
CountX = COUNTX(FILTER('各社の在庫', '各社の在庫'[在庫数]>=10), '各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/a9041326-16ce-eea2-2193-f95edd6c02ed.png">
第1引数で**「在庫数が10以上」にフィルターされるテーブル**を取っています。
そして「在庫数が10以上」なのは、**✕✕社のマウス**と**□□社のマウス**であるため、結果は **2** となります。
このように`CountX`は、`Count`よりも**高度なカウントができます。**

もちろん`Count`と同様、**空文字もカウントできます。**

```:DAX
CountX = COUNTX(FILTER('各社の在庫', '各社の在庫'[在庫数]==1), '各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/ebf1f4f1-12fd-9cce-ee3a-c431d62c3bee.png">
○✕社の [製品] は空文字ですが、カウントできています。


# CountAX (Count All Expression)
https://docs.microsoft.com/ja-jp/dax/countax-function-dax

`CountAX`は **Count All Expression** です。**すべて(All)を式(Expression)を使ってカウント**します。

`CountAX`は、**`CountA`と`CountX`が合体したものです。**
そのため、**第1引数にフィルター済みテーブルを取ったり、ブール値をカウントしたりすることができます。**

```:DAX
/* COUNTAX(<テーブル>, <式>) */

CountAX = COUNTAX(FILTER('ブール', 'ブール'[No]<=3), 'ブール'[ブール値])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/517d20f8-e6e6-2fd4-582d-1b1d6f5655b6.png">
ここでは「**Noが3以下**」にフィルターしているため、結果は **3** となります。

その他については`CountA`と`CountX`で説明しているので省略します。



# CountRows
https://docs.microsoft.com/ja-jp/dax/countrows-function-dax

`CountRows`は**テーブルの行数(Rows)をカウント**します。

```:DAX
/* COUTROWS([<テーブル>]) */

CountRows = COUNTROWS('各社の在庫')
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/0378a04d-745c-2c61-737c-5bb1feb92d01.png">

**簡単ですね！**

また、**引数が空でも大丈夫です。**
その場合、メジャーが存在するテーブル(ホームテーブル)の行数がカウントされます。

```:DAX
CountRows = COUNTROWS()
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/c06de21b-f7a3-8a43-3947-352f5ef56b8f.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/aea1e225-24ec-7ce6-6456-ec6b44e0cba7.png">

このように、メジャー`CountRows`は **'ブール'** テーブル下にあるため、**引数をなしにすると 'ブール' テーブルの行数 5 を取得できます。**



# CountBlank
https://docs.microsoft.com/ja-jp/dax/countblank-function-dax

`CountBlank`は**空白(Blank)をカウント**します。**空文字もカウントします。**

```:DAX
/* COUNTBLANK(<列>) */

CountBlank = COUNTBLANK('各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/037d2cd1-d79e-cd69-ed3a-b2d450b93e3c.png">
○✕社の空文字も、□□社の空白もカウントし、結果は **2** になりました。


# DistinctCount
https://docs.microsoft.com/ja-jp/dax/distinctcount-function-dax

`DistinctCount`は**重複する値を加算せず(Distinct)にカウント**します。
**空白と空文字は 別物** としてカウントされます。

```:DAX
/* DISTINCTCOUNT(<列>) */

DistinctCount = DISTINCTCOUNT('各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/aef27c12-4ade-a72b-6c80-fff0c9f29065.png">
**[製品]** には「マウス, モニター, キーボード, ヘッドホン」の4つに加えて、**空文字と空白の2つ**があります。
したがって、結果は **6** となります。

**重複する値が1つとしてカウントされている**のが分かりますね。


# DistinctCountNoBlank
https://docs.microsoft.com/ja-jp/dax/distinctcountnoblank-function-dax

`DistinctCountNoBlank`は`DistinctCount`が**空白を無視(NoBlank)する**ようにしたものです。
**空文字は無視されません。**

```:DAX
/*  DISTINCTCOUNTNOBLANK(<列>)*/

DistinctCountNoBlank = DISTINCTCOUNTNOBLANK('各社の在庫'[製品])
```

**結果**
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/9b239b43-3709-4eb0-394e-b64277f2b3de.png">
`DistinctCount`では 6 でしたが、`DistinctCountNoBlank`は**□□社の空白を無視する**ため **5** となります。
また、**○✕社の空文字は無視されていない**のも確認できます。

#おわりに
自分なりにCount系関数をまとめてみましたが、いかがだったでしょうか。
この記事が、私と同じように悩んでいる方の助けになれば幸いです。

それではまた、次の記事で。


# 参考にしたもの
- [COUNT, COUNTA, COUNTAX, COUNTX - DAX Guide (YouTube)](https://youtu.be/9waZ7vMJnSo)
- [DAX PowerBI: COUNT, COUNTA, COUNTX, COUNTAX, DISTINCTCOUNT and COUNTROWS (YouTube)](https://youtu.be/y2GPFFOW3fA)