# 機能設計書 38-across

## 概要

本ドキュメントは、dplyrパッケージの `across()` 関数の機能設計を記述する。この関数は複数の列に同じ変換を適用するための関数で、`mutate()` や `summarise()` などのデータマスキング関数内で使用される。

### 本機能の処理概要

`across()` は、指定された列選択に対して1つまたは複数の関数を適用し、結果をtibbleとして返す関数である。これにより、`summarise_at()` や `mutate_if()` などのスコープ付きバリアント（superseded）を置き換え、より柔軟で表現力のある複数列操作を可能にする。

**業務上の目的・背景**：データ分析において、複数の列に対して同じ変換や集計を適用する必要がある場面は非常に多い。例えば、すべての数値列の平均を計算、特定の列群を文字列に変換、複数の指標に対して複数の集計関数を適用するなどの操作が必要になる。`across()` は、tidy-select構文を使用した柔軟な列選択と、ラムダ関数や関数リストを使用した変換の指定を組み合わせ、これらの操作を簡潔に表現できる。

**機能の利用シーン**：
- すべての数値列に対する変換（標準化、対数変換など）
- 特定のパターンに一致する列の集計
- 複数の関数を複数の列に適用（平均と標準偏差など）
- グループごとの複数列集計
- 列名パターンに基づく一括変換

**主要な処理内容**：
1. 列選択（.cols）をtidyselectで評価
2. 関数（.fns）を検証・正規化
3. .namesパラメータで出力列名を生成
4. 各列に対して関数を適用
5. 結果をtibbleとして返す
6. .unpackオプションでネストされたデータフレームを展開

**関連システム・外部連携**：tidyselectパッケージの `eval_select()` に依存。vctrパッケージの各種関数に依存。

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

## 関連画面

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

## 機能種別

データ操作 / 複数列変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| .cols | tidy-select | Yes | 変換する列の選択。グループ化列は選択不可 | tidyselect構文に従う |
| .fns | function/formula/list | No | 適用する関数。関数、purrr形式のラムダ（`~ .x`）、または関数/ラムダのリスト | 関数、フォーミュラ、またはそれらのリスト |
| ... | ... | No | 非推奨。関数への追加引数（ラムダで直接指定することが推奨） | 使用時に警告 |
| .names | glue specification | No | 出力列名のテンプレート。`{.col}` と `{.fn}` が使用可能 | glue構文に従う |
| .unpack | logical/string | No | データフレームを返す関数の結果を展開するかどうか | TRUE/FALSE/文字列 |

### 入力データソース

データマスク環境（`mutate()`, `summarise()` などの内部から呼び出される）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (返り値) | tibble | 各列と各関数の組み合わせに対応する列を持つtibble。.unpackが使用された場合はより多くの列が返される可能性がある |

### 出力先

呼び出し元への戻り値として返却（通常は`mutate()`や`summarise()`の結果に統合）

## 処理フロー

### 処理シーケンス

```
1. データマスクの取得
   └─ peek_mask()で現在のデータマスクを取得

2. 列選択の評価
   └─ tidyselect::eval_select()で.colsを評価
   └─ グループ化列は自動的に除外

3. 関数の正規化
   └─ quo_eval_fns()で.fnsを検証・正規化
   └─ インライン可能なラムダを検出・マーク

4. 出力列名の生成
   └─ across_setup()で.namesテンプレートを評価
   └─ {.col}と{.fn}を置換

5. 各列への関数適用
   └─ ループで各列・各関数の組み合わせを処理
   └─ cur_column()コンテキストを設定

6. 結果の集約
   └─ vec_size_common()とvec_recycle_common()でサイズ調整
   └─ tibbleとして構築

7. アンパック処理（必要な場合）
   └─ df_unpack()でネストされたデータフレームを展開
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[peek_mask でマスク取得]
    B --> C[tidyselect で列選択]
    C --> D{列が選択されたか}
    D -->|No| E[空のtibbleを返却]
    D -->|Yes| F[関数を正規化]
    F --> G{関数が指定されたか}
    G -->|No| H[列のみを返却 pick相当]
    G -->|Yes| I[出力列名を生成]
    I --> J[各列・各関数をループ]
    J --> K[関数を適用]
    K --> L{全て完了?}
    L -->|No| J
    L -->|Yes| M[サイズを調整]
    M --> N{.unpack?}
    N -->|Yes| O[df_unpackで展開]
    N -->|No| P[tibbleとして返却]
    O --> P
    P --> Q[終了]
    E --> Q
    H --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | グループ列除外 | グループ化列は自動的に選択から除外される | グループ化されたデータフレームの場合 |
| BR-38-02 | 評価タイミング | コードはグループと列の組み合わせごとに評価される | 常時 |
| BR-38-03 | 列名の一意性 | 出力列名は一意である必要がある | .namesが指定された場合 |
| BR-38-04 | ラムダのインライン化 | 単純なラムダは効率化のためにインライン化される | 単一引数のラムダの場合 |

### 計算ロジック

- 列選択: tidyselect構文による柔軟な列選択
- 関数適用: 各列に対して指定された関数を順番に適用
- 結果サイズ: `vec_size_common()` による共通サイズへの調整

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 列名エラー | .colsで存在しない列を指定した場合 | 正しい列名を指定 |
| - | 関数エラー | .fnsが関数、フォーミュラ、またはリストでない場合 | 正しい形式で指定 |
| - | 計算エラー | 関数の適用中にエラーが発生した場合 | 関数のロジックを確認 |
| - | 列名重複エラー | .namesで生成された列名が重複する場合 | ユニークな.namesを指定 |

### リトライ仕様

該当なし（同期処理）

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

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

## パフォーマンス要件

特に定義なし。内部でラムダのインライン化などの最適化が行われる。

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

特になし

## 備考

- `summarise_at()`, `summarise_if()`, `summarise_all()` などのスコープ付きバリアントを置き換える
- `pick()` は変換なしで列を選択する場合に推奨される
- `cur_column()` を使用して現在処理中の列名にアクセス可能
- `...` への追加引数は非推奨、ラムダで直接指定することが推奨

---

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

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

### 推奨読解順序

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

データマスクとクォージャーの仕組みを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | data-mask.R | `R/data-mask.R` | dplyrのデータマスク実装 |
| 1-2 | context.R | `R/context.R` | cur_column()などのコンテキスト関数 |

**読解のコツ**: `across()` はデータマスク内で評価されるため、`peek_mask()` でマスク環境にアクセスする。

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

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

**主要処理フロー**:
1. **217行目**: `peek_mask()` でデータマスクを取得
2. **223-230行目**: `.cols` の処理（デフォルト警告を含む）
3. **232-242行目**: `.fns` の処理と正規化
4. **255-263行目**: `across_setup()` で設定を準備
5. **283-289行目**: 列が空の場合の処理
6. **303-339行目**: メインループ（列と関数の適用）
7. **341-344行目**: 結果のtibble構築
8. **346-348行目**: `.unpack` 処理

#### Step 3: セットアップ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | across.R | `R/across.R` | across_setup関数（423-517行目） |

**主要処理フロー**:
- **453-459行目**: tidyselectで列を評価
- **461-476行目**: .fnsがNULLの場合の処理
- **478-484行目**: .names のデフォルト設定
- **501-510行目**: glueで列名を生成

#### Step 4: 関数評価を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | across.R | `R/across.R` | quo_eval_fns関数（943-1003行目） |

**主要処理フロー**:
- インライン可能なラムダの検出
- データマスクへの環境設定
- 関数の正規化（フォーミュラからの変換）

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

```
across(.cols, .fns, ..., .names, .unpack)
    │
    ├─ peek_mask()                      [dplyr]
    │
    ├─ across_setup()                   [dplyr]
    │      │
    │      ├─ tidyselect::eval_select() [tidyselect]
    │      │
    │      └─ glue()                    [glue]
    │
    ├─ quo_eval_fns()                   [dplyr]
    │      │
    │      ├─ as_function()             [rlang]
    │      │
    │      └─ is_inlinable_lambda()
    │
    ├─ mask$current_cols()              [dplyr]
    │
    ├─ context_poke("column", ...)      [dplyr]
    │
    ├─ fn(col, ...)                     [各関数の適用]
    │
    ├─ vec_size_common()                [vctrs]
    │
    ├─ vec_recycle_common()             [vctrs]
    │
    ├─ dplyr_new_tibble()               [dplyr]
    │
    └─ df_unpack()                      [dplyr] (必要な場合)
```

### データフロー図

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

データフレーム ─────────▶ mutate/summarise ─────────────▶ 変換されたデータ
     │                          │
     │                          ▼
     │                     across()
     │                          │
     │         ┌────────────────┼────────────────┐
     │         ▼                ▼                ▼
     └───▶ 列A選択 ────▶    列B選択 ────▶   列C選択
              │                │                │
              ▼                ▼                ▼
           fn1(A)           fn1(B)          fn1(C)
           fn2(A)           fn2(B)          fn2(C)
              │                │                │
              └────────────────┼────────────────┘
                               ▼
                        結果tibble
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| across.R | `R/across.R` | ソース | across、c_across、if_any、if_all関数の定義 |
| context.R | `R/context.R` | ソース | cur_column()などのコンテキスト関数 |
| data-mask.R | `R/data-mask.R` | ソース | データマスクの実装 |
| vctrs.R | `R/vctrs.R` | ソース | vctrヘルパー関数 |
| pick.R | `R/pick.R` | ソース | 関連するpick関数 |
