# 機能設計書 44-case_when

## 概要

本ドキュメントは、dplyrパッケージの`case_when()`関数の機能設計について記述する。`case_when()`は複数条件に基づく一般的なベクトル化された条件分岐を提供し、SQLのCASE WHEN文に類似した機能を持つ。

### 本機能の処理概要

`case_when()`は、複数の条件式と対応する値をフォーミュラ形式（`condition ~ value`）で受け取り、各要素に対して最初に一致した条件の値を返すベクトル化された条件分岐関数である。条件は順次評価され、最初に`TRUE`となった条件に対応する値が使用される。どの条件にも一致しない場合は`.default`の値が使用される。

**業務上の目的・背景**：データ変換において、2つ以上の条件に基づいて値を決定する必要がある場面は頻繁に発生する。ネストした`if_else()`は可読性が低く、保守が困難になる。`case_when()`は、SQLのCASE WHEN文のように、複数条件を宣言的かつ可読性高く記述できるため、複雑な条件ロジックの実装が容易になる。

**機能の利用シーン**：
- カテゴリ変数の作成（例：年齢を「若年」「中年」「高齢」に分類）
- スコアリングロジックの実装
- 複数条件に基づくデータのラベリング
- 条件付きのデータ変換

**主要な処理内容**：
1. フォーミュラを解析してLHS（条件）とRHS（値）を抽出する
2. LHSの共通サイズを決定する
3. RHSの共通型を決定する
4. 各条件を順次評価し、最初に一致した条件の値を選択する
5. 一致しない場合は`.default`値を使用する

**関連システム・外部連携**：vctrsパッケージの`vec_case_when()`を内部で使用する。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはRライブラリであり、ユーザーインターフェース画面は存在しない |

## 機能種別

計算処理 / データ変換 / 条件分岐

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| ... | formula | Yes | `condition ~ value`形式の二項フォーミュラ。NULLは無視される | 少なくとも1つのフォーミュラが必要 |
| .default | any | No | どの条件にも一致しない場合のデフォルト値（デフォルト: NULL = NA） | RHSと共通型を持つこと |
| .unmatched | character | No | 一致しない場合の処理。"default"または"error" | "default"または"error" |
| .ptype | any | No | 出力型を指定するプロトタイプ | 有効な型であること |
| .size | integer | No | 出力サイズを指定 | LHSの共通サイズと一致すること |

### 入力データソース

関数の引数として直接渡されるフォーミュラ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | any | LHS入力の共通サイズで、RHS入力と.defaultの共通型を持つベクトル |

### 出力先

呼び出し元に返される。

## 処理フロー

### 処理シーケンス

```
1. フォーミュラの解析
   └─ eval_formulas()で...を解析
   └─ NULLを除外
   └─ LHS（conditions）とRHS（values）を分離
2. サイズの決定
   └─ case_when_size_common()でLHSとRHSの共通サイズを計算
   └─ スカラーLHS + ベクターRHSの場合は非推奨警告
3. conditionsのリサイクル
   └─ vec_recycle_common()でconditionsを共通サイズにリサイクル
4. vctrs::vec_case_when()の呼び出し
   └─ conditions, values, default, ptype, sizeを渡す
5. 結果ベクトルを返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始: case_when呼び出し] --> B[eval_formulas で...を解析]
    B --> C{...が空か?}
    C -->|Yes| D[エラー: ... can't be empty]
    C -->|No| E[LHSとRHSを分離]
    E --> F[case_when_size_common でサイズ決定]
    F --> G{スカラーLHS + ベクターRHS?}
    G -->|Yes| H[非推奨警告を出力]
    G -->|No| I[conditionsをリサイクル]
    H --> I
    I --> J[vec_case_when を呼び出し]
    J --> K{各要素を評価}
    K --> L[最初に一致した条件の値を選択]
    K --> M{一致なし?}
    M -->|.unmatched = error| N[エラー]
    M -->|.unmatched = default| O[.default値を使用]
    L --> P[結果ベクトルを返す]
    O --> P
    D --> Q[終了]
    N --> Q
    P --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | 順次評価 | 条件は上から順に評価され、最初に一致した条件が使用される | 常に適用 |
| BR-44-02 | NAの処理 | 条件がNAの場合はFALSEとして扱われ、.defaultが使用される | 常に適用 |
| BR-44-03 | 型の共通化 | すべてのRHS値と.defaultは共通型にキャストされる | 常に適用 |
| BR-44-04 | NULLの無視 | ...内のNULLフォーミュラは無視される | 常に適用 |
| BR-44-05 | スカラーLHS非推奨 | スカラーLHSとベクターRHSの組み合わせは非推奨 | dplyr 1.2.0以降 |

### 計算ロジック

```
for each i in 1:size:
    for each j in 1:n_conditions:
        if conditions[j][i] == TRUE:
            result[i] = values[j][i]
            break
    if no match:
        if .unmatched == "error":
            error("unmatched values")
        else:
            result[i] = .default[i]
```

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

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | 本関数はインメモリ操作のみでデータベース操作を行わない |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 引数エラー | ...が空の場合 | 少なくとも1つのフォーミュラを渡す |
| - | フォーミュラエラー | 二項フォーミュラでない場合 | `condition ~ value`形式を使用する |
| - | 型エラー | LHSが論理型でない場合 | 論理式を使用する |
| - | 一致エラー | .unmatched = "error"で一致しない値がある場合 | すべてのケースをカバーする |

### リトライ仕様

リトライは不要（即座にエラーを返す）

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

該当なし（インメモリ操作のみ）

## パフォーマンス要件

- vctrsの効率的な実装を活用すること
- 条件は遅延評価されないため、すべての条件と値が評価される点に注意

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

特になし

## 備考

- `case_when()`は`if_else()`の複数条件版である
- 条件の順序は重要（最も具体的な条件を先に記述すること）
- 単純な二値分岐には`if_else()`を使用すること
- 値のマッチングには`case_match()`または`recode_values()`を検討すること

---

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

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

### 推奨読解順序

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

`case_when()`はフォーミュラを解析し、vctrsの`vec_case_when()`に委譲する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | case-when.R | `R/case-when.R` | `case_when()`関数の全体構造を理解する |

**読解のコツ**: `case_when()`と`replace_when()`は同じファイルで定義され、共通のヘルパー関数を使用している。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | case-when.R | `R/case-when.R` | 288-322行目の`case_when()`関数定義 |

**主要処理フロー**:
1. **295行目**: `eval_formulas()`でフォーミュラを解析
2. **296-297行目**: LHS（conditions）とRHS（values）を取得
3. **299-303行目**: `case_when_size_common()`でサイズを決定
4. **308行目**: `vec_recycle_common()`でconditionsをリサイクル
5. **310-321行目**: `vec_case_when()`を呼び出し

#### Step 3: フォーミュラ解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | case-when.R | `R/case-when.R` | 505-639行目の`eval_formulas()`と関連関数 |

**主要処理フロー**:
- **505-603行目**: `eval_formulas()` - フォーミュラの解析と評価
- **605-639行目**: `validate_and_split_formula()` - フォーミュラの検証と分割

#### Step 4: サイズ計算ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | case-when.R | `R/case-when.R` | 385-457行目の`case_when_size_common()`関数 |

**読解のコツ**: この関数は歴史的な互換性のために複雑なサイズ計算を行う。スカラーLHS + ベクターRHSのパターンは非推奨である。

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

```
case_when(..., .default, .unmatched, .ptype, .size)
    │
    ├─ eval_formulas(...)
    │      ├─ validate_and_split_formula()
    │      └─ eval_tidy() [LHS/RHSの評価]
    │
    ├─ case_when_size_common()
    │      ├─ vec_size_common() [LHSサイズ]
    │      ├─ vec_size_common() [RHSサイズ]
    │      └─ warn_case_when_scalar_lhs_vector_rhs() [非推奨警告]
    │
    ├─ vec_recycle_common() [conditions]
    │
    └─ vctrs::vec_case_when()
           ├─ 条件の順次評価
           └─ 最初の一致を選択
```

### データフロー図

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

...フォーミュラ    ───▶  eval_formulas()で解析      ───▶  conditions, values
  (condition ~ value)

conditions,        ───▶  case_when_size_common()    ───▶  共通サイズ
  values                 でサイズ決定

conditions         ───▶  vec_recycle_common()       ───▶  リサイクル済み
                         でリサイクル                      conditions

リサイクル済み     ───▶  vec_case_when()で          ───▶  結果ベクトル
  + values + default     条件評価と値選択
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| case-when.R | `R/case-when.R` | ソース | `case_when()`、`replace_when()`の実装 |
| if-else.R | `R/if-else.R` | ソース | `if_else()`関数（単純な条件分岐） |
| case-match.R | `R/case-match.R` | ソース | `case_match()`関数（値マッチング） |
| recode-values.R | `R/recode-values.R` | ソース | `recode_values()`関数（値の再コード化） |
