# 機能設計書 19-tally

## 概要

本ドキュメントは、dplyrパッケージの`tally()`関数に関する機能設計書である。この関数は、グループ内の観測数を集計するための低レベルヘルパー関数であり、`count()`の基盤となる機能を提供する。

### 本機能の処理概要

`tally()`関数は、データフレームの現在のグループ構造に基づいて観測数をカウントし、集計結果を返す。`summarise(n = n())`と同等の処理を行うが、より簡潔に記述できる。`count()`との主な違いは、`tally()`はグループ化を行わず、既存のグループ構造をそのまま使用する点である。

**業務上の目的・背景**：データ分析において、既にグループ化されたデータの件数を素早く確認したい場面がある。`tally()`は、`group_by()`でグループ化済みのデータに対して、追加のグループ化なしでカウントを実行するための簡潔な方法を提供する。これは`count()`の内部実装としても使用されている。

**機能の利用シーン**：
- 既にグループ化されたデータの件数確認
- パイプライン内での簡潔なカウント処理
- 重み付きカウント（既に集計済みのデータを再集計）
- `count()`を使わずにより細かい制御が必要な場合

**主要な処理内容**：
1. `UseMethod()`によるメソッドディスパッチ
2. `check_n_name()`でカウント列名を決定（重複回避）
3. `tally_n()`でカウント式を生成
4. `summarise()`を呼び出してカウント実行
5. `sort = TRUE`の場合、カウントの降順でソート

**関連システム・外部連携**：`count()`から内部的に呼び出される。`summarise()`を内部で使用。`add_tally()`はmutate版。

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

## 関連画面

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

## 機能種別

データ集計処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | data.frame | Yes | 操作対象のデータフレーム（通常はgrouped_df） | data.frameまたはその派生クラスであること |
| wt | data-masking | No | 重み変数（NULLの場合は行数をカウント） | 数値型であること |
| sort | logical | No | TRUEの場合、カウントの降順でソート（デフォルト: FALSE） | 論理値 |
| name | character | No | カウント列の名前（デフォルト: "n"） | 文字列 |

### 入力データソース

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

## 出力仕様

### 出力データ

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

### 出力先

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

### 出力特性

- `summarise()`と同様の出力構造（グループごとに1行）
- グループ化されていないデータの場合は全体で1行
- カウント列の名前は`name`引数で指定可能

## 処理フロー

### 処理シーケンス

```
1. UseMethod("tally")によるメソッドディスパッチ
   └─ xの型に応じた適切なメソッドが呼び出される
2. check_n_name()でカウント列名を決定
   └─ 既存の列名と重複しないよう調整
3. compat_wt()でwt引数の互換性チェック
4. tally_n()でカウント式を生成
   └─ wt指定時はsum(wt)、そうでなければn()
5. summarise()を呼び出し
   └─ dplyr.summarise.informを一時的にFALSEに設定
6. sort = TRUEの場合
   └─ arrange(desc(n))でソート
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[tally呼び出し]
    B --> C[UseMethodでディスパッチ]
    C --> D[check_n_nameで列名決定]
    D --> E[compat_wtで互換性チェック]
    E --> F[tally_nでカウント式生成]
    F --> G{wt引数あり?}
    G -->|Yes| H[sum wt na.rm=TRUE]
    G -->|No| I[n]
    H --> J[summarise実行]
    I --> J
    J --> K{sort = TRUE?}
    K -->|Yes| L[arrange desc n]
    K -->|No| M[そのまま返却]
    L --> N[結果を返却]
    M --> N
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | グループ化なし | tally()はグループ化を行わず、既存グループをそのまま使用 | 常に適用 |
| BR-02 | 重み付きカウント | wt指定時はsum(wt, na.rm = TRUE)でカウント | wtが指定された場合 |
| BR-03 | カウント列名の自動調整 | "n"が既に存在する場合は"nn"、"nnn"...と調整 | name未指定で"n"が存在 |
| BR-04 | 情報メッセージ抑制 | summarise時のグループメッセージを抑制 | 常に適用 |

### 計算ロジック

**カウント式の生成（tally_n関数）**:
```r
tally_n <- function(x, wt) {
  if (quo_is_null(wt)) {
    expr(dplyr::n())
  } else {
    expr(base::sum(!!wt, na.rm = TRUE))
  }
}
```

**列名の決定（n_name関数）**:
```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()によるエラー |

### リトライ仕様

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

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

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

## パフォーマンス要件

- 内部で`summarise()`を使用するため、それと同等の計算量
- `count()`よりもオーバーヘッドが少ない（グループ化処理がない）

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

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

## 備考

- `tally()`は`count()`の低レベル版であり、グループ化を行わない
- `starwars |> tally()`は全体の行数を返す
- `starwars |> group_by(species) |> tally()`は種別ごとの件数を返す
- `add_tally()`はカウントを既存データに追加する（mutate版）

---

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

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

### 推奨読解順序

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

`tally()`は`summarise()`のラッパーであり、出力構造は`summarise()`と同一。

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

**読解のコツ**: `tally()`は`summarise(n = n())`の短縮形。`count()`から内部的に呼び出される。

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

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

**主要処理フロー**:
1. **122行目**: `check_n_name()`でカウント列名を決定
2. **126行目**: `compat_wt()`でwt引数の互換性チェック
3. **127行目**: `tally_n()`でカウント式を生成
4. **129-130行目**: `summarise()`を実行（メッセージ抑制）
5. **132-136行目**: `sort = TRUE`の場合はソート

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

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

**主要処理フロー（tally_n）**:
- **238行目**: wtがNULLならn()を返す
- **241行目**: wtが指定されていればsum(wt, na.rm = TRUE)を返す

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

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

### データフロー図

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

x (grouped_df) ────────┬─ check_n_name() ────────────── 列名(name)
                       │         │
wt (重み) ─────────────┤         │
                       │         ▼
sort ──────────────────┤   tally_n() ─────────────────── カウント式
                       │         │
name ──────────────────┘         │
                                 ▼
                    local_options(inform = FALSE)
                                 │
                                 ▼
                           summarise()
                                 │
                                 ▼
                    arrange(desc(n)) [sort時]
                                 │
                                 ▼
                       [集約されたdata.frame]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| count-tally.R | `R/count-tally.R` | ソース | tally関数の実装 |
| summarise.R | `R/summarise.R` | ソース | summarise（内部で使用） |
| context.R | `R/context.R` | ソース | n()関数の定義 |
| test-count-tally.R | `tests/testthat/test-count-tally.R` | テスト | tallyのユニットテスト |
