# 機能設計書 5-distinct

## 概要

本ドキュメントは、dplyrパッケージの`distinct`関数に関する機能設計書です。`distinct`関数は、データフレームから重複行を削除し、一意の行のみを保持する機能を提供します。

### 本機能の処理概要

`distinct`関数は、指定された列（または全列）の組み合わせに基づいて重複行を検出し、各組み合わせの最初の行のみを保持するデータ操作関数です。`unique.data.frame()`と同様の機能を提供しますが、大幅に高速化されています。

**業務上の目的・背景**：データ分析において、重複データの除去は基本的かつ重要な操作です。マスターデータから一意のキーを抽出したり、結合操作後に発生した重複を除去したりする際に使用します。SQLのDISTINCT句に相当する機能を提供し、tidyverse流の簡潔な構文でデータの重複除去が可能です。

**機能の利用シーン**：
- カテゴリ変数の一意な値リストの取得
- 顧客IDの重複除去
- 結合操作後の重複レコード除去
- データの集約前の前処理

**主要な処理内容**：
1. 引数の解析とquosure変換
2. 計算列がある場合は`mutate`で新しい列を追加
3. 重複判定に使用する変数の決定
4. `vec_unique_loc`で一意な行の位置を取得
5. 列の選択（`.keep_all`に応じて）
6. `dplyr_row_slice`による行の抽出

**関連システム・外部連携**：
- vctrs: `vec_unique_loc`による高速な重複検出
- rlang: quosureとデータマスキング機能

**権限による制御**：該当なし（Rライブラリのため）

## 関連画面

本プロジェクトはRライブラリ（dplyr）であり、ユーザーインターフェース画面は存在しません。

## 機能種別

データ操作（重複除去）/ データ変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| .data | data.frame | Yes | 処理対象のデータフレーム | data.frameまたはその派生クラス |
| ... | expressions | No | 重複判定に使用する列または式 | 省略時は全列を使用 |
| .keep_all | logical | No | 全列を保持するか（デフォルト: FALSE） | - |

### 入力データソース

- メモリ上のdata.frame、tibble、grouped_df

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | .dataと同じ型 | 重複が除去されたデータフレーム |

### 出力先

- 関数の戻り値として返却（メモリ上）

## 処理フロー

### 処理シーケンス

```
1. 引数の解析
   └─ enquos(...)で判定変数をquosureに変換
2. distinct_prepare()の実行
   └─ 変数指定がない場合は全列を使用
   └─ 計算式がある場合はmutateで列を追加
   └─ 変数名の解決とバリデーション
3. 一意な行の位置を取得
   └─ dplyr_col_select()で判定列を選択
   └─ vec_unique_loc()で一意行の位置を計算
4. 出力列の選択
   └─ .keep_all=FALSEなら判定列のみ
   └─ .keep_all=TRUEなら全列
5. dplyr_row_slice()で行を抽出
```

### フローチャート

```mermaid
flowchart TD
    A[distinct開始] --> B{変数指定あり?}
    B -->|No| C[全列を判定対象に]
    B -->|Yes| D[distinct_prepare]
    C --> D
    D --> E{計算式あり?}
    E -->|Yes| F[mutateで列追加]
    E -->|No| G[変数名解決]
    F --> G
    G --> H[判定列を選択]
    H --> I[vec_unique_loc]
    I --> J{.keep_all?}
    J -->|TRUE| K[全列を出力対象に]
    J -->|FALSE| L[判定列のみ出力対象に]
    K --> M[dplyr_row_slice]
    L --> M
    M --> N[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 最初の行を保持 | 重複がある場合、最初の行のみが保持される | 常時 |
| BR-02 | 行順序の保持 | 出力行は入力と同じ順序で出現する | 常時 |
| BR-03 | グループ内判定 | grouped_dfでは各グループ内で一意性を判定 | grouped_df使用時 |
| BR-04 | 全列判定 | 変数指定なしの場合は全列で一意性を判定 | 引数省略時 |
| BR-05 | 計算列の追加 | 計算式を指定した場合、mutateで列が追加される | 式を含む場合 |

### 計算ロジック

- 一意性判定は`vctrs::vec_unique_loc()`を使用
- 同一値の組み合わせの中から最初の行（最小インデックス）を選択
- NA値は他のNA値と同じとみなされる

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | abort | 指定した変数がデータに存在しない | 正しい変数名を指定 |

### リトライ仕様

該当なし（同期処理のため）

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

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

## パフォーマンス要件

- `vctrs::vec_unique_loc()`により`unique.data.frame()`より大幅に高速
- 大規模データセットでも効率的に動作

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

- 入力データの検証はdplyrフレームワーク内で実施

## 備考

- `pick()`を使用してtidy-select構文で列を選択可能
- grouped_dfでは一意性判定にグループ化変数が自動的に含まれる

---

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

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

### 推奨読解順序

#### Step 1: distinct関数の基本構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | distinct.R | `R/distinct.R` | distinct関数の定義と基本フロー |

**読解のコツ**: distinct関数は比較的シンプルで、distinct_prepareとvec_unique_locが主要な処理を担当する。

**主要処理フロー**:
- **68-70行目**: distinct関数定義（ジェネリック関数）
- **129-145行目**: distinct.data.frame実装

#### Step 2: 準備処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | distinct.R | `R/distinct.R` | distinct_prepare関数 |

**主要処理フロー**:
- **76-126行目**: distinct_prepare関数
- **87-93行目**: 変数指定なしの場合の処理
- **96-98行目**: 計算列の追加（add_computed_columns）
- **119-125行目**: 保持する列の決定

#### Step 3: 一意行の検出を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | distinct.R | `R/distinct.R` | vec_unique_locの使用 |

**主要処理フロー**:
- **140行目**: dplyr_col_selectで判定列を選択
- **141行目**: vec_unique_loc(cols)で一意行の位置を取得
- **144行目**: dplyr_row_sliceで行を抽出

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

```
distinct(.data, ..., .keep_all)
    │
    └─ UseMethod("distinct")
           │
           └─ distinct.data.frame(.data, ..., .keep_all)
                  │
                  ├─ distinct_prepare(.data, vars, group_vars, .keep_all)
                  │      │
                  │      ├─ (vars空) → 全列を使用
                  │      ├─ add_computed_columns(.data, vars)
                  │      │      └─ mutateで計算列追加
                  │      └─ 保持列の決定
                  │
                  ├─ dplyr_col_select(out, prep$vars)
                  │
                  ├─ vec_unique_loc(cols)
                  │      └─ 一意行の位置を返す
                  │
                  ├─ dplyr_col_select(out, prep$keep)
                  │
                  └─ dplyr_row_slice(out, loc)
```

### データフロー図

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

.data (data.frame)  ───▶ 判定列決定 ───▶ 一意性判定 ───▶ 行選択
      │                       │                  │              │
      │                       ▼                  ▼              ▼
      │              計算列追加(必要時)    vec_unique_loc      loc
      │                       │                  │              │
      └───────────────────────┴──────────────────┴──────────────┘
                                                                │
                                                                ▼
                                          (.keep_all) ? 全列 : 判定列
                                                                │
                                                                ▼
                                                      dplyr_row_slice
                                                                │
                                                                ▼
                                                        結果data.frame
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| distinct.R | `R/distinct.R` | ソース | distinct関数の実装 |
| generics.R | `R/generics.R` | ソース | dplyr_row_slice, dplyr_col_select |
| colwise-distinct.R | `R/colwise-distinct.R` | ソース | distinct_at/if/all（非推奨） |
| pick.R | `R/pick.R` | ソース | pick関数（列選択） |
