# 機能設計書 18-count

## 概要

本ドキュメントは、dplyrパッケージの`count()`関数に関する機能設計書である。この関数は、データフレーム内の一意な値の組み合わせの出現回数を素早くカウントするための機能を提供する。

### 本機能の処理概要

`count()`関数は、指定した変数の一意な値の組み合わせごとに観測数をカウントし、新しいデータフレームを返す。これは`group_by(...) |> summarise(n = n())`と概ね同等だが、より簡潔に記述できる。オプションで重み付きカウントも可能。

**業務上の目的・背景**：データ分析において、カテゴリ変数の分布を把握することは最も基本的な操作の一つである。顧客の属性分布、製品カテゴリ別の件数、エラー種別の発生頻度など、「何が何件あるか」という情報はデータ理解の第一歩となる。`count()`は、このような頻度集計を最小限のコードで実現する便利関数である。

**機能の利用シーン**：
- カテゴリ変数の分布確認
- 重複データの検出と件数確認
- クロス集計（複数変数の組み合わせ）
- 重み付きカウント（既に集計済みのデータを再集計）
- データ品質チェック（欠損値や異常値の件数確認）

**主要な処理内容**：
1. `UseMethod()`によるメソッドディスパッチ
2. 引数が指定されている場合、`group_by()`で一時的にグループ化（`.add = TRUE`）
3. `tally()`を呼び出してカウントを実行
4. `wt`引数が指定されている場合は`sum(wt)`、そうでなければ`n()`を使用
5. `sort = TRUE`の場合、カウントの降順でソート
6. `dplyr_reconstruct()`で元のグループ構造を復元（一時的グループ化）

**関連システム・外部連携**：`tally()`は`count()`の低レベルヘルパー。`group_by()`と`summarise()`を内部で使用。`add_count()`はmutate版。

**権限による制御**：本機能はRパッケージの一部であり、特定の権限制御は存在しない。関数を呼び出す全てのユーザーが同じ機能を利用可能である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本機能はRライブラリ（dplyr）のため画面は存在しない |

## 機能種別

データ集計処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | data.frame | Yes | 操作対象のデータフレーム | data.frameまたはその派生クラスであること |
| ... | data-masking | No | グループ化に使用する変数（計算式も可） | - |
| wt | data-masking | No | 重み変数（NULLの場合は行数をカウント） | 数値型であること |
| sort | logical | No | TRUEの場合、カウントの降順でソート（デフォルト: FALSE） | 論理値 |
| name | character | No | カウント列の名前（デフォルト: "n"） | 文字列 |
| .drop | logical | No | ファクターの未使用レベルを除外するか（デフォルト: TRUE） | 論理値 |

### 入力データソース

- Rセッション内のdata.frame、tibble、grouped_df、またはその派生クラスのオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | data.frame | グループ化変数とカウント列を含むデータフレーム |

### 出力先

- Rセッション内のオブジェクトとして返却

### 出力特性

- 入力と同じ型のオブジェクトを返す
- グループ化は一時的（transient）で、出力は入力と同じグループ構造を持つ
- カウント列の名前は`name`引数で指定可能（デフォルト"n"、既存の場合は"nn"、"nnn"...）

## 処理フロー

### 処理シーケンス

```
1. UseMethod("count")によるメソッドディスパッチ
   └─ xの型に応じた適切なメソッドが呼び出される
2. グループ化変数がある場合
   └─ group_by(x, ..., .add = TRUE, .drop = .drop)で一時グループ化
3. wt引数の互換性チェック
   └─ wt = n()は非推奨（1.0.1以降）
4. tally()を呼び出し
   └─ カウント実行
5. グループ構造の復元
   └─ dplyr_reconstruct()で元の構造に戻す
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[count呼び出し]
    B --> C[UseMethodでディスパッチ]
    C --> D{...引数あり?}
    D -->|Yes| E[group_by .add=TRUE]
    D -->|No| F[そのまま進行]
    E --> G[tally呼び出し]
    F --> G
    G --> H{wt引数あり?}
    H -->|Yes| I[sum(wt)で集計]
    H -->|No| J[n()で集計]
    I --> K[summarise実行]
    J --> K
    K --> L{sort = TRUE?}
    L -->|Yes| M[desc(n)でソート]
    L -->|No| N[そのまま進行]
    M --> O[dplyr_reconstruct]
    N --> O
    O --> P[結果を返却]
    P --> Q[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 一時的グループ化 | count()によるグループ化は一時的で、出力は入力と同じグループ構造 | 常に適用 |
| BR-02 | 追加グループ化 | ...で指定した変数は既存グループに追加される（.add = TRUE） | ...が指定された場合 |
| BR-03 | カウント列名の自動調整 | "n"が既に存在する場合は"nn"、"nnn"...と調整 | name未指定で"n"が存在 |
| BR-04 | 重み付きカウント | wt指定時はsum(wt, na.rm = TRUE)でカウント | wtが指定された場合 |
| BR-05 | ファクターレベル | .drop=FALSEでデータにないファクターレベルも0件として表示 | .drop=FALSE |

### 計算ロジック

**カウント式の生成**:
```r
if (is.null(wt)) {
  n_expr = expr(dplyr::n())
} else {
  n_expr = expr(base::sum(!!wt, na.rm = TRUE))
}
```

**列名の決定**:
```r
n_name <- function(x) {
  name <- "n"
  while (name %in% x) {
    name <- paste0("n", name)
  }
  name
}
```

## データベース操作仕様

該当なし（本機能はインメモリのデータフレーム操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 非推奨警告 | wt = n()が使用された場合 | lifecycle::deprecate_warn（wtを省略するよう案内） |
| - | 型エラー | nameが文字列でない場合 | check_string()によるエラー |

### リトライ仕様

該当なし（インタラクティブな関数呼び出しのため）

## トランザクション仕様

該当なし（本機能はデータベーストランザクションを使用しない）

## パフォーマンス要件

- 内部で`group_by()`と`summarise()`を使用するため、それらと同等の計算量
- O(n log n)（グループ化のソートが必要）

## セキュリティ考慮事項

- 本機能は純粋なデータ変換操作であり、特別なセキュリティ考慮事項はない

## 備考

- `count(a, b)`は`group_by(a, b) |> summarise(n = n())`とほぼ同等
- ただし`count()`は一時的グループ化のため、元のグループ構造が保持される
- `add_count()`はカウントを既存データに追加する（mutate版）
- `tally()`は`count()`の低レベル版（グループ化なしでカウントのみ）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

`count()`は内部で`group_by()`と`tally()`を組み合わせて使用する。出力は入力と同じグループ構造を持つ。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | count-tally.R | `R/count-tally.R` | count/tallyの関係性（1-11行目） |

**読解のコツ**: `count()`は`group_by() + tally()`のラッパー。`tally()`は`summarise(n = n())`のラッパー。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | count-tally.R | `R/count-tally.R` | `count()`関数のジェネリック定義（83-85行目） |
| 2-2 | count-tally.R | `R/count-tally.R` | `count.data.frame()`（89-112行目） |

**主要処理フロー**:
1. **99-103行目**: `...`がある場合は`group_by()`で一時グループ化
2. **105行目**: `compat_wt()`でwt引数の互換性チェック
3. **106行目**: `tally()`を呼び出し
4. **109行目**: `dplyr_reconstruct()`で元構造を復元

#### Step 3: tally実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | count-tally.R | `R/count-tally.R` | `tally()`関数（116-137行目） |
| 3-2 | count-tally.R | `R/count-tally.R` | `tally_n()`ヘルパー（237-243行目） |

**主要処理フロー（tally.data.frame）**:
- **122行目**: `check_n_name()`でカウント列名を決定
- **127行目**: `tally_n()`でカウント式を生成
- **130行目**: `summarise()`を実行
- **132-136行目**: `sort = TRUE`の場合はソート

#### Step 4: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | count-tally.R | `R/count-tally.R` | `check_n_name()`関数（264-284行目） |
| 4-2 | count-tally.R | `R/count-tally.R` | `n_name()`関数（286-293行目） |
| 4-3 | count-tally.R | `R/count-tally.R` | `compat_wt()`関数（245-262行目） |

### プログラム呼び出し階層図

```
count(x, ..., wt, sort, name, .drop)
    │
    ├─ UseMethod("count")
    │      │
    │      └─ count.data.frame()
    │             │
    │             ├─ group_by(..., .add = TRUE, .drop) [条件付き]
    │             │
    │             ├─ compat_wt() [wt互換性チェック]
    │             │
    │             ├─ tally()
    │             │      │
    │             │      ├─ check_n_name() [列名決定]
    │             │      │      │
    │             │      │      └─ n_name() [重複回避]
    │             │      │
    │             │      ├─ tally_n() [カウント式生成]
    │             │      │
    │             │      ├─ summarise(n = !!)
    │             │      │
    │             │      └─ arrange(desc(n)) [条件付き]
    │             │
    │             └─ dplyr_reconstruct() [構造復元]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

x ─────────────────────┬─ group_by(..., .add=TRUE) ──── grouped_x
                       │         │
... (グループ変数) ────┤         │
                       │         ▼
wt (重み) ─────────────┤   tally()
                       │         │
sort ──────────────────┤         ├─ tally_n() [n() or sum(wt)]
                       │         │
name ──────────────────┤         ├─ summarise()
                       │         │
.drop ─────────────────┘         └─ arrange() [sort時]
                                         │
                                         ▼
                                 dplyr_reconstruct()
                                         │
                                         ▼
                              [カウント結果data.frame]
                              (元と同じグループ構造)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| count-tally.R | `R/count-tally.R` | ソース | count/tally/add_count/add_tallyの実装 |
| group-by.R | `R/group-by.R` | ソース | group_by（内部で使用） |
| summarise.R | `R/summarise.R` | ソース | summarise（内部で使用） |
| test-count-tally.R | `tests/testthat/test-count-tally.R` | テスト | count/tallyのユニットテスト |
