# 機能設計書 41-if_all

## 概要

本ドキュメントは、dplyrパッケージの`if_all()`関数の機能設計について記述する。`if_all()`は複数列に対して同じ条件を適用し、すべての列で条件が真である場合に`TRUE`を返す関数である。

### 本機能の処理概要

`if_all()`は、選択した複数の列に同じ述語関数を適用し、すべての選択された列で条件が真である場合に`TRUE`を返す論理ベクトルを生成する。これは`all()`関数の列方向への拡張であり、`filter()`関数と組み合わせて使用することで、複数列の条件を簡潔に記述できる。

**業務上の目的・背景**：データ分析において、複数の変数が同時に特定の条件を満たす行を抽出する必要がある場面は頻繁に発生する。例えば、すべての数値列が正の値である行、すべての文字列列が空でない行を抽出するなどのケースである。従来は各列に対して個別に条件を記述し、`&`演算子で結合する必要があったが、`if_all()`を使用することでコードが簡潔になり、可読性と保守性が向上する。

**機能の利用シーン**：
- データクレンジングで、複数列のすべてが有効な値を持つ行のみを抽出する場合
- 品質管理で、複数の測定値がすべて基準範囲内にある行を特定する場合
- フォームバリデーションで、必須フィールドがすべて入力されているレコードを抽出する場合

**主要な処理内容**：
1. tidyselect構文を使用して対象列を選択する
2. 選択した各列に対して述語関数を適用する
3. 各行について、すべての列の結果を`&`演算子で結合する
4. 結果の論理ベクトルを返す

**関連システム・外部連携**：特になし。dplyr内部で完結する関数である。

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

## 関連画面

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

## 機能種別

計算処理 / データ変換 / フィルタリング補助

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| .cols | tidy-select | Yes | 処理対象の列を選択するtidyselect式 | tidyselect構文に準拠すること |
| .fns | function/formula | Yes | 各列に適用する述語関数。purrr形式のlambda（`~ .x > 0`）も使用可能 | 論理値を返す関数であること |
| ... | any | No | 非推奨。関数への追加引数 | 使用は推奨されない |
| .names | character | No | 出力列の命名テンプレート（glue形式） | 有効なglueテンプレートであること |

### 入力データソース

データマスク内のデータフレーム（`mutate()`、`filter()`、`summarise()`などのdplyr動詞内で評価される）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | logical | 各行について、選択したすべての列で条件が真の場合`TRUE`、それ以外は`FALSE`を返す論理ベクトル |

### 出力先

呼び出し元のdplyr動詞に返される。通常は`filter()`の条件として使用される。

## 処理フロー

### 処理シーケンス

```
1. パラメータの検証
   └─ .colsが欠損の場合は非推奨警告を出してeverything()をデフォルトとする
2. across()関数を呼び出して各列に述語関数を適用
   └─ tidyselect::eval_select()で列を選択
   └─ 各列に.fnsを適用
3. 結果のデータフレームをリストに変換
   └─ dplyr_new_list()でリスト化
4. dplyr_list_pall()で各行の論理積を計算
   └─ 列が0個の場合はTRUEを返す（all()と同じ動作）
5. 論理ベクトルを返す
```

### フローチャート

```mermaid
flowchart TD
    A[開始: if_all呼び出し] --> B{.colsが指定されているか?}
    B -->|No| C[非推奨警告を出力]
    C --> D[.cols = everything に設定]
    B -->|Yes| D
    D --> E[across で各列に.fnsを適用]
    E --> F[結果データフレームをリスト化]
    F --> G{列数が0か?}
    G -->|Yes| H[TRUEを返す]
    G -->|No| I[各行で全列の論理積を計算]
    I --> J[論理ベクトルを返す]
    H --> K[終了]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-01 | 空選択時のデフォルト | 選択列が0個の場合、`TRUE`を返す | .colsで列が1つも選択されない場合 |
| BR-41-02 | all()との整合性 | 空入力に対する動作は`all()`関数と一致する | 常に適用 |
| BR-41-03 | グループ変数の除外 | グループ化変数は自動的に選択から除外される | grouped_dfに対して使用する場合 |

### 計算ロジック

各行について、選択されたすべての列の条件結果を`&`演算子で結合する。

```
result[i] = condition(col1[i]) & condition(col2[i]) & ... & condition(colN[i])
```

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 引数エラー | .fnsが関数でもフォーミュラでもない場合 | 関数またはフォーミュラを指定する |
| - | 評価エラー | .fnsが論理値以外を返す場合 | 論理値を返す関数を使用する |
| - | 選択エラー | .colsのtidyselect式が無効な場合 | 有効なtidyselect構文を使用する |

### リトライ仕様

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

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

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

## パフォーマンス要件

- 大規模データセットでも効率的に動作すること
- `across()`の最適化を活用し、可能な場合は展開による効率化を行う

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

特になし

## 備考

- `if_all()`は`if_any()`と対になる関数である
- dplyr 1.1.0以降、`.cols`引数を省略することは非推奨となっている
- `...`引数への値の渡しは非推奨であり、代わりにラムダ関数内で引数を指定すること

---

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

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

### 推奨読解順序

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

`if_all()`は内部で`across()`を利用し、その結果を論理ベクトルに変換する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | across.R | `R/across.R` | `if_all()`の本体と`across()`との関係を理解する |

**読解のコツ**: `if_all()`は`across()`のラッパーであり、`across()`の結果に対して`dplyr_list_pall()`を適用している。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | across.R | `R/across.R` | 366-373行目の`if_all()`関数定義 |

**主要処理フロー**:
1. **366行目**: `if_all`関数の定義開始
2. **367行目**: `context_local("across_if_fn", "if_all")` - コンテキストに関数名を設定
3. **368行目**: `context_local("across_frame", current_env())` - エラーフレームを設定
4. **369行目**: `across({{ .cols }}, .fns, ..., .names = .names)` - across関数を呼び出し
5. **370行目**: `dplyr_new_list(df)` - 結果をリストに変換
6. **371行目**: `vec_size(df)` - サイズを取得
7. **372行目**: `dplyr_list_pall(x, size = size)` - 全列の論理積を計算

#### Step 3: across()の内部処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | across.R | `R/across.R` | 216-351行目の`across()`関数の処理フロー |

**主要処理フロー**:
- **255-263行目**: `across_setup()`で列選択と関数準備を行う
- **320-339行目**: 各列に関数を適用するループ処理
- **341-344行目**: 結果をtibbleとして整形

#### Step 4: 展開処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | across.R | `R/across.R` | 623-683行目の`expand_if_across()`関数 |

**読解のコツ**: `expand_if_across()`は`if_all()`/`if_any()`呼び出しを事前に展開し、効率的な実行を可能にする。これはtidyselectの評価を1回だけ行うための最適化である。

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

```
if_all(.cols, .fns)
    │
    ├─ context_local() [コンテキスト設定]
    │
    ├─ across({{ .cols }}, .fns)
    │      │
    │      ├─ across_setup()
    │      │      ├─ quo_set_env_to_data_mask_top()
    │      │      └─ tidyselect::eval_select()
    │      │
    │      └─ [各列に.fnsを適用するループ]
    │
    ├─ dplyr_new_list(df)
    │
    └─ dplyr_list_pall(x, size)
           └─ [各行の全列論理積を計算]
```

### データフロー図

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

data.frame        ───▶  tidyselect で列選択        ───▶  選択列
  (.cols指定)            across_setup()

選択列            ───▶  各列に.fnsを適用           ───▶  論理値data.frame
  + .fns                 across()ループ

論理値data.frame  ───▶  各行の論理積を計算         ───▶  論理ベクトル
                        dplyr_list_pall()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| across.R | `R/across.R` | ソース | `if_all()`、`if_any()`、`across()`の実装 |
| context.R | `R/context.R` | ソース | `context_local()`などのコンテキスト管理関数 |
| data-mask.R | `R/data-mask.R` | ソース | データマスク関連のユーティリティ |
| colwise-filter.R | `R/colwise-filter.R` | ソース | 旧式の`filter_all()`等（`if_all()`の前身） |
