# 機能設計書 40-if_any

## 概要

本ドキュメントは、dplyrパッケージの `if_any()` 関数の機能設計を記述する。この関数は選択した列のいずれかで条件が真かを判定し、`filter()` などのデータマスキング関数内で使用される。

### 本機能の処理概要

`if_any()` は、指定された列選択に対して述語関数を適用し、いずれかの列で条件が TRUE の場合に TRUE を返すlogicalベクトルを生成する関数である。これは `any()` 関数の列方向版として機能し、複数列にまたがる条件判定を簡潔に表現できる。

**業務上の目的・背景**：データ分析において、「いずれかの列が条件を満たす行」をフィルタリングする必要がある場面は多い。例えば、複数の測定値のいずれかが欠損している行の検出、複数のフラグ列のいずれかが TRUE の行の抽出、複数の閾値条件のいずれかを満たす行の特定などに使用される。`if_any()` は、`filter_all()` や `filter_any()` などのスコープ付きバリアント（superseded）を置き換え、より柔軟で表現力のある条件判定を可能にする。

**機能の利用シーン**：
- いずれかの列が NA の行をフィルタリング
- 複数のフラグ列のいずれかが TRUE の行を抽出
- 複数の数値列のいずれかが閾値を超える行を特定
- 複数のカテゴリ列のいずれかが特定の値を持つ行を検出

**主要な処理内容**：
1. コンテキストを `if_any` に設定
2. `across()` を呼び出して各列に関数を適用
3. 結果のデータフレームをリストに変換
4. `dplyr_list_pany()` で列方向の OR 演算を実行
5. logicalベクトルを返す

**関連システム・外部連携**：`across()` 関数の上位関数として実装。vctrパッケージの `vec_pany()` に依存。

**権限による制御**：特になし。すべてのユーザーが利用可能。

## 関連画面

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

## 機能種別

データ操作 / 条件判定 / 複数列フィルタリング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| .cols | tidy-select | Yes | 条件を判定する列の選択 | tidyselect構文に従う |
| .fns | function/formula | Yes | 各列に適用する述語関数。logical値を返す必要がある | 関数またはフォーミュラ |
| ... | ... | No | 非推奨。追加引数（ラムダで直接指定することが推奨） | 使用時は across と同様の警告 |
| .names | glue specification | No | 出力列名のテンプレート（通常は使用されない） | glue構文に従う |

### 入力データソース

データマスク環境（`filter()` などのデータマスキング関数内から呼び出される）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (返り値) | logical vector | いずれかの列で条件がTRUEの場合TRUE、すべての列でFALSEの場合FALSE。NAの扱いはvctrの動作に従う |

### 出力先

呼び出し元への戻り値として返却（通常は`filter()`の条件として使用）

## 処理フロー

### 処理シーケンス

```
1. コンテキストの設定
   └─ context_local()で"across_if_fn"を"if_any"に設定
   └─ context_local()で"across_frame"を現在の環境に設定

2. acrossの呼び出し
   └─ across()を呼び出して各列に関数を適用
   └─ 結果はtibble（各列がlogical）

3. リストへの変換
   └─ dplyr_new_list()でデータフレームをリストに変換

4. サイズの取得
   └─ vec_size()で行数を取得

5. OR演算の実行
   └─ dplyr_list_pany()で列方向のOR演算
   └─ いずれかがTRUEならTRUE

6. 結果の返却
   └─ logicalベクトルを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[context_localでif_anyを設定]
    B --> C[across を呼び出し]
    C --> D[各列に.fnsを適用]
    D --> E[結果tibble]
    E --> F[dplyr_new_listでリストに変換]
    F --> G[vec_sizeでサイズ取得]
    G --> H[dplyr_list_panyでOR演算]
    H --> I[logicalベクトルを返却]
    I --> J[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | OR論理演算 | 選択した列のいずれかで条件がTRUEならば結果はTRUE | 常時 |
| BR-40-02 | 空列時FALSE | 選択した列がない場合はFALSEを返す（any()の動作に一致） | 列が選択されない場合 |
| BR-40-03 | NA伝播 | NA値は通常のlogical演算と同様に伝播する | NAが含まれる場合 |
| BR-40-04 | グループ列除外 | グループ化列は自動的に選択から除外される | グループ化されたデータの場合 |

### 計算ロジック

列方向の OR 演算: `result[i] = col1[i] | col2[i] | ... | colN[i]`

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 列名エラー | .colsで存在しない列を指定した場合 | 正しい列名を指定 |
| - | 関数エラー | .fnsがlogicalを返さない場合 | logicalを返す関数を指定 |
| - | 計算エラー | 関数の適用中にエラーが発生した場合 | 関数のロジックを確認 |

### リトライ仕様

該当なし（同期処理）

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

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

## パフォーマンス要件

特に定義なし。内部で`across()`と同様の最適化が行われる。

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

特になし

## 備考

- `if_all()` は AND 演算（すべての列で条件が TRUE の場合に TRUE）
- `if_any()` は OR 演算（いずれかの列で条件が TRUE の場合に TRUE）
- `filter_all()`, `filter_if()`, `filter_at()` などのスコープ付きバリアントを置き換える
- `any_vars()` との違い: `if_any()` はより柔軟なtidy-select構文をサポート

---

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

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

### 推奨読解順序

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

`across()` との関係を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | across.R | `R/across.R` | across()の実装（216-351行目） |
| 1-2 | vctrs.R | `R/vctrs.R` | dplyr_list_pany()の実装（61-70行目） |

**読解のコツ**: `if_any()` は `across()` のラッパーとして実装され、結果を論理演算で集約する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | across.R | `R/across.R` | if_any関数本体（355-362行目） |

**主要処理フロー**:
1. **356行目**: `context_local("across_if_fn", "if_any")` でコンテキストを設定
2. **357行目**: `context_local("across_frame", current_env())` でフレームを設定
3. **358行目**: `across({{ .cols }}, .fns, ..., .names = .names)` を呼び出し
4. **359行目**: `dplyr_new_list(df)` でリストに変換
5. **360行目**: `vec_size(df)` でサイズを取得
6. **361行目**: `dplyr_list_pany(x, size = size)` でOR演算

#### Step 3: if_allとの比較

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | across.R | `R/across.R` | if_all関数（366-373行目）との違い |

**比較ポイント**:
- `if_any()`: `dplyr_list_pany()` を使用（OR演算）
- `if_all()`: `dplyr_list_pall()` を使用（AND演算）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | vctrs.R | `R/vctrs.R` | dplyr_list_pany関数（61-70行目） |

**主要処理フロー**:
- **68行目**: `vec_pany(!!!x, ...)` で要素ごとの OR 演算

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

```
if_any(.cols, .fns, ..., .names)
    │
    ├─ context_local()              [dplyr]
    │      ├─ "across_if_fn" = "if_any"
    │      └─ "across_frame" = current_env()
    │
    ├─ across()                     [dplyr]
    │      │
    │      ├─ peek_mask()
    │      ├─ across_setup()
    │      │      └─ tidyselect::eval_select()
    │      ├─ quo_eval_fns()
    │      └─ [各列に関数適用]
    │
    ├─ dplyr_new_list()             [dplyr]
    │
    ├─ vec_size()                   [vctrs]
    │
    └─ dplyr_list_pany()            [dplyr]
           │
           └─ vec_pany()            [vctrs]
```

### データフロー図

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

データフレーム ─────────▶ filter ─────────────────────▶ フィルタされたデータ
     │                      │
     │                      ▼
     │                  if_any()
     │                      │
     │         ┌────────────┼────────────┐
     │         ▼            ▼            ▼
     └───▶ 列A判定      列B判定      列C判定
           (logical)    (logical)   (logical)
              │            │            │
              └────────────┼────────────┘
                           ▼
                     dplyr_list_pany
                     (列方向 OR)
                           │
                           ▼
                    logicalベクトル
                           │
                           ▼
                    filter()で使用

例:
列A = {T, F, F}
列B = {F, T, F}
列C = {F, F, F}
if_any結果 = {T, T, F}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| across.R | `R/across.R` | ソース | if_any関数の定義（355-362行目） |
| across.R | `R/across.R` | ソース | if_all関数の定義（366-373行目） |
| across.R | `R/across.R` | ソース | across関数の定義（216-351行目） |
| vctrs.R | `R/vctrs.R` | ソース | dplyr_list_pany関数（61-70行目） |
| colwise-filter.R | `R/colwise-filter.R` | ソース | 旧filter_all等（superseded） |
