# 機能設計書 14-ungroup

## 概要

本ドキュメントは、dplyrパッケージの`ungroup()`関数に関する機能設計書である。この関数は、グループ化されたデータフレームからグループ化を解除するための機能を提供する。

### 本機能の処理概要

`ungroup()`関数は、`grouped_df`（グループ化されたデータフレーム）または`rowwise_df`（行単位グループ化されたデータフレーム）からグループ化を解除し、通常のデータフレーム（tibble）に変換する。また、特定のグループ変数のみを解除する部分的なアングループにも対応している。

**業務上の目的・背景**：グループ化されたデータフレームに対して集計処理を行った後、全体に対する操作（例：グローバルな合計、全体順位）を行いたい場合や、グループ情報を削除してクリーンなデータフレームを取得したい場合がある。`ungroup()`は、グループ化の状態を明示的に制御し、意図しないグループ単位の処理を防ぐための機能を提供する。

**機能の利用シーン**：
- グループ集計後に全体に対する計算を行いたい場合
- グループ化されたデータフレームを通常のデータフレームとして保存したい場合
- 特定のグループ変数のみを解除して、より大きなグループで再集計したい場合
- `rowwise_df`を通常のデータフレームに戻したい場合

**主要な処理内容**：
1. `UseMethod()`によるメソッドディスパッチ
2. データフレームの型に応じた処理
   - `grouped_df`: グループ属性を削除してtibbleに変換
   - `rowwise_df`: rowwise属性を削除してtibbleに変換
   - `data.frame`: そのまま返却（変更なし）
3. 引数が指定された場合は部分的アングループ
   - 指定された変数をグループから削除
   - 残りの変数で再グループ化

**関連システム・外部連携**：`group_by()`と対になる関数。`as_tibble()`でも同様の効果が得られる場合がある。

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

## 関連画面

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

## 機能種別

データ変換処理（グループ操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | data.frame | Yes | 操作対象のデータフレーム（grouped_df、rowwise_df、またはdata.frame） | data.frameまたはその派生クラスであること |
| ... | tidy-select | No | 解除するグループ変数（grouped_dfのみ） | 存在するグループ変数であること |

### 入力データソース

- Rセッション内のgrouped_df、rowwise_df、またはdata.frameオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | tibble/grouped_df | グループ解除されたデータフレーム |

### 出力先

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

### 出力特性

- `...`が未指定の場合: tibbleを返却
- `...`が指定された場合（grouped_dfのみ）: 残りのグループで再グループ化されたgrouped_dfを返却
- データの行と列は変更されない

## 処理フロー

### 処理シーケンス

```
1. UseMethod("ungroup")によるメソッドディスパッチ
   └─ xの型に応じた適切なメソッドが呼び出される
2. 型別の処理
   ├─ grouped_df:
   │    ├─ ...未指定: as_tibble(x)で変換
   │    └─ ...指定: 部分アングループ後に再group_by
   ├─ rowwise_df:
   │    └─ check_dots_empty()後にas_tibble(x)
   └─ data.frame:
        └─ check_dots_empty()後にそのまま返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ungroup呼び出し]
    B --> C[UseMethodでディスパッチ]
    C --> D{xの型は?}
    D -->|grouped_df| E{...引数あり?}
    D -->|rowwise_df| F[check_dots_empty]
    D -->|data.frame| G[check_dots_empty]
    E -->|No| H[as_tibble変換]
    E -->|Yes| I[グループ変数を取得]
    I --> J[削除対象を特定]
    J --> K[setdiffで残りを計算]
    K --> L[残りでgroup_by]
    F --> H
    G --> M[そのまま返却]
    H --> N[結果を返却]
    L --> N
    M --> N
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 完全アングループ | 引数なしで呼び出すと全てのグループが解除される | ...が未指定 |
| BR-02 | 部分アングループ | grouped_dfで引数指定時、指定変数のみがグループから除外される | grouped_dfで...が指定 |
| BR-03 | rowwise引数不可 | rowwise_dfでは追加引数を指定できない | rowwise_dfで...が指定された場合 |
| BR-04 | data.frame引数不可 | 通常のdata.frameでは追加引数を指定できない | data.frameで...が指定された場合 |

### 計算ロジック

**部分アングループの計算**:
```
old_groups = group_vars(x)
to_remove = tidyselect::eval_select(c(...), x)
new_groups = setdiff(old_groups, to_remove)
result = group_by(x, !!!syms(new_groups))
```

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

該当なし（本機能はインメモリのデータフレーム操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 引数エラー | rowwise_dfまたはdata.frameで...が指定された場合 | check_dots_empty()によるエラーメッセージ |

### リトライ仕様

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

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

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

## パフォーマンス要件

- O(1)の計算量（グループ属性の削除のみ）
- 部分アングループの場合はgroup_by()の計算量に準ずる

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

- 本機能は純粋なデータ変換操作であり、特別なセキュリティ考慮事項はない
- グループ解除によってデータの内容は変更されない

## 備考

- `ungroup()`は`group_by()`と対になる関数
- `as_tibble()`でも同様にグループを解除できる
- 部分アングループは`grouped_df`でのみ利用可能

---

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

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

### 推奨読解順序

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

`ungroup()`は`grouped_df`や`rowwise_df`を通常のtibbleに変換する。グループ情報は属性として保持されているため、解除は属性の削除に相当する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | grouped-df.R | `R/grouped-df.R` | grouped_dfのグループ属性構造 |
| 1-2 | rowwise.R | `R/rowwise.R` | rowwise_dfの構造 |

**読解のコツ**: `grouped_df`も`rowwise_df`も`tbl_df`の派生クラスであり、グループ情報は属性として保持されている。`as_tibble()`は属性を削除して基底クラスに戻す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | group-by.R | `R/group-by.R` | `ungroup()`関数のジェネリック定義（160-162行目） |

**主要処理フロー**:
1. **160-162行目**: `UseMethod("ungroup")`によるジェネリック定義

#### Step 3: 各メソッドの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | group-by.R | `R/group-by.R` | `ungroup.grouped_df()`（165-180行目） |
| 3-2 | group-by.R | `R/group-by.R` | `ungroup.rowwise_df()`（183-186行目） |
| 3-3 | group-by.R | `R/group-by.R` | `ungroup.data.frame()`（189-192行目） |

**主要処理フロー（ungroup.grouped_df）**:
- **166-167行目**: `...`がない場合は`as_tibble(x)`
- **169-175行目**: `...`がある場合、削除対象変数を特定
- **177-178行目**: `setdiff()`で残りを計算し再`group_by()`

**主要処理フロー（ungroup.rowwise_df）**:
- **184行目**: `check_dots_empty()`で引数チェック
- **185行目**: `as_tibble(x)`で変換

**主要処理フロー（ungroup.data.frame）**:
- **190行目**: `check_dots_empty()`で引数チェック
- **191行目**: そのまま`x`を返却

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

```
ungroup(x, ...)
    │
    ├─ UseMethod("ungroup")
    │      │
    │      ├─ ungroup.grouped_df()
    │      │      │
    │      │      ├─ [引数なし] as_tibble(x)
    │      │      │
    │      │      └─ [引数あり]
    │      │             ├─ group_vars()
    │      │             ├─ tidyselect::eval_select()
    │      │             ├─ setdiff()
    │      │             └─ group_by()
    │      │
    │      ├─ ungroup.rowwise_df()
    │      │      │
    │      │      ├─ check_dots_empty()
    │      │      │
    │      │      └─ as_tibble(x)
    │      │
    │      └─ ungroup.data.frame()
    │             │
    │             ├─ check_dots_empty()
    │             │
    │             └─ x (そのまま)
```

### データフロー図

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

grouped_df ─────────┬─ ungroup.grouped_df() ──────── tibble / grouped_df
                    │         │
... (除外変数) ─────┤         ├─ 引数なし → as_tibble()
                    │         │
                    │         └─ 引数あり → setdiff() → group_by()
                    │
rowwise_df ─────────┼─ ungroup.rowwise_df() ──────── tibble
                    │         │
                    │         └─ as_tibble()
                    │
data.frame ─────────┴─ ungroup.data.frame() ──────── data.frame (変更なし)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| group-by.R | `R/group-by.R` | ソース | ungroup関数のメイン実装（group_byと同じファイル） |
| grouped-df.R | `R/grouped-df.R` | ソース | grouped_dfクラスの定義 |
| rowwise.R | `R/rowwise.R` | ソース | rowwise_dfクラスの定義 |
| test-group-by.R | `tests/testthat/test-group-by.R` | テスト | ungroupのユニットテスト |
