# 機能設計書 20-add_count

## 概要

本ドキュメントは、dplyrパッケージの`add_count()`関数に関する機能設計書である。この関数は、`count()`と同様にカウントを計算するが、`summarise()`ではなく`mutate()`を使用して既存のデータフレームにカウント列を追加する機能を提供する。

### 本機能の処理概要

`add_count()`関数は、指定した変数のグループごとの観測数をカウントし、その結果を新しい列として元のデータフレームに追加する。`count()`がグループごとに1行に集約するのに対し、`add_count()`は元の行数を維持したまま、各行にそのグループの件数を付与する。

**業務上の目的・背景**：データ分析において、グループの件数情報を元データに保持したまま分析を続けたい場面がある。例えば、カテゴリごとの件数が一定以上のデータのみをフィルタリングしたい場合や、グループサイズに基づく計算（割合、ランキングなど）を行いたい場合である。`add_count()`は、集約せずにカウント情報を追加することで、このようなユースケースを効率的にサポートする。

**機能の利用シーン**：
- グループの件数に基づくフィルタリング（例：5件以上のグループのみ抽出）
- グループ内での割合計算（各行の値 / グループ合計）
- データの重複件数の確認（同じ値が何件あるか）
- グループサイズによる条件分岐

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

**関連システム・外部連携**：`add_tally()`は`add_count()`の低レベルヘルパー。`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 | deprecated | No | 非推奨（効果なし） | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | data.frame | 元のデータにカウント列が追加されたデータフレーム |

### 出力先

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

### 出力特性

- 入力と同じ型のオブジェクトを返す
- 行数は入力と同じ（集約されない）
- 新しいカウント列が追加される
- グループ化は一時的で、出力は入力と同じグループ構造を持つ

## 処理フロー

### 処理シーケンス

```
1. UseMethod("add_count")によるメソッドディスパッチ
   └─ xの型に応じた適切なメソッドが呼び出される
2. .drop引数のチェック
   └─ 指定されている場合はdeprecate_stop
3. グループ化変数がある場合
   └─ group_by(x, ..., .add = TRUE)で一時グループ化
4. add_tally()を呼び出し
   └─ mutate()でカウント列を追加
5. データ構造の復元
   └─ dplyr_reconstruct()で元の構造に戻す
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[add_count呼び出し]
    B --> C[UseMethodでディスパッチ]
    C --> D{.drop引数あり?}
    D -->|Yes| E[deprecate_stop]
    D -->|No| F{...引数あり?}
    F -->|Yes| G[group_by .add=TRUE]
    F -->|No| H[そのまま進行]
    G --> I[add_tally呼び出し]
    H --> I
    I --> J[check_n_nameで列名決定]
    J --> K{wt引数あり?}
    K -->|Yes| L[sum wt na.rm=TRUE]
    K -->|No| M[n]
    L --> N[mutate実行]
    M --> N
    N --> O{sort = TRUE?}
    O -->|Yes| P[arrange desc n]
    O -->|No| Q[そのまま進行]
    P --> R[dplyr_reconstruct]
    Q --> R
    R --> S[結果を返却]
    S --> T[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 行数維持 | add_count()は元の行数を維持する（mutate版） | 常に適用 |
| BR-02 | 一時的グループ化 | add_count()によるグループ化は一時的で、出力は入力と同じグループ構造 | 常に適用 |
| BR-03 | 追加グループ化 | ...で指定した変数は既存グループに追加される（.add = TRUE） | ...が指定された場合 |
| BR-04 | .drop非推奨 | .drop引数は廃止されており、使用するとエラー | .dropが指定された場合 |

### 計算ロジック

**count()との違い**:
```r
# count(): 集約版
df |> count(species)
# グループごとに1行、n列を含む

# add_count(): mutate版
df |> add_count(species)
# 元の行数を維持、n列が追加される
```

**内部処理**:
```r
add_count_impl <- function(x, ..., wt, sort, name, .drop) {
  if (!missing(...)) {
    out <- group_by(x, ..., .add = TRUE)
  } else {
    out <- x
  }
  add_tally(out, wt = !!wt, sort = sort, name = name)
}
```

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 非推奨エラー | .drop引数が使用された場合 | lifecycle::deprecate_stop（.dropを削除するよう案内） |
| - | 非推奨警告 | wt = n()が使用された場合 | lifecycle::deprecate_warn（wtを省略するよう案内） |

### リトライ仕様

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

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

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

## パフォーマンス要件

- 内部で`group_by()`と`mutate()`を使用するため、それらと同等の計算量
- `count()`より出力データが大きい（行数が維持される）ため、メモリ使用量に注意

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

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

## 備考

- `add_count()`は`count()`のmutate版
- `add_tally()`は`add_count()`の低レベル版（グループ化なしでカウント追加のみ）
- `.drop`引数はdplyr 1.0.0で廃止された（効果がなかったため）
- グループサイズに基づくフィルタリングに便利: `df |> add_count(g) |> filter(n > 5)`

---

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

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

### 推奨読解順序

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

`add_count()`は`mutate()`のラッパーであり、出力は入力と同じ行数を持つ。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | count-tally.R | `R/count-tally.R` | add_count()の位置づけ（10-13行目） |

**読解のコツ**: `add_count()`は`count()`の対になる関数。`summarise()`の代わりに`mutate()`を使用する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | count-tally.R | `R/count-tally.R` | `add_count()`関数のジェネリック定義（141-150行目） |
| 2-2 | count-tally.R | `R/count-tally.R` | `add_count.data.frame()`（172-189行目） |
| 2-3 | count-tally.R | `R/count-tally.R` | `add_count_impl()`（191-215行目） |

**主要処理フロー**:
1. **180-187行目**: `add_count_impl()`を呼び出し
2. **188行目**: `dplyr_reconstruct()`で元構造を復元
3. **201-203行目**: `.drop`引数のチェック（非推奨エラー）
4. **207-211行目**: グループ化処理
5. **214行目**: `add_tally()`を呼び出し

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | count-tally.R | `R/count-tally.R` | `add_tally()`関数（219-233行目） |

**主要処理フロー（add_tally）**:
- **220行目**: `check_n_name()`でカウント列名を決定
- **224行目**: `compat_wt()`でwt引数の互換性チェック
- **225行目**: `tally_n()`でカウント式を生成
- **226行目**: `mutate()`を実行
- **228-232行目**: `sort = TRUE`の場合はソート

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

```
add_count(x, ..., wt, sort, name, .drop)
    │
    ├─ UseMethod("add_count")
    │      │
    │      ├─ add_count.default()
    │      │      │
    │      │      └─ add_count_impl()
    │      │
    │      └─ add_count.data.frame()
    │             │
    │             ├─ add_count_impl()
    │             │      │
    │             │      ├─ lifecycle::deprecate_stop() [.drop時]
    │             │      │
    │             │      ├─ group_by(..., .add = TRUE) [条件付き]
    │             │      │
    │             │      └─ add_tally()
    │             │             │
    │             │             ├─ check_n_name() [列名決定]
    │             │             │
    │             │             ├─ compat_wt() [wt互換性チェック]
    │             │             │
    │             │             ├─ tally_n() [カウント式生成]
    │             │             │
    │             │             ├─ mutate(n = !!)
    │             │             │
    │             │             └─ arrange(desc(n)) [条件付き]
    │             │
    │             └─ dplyr_reconstruct() [構造復元]
```

### データフロー図

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

x ─────────────────────┬─ add_count_impl() ───────────── grouped_x
                       │         │
... (グループ変数) ────┤         ├─ group_by(.add=TRUE)
                       │         │
wt (重み) ─────────────┤         ▼
                       │   add_tally()
sort ──────────────────┤         │
                       │         ├─ tally_n() [n() or sum(wt)]
name ──────────────────┘         │
                                 ├─ mutate()
                                 │
                                 └─ arrange() [sort時]
                                         │
                                         ▼
                                 dplyr_reconstruct()
                                         │
                                         ▼
                            [元データ + n列]
                            (元と同じ行数・グループ構造)
```

### 関連ファイル一覧

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