# 機能設計書 55-ntile

## 概要

本ドキュメントは、dplyrパッケージにおける`ntile()`関数の機能設計を定義する。`ntile()`はベクトルをn個のバケット（グループ）に分割するランキング関数である。

### 本機能の処理概要

**業務上の目的・背景**：データ分析において、データを等分割したグループに分類することは頻繁に発生する要件である。例えば、顧客を売上高で4分位（四分位数）に分けたり、学生を成績で上位/中位/下位の3グループに分類したりする場合である。`ntile()`は、このようなデータの分位数ベースのグループ分けを簡潔に実現するために設計された。

**機能の利用シーン**：データの分位数によるグループ化、パーセンタイルベースのセグメンテーション、等分割によるバケット作成、ヒストグラムのビン作成などに利用される。

**主要な処理内容**：
1. 入力ベクトルまたは行番号に基づいてデータをn個のバケットに分割する
2. データ数がnで均等に割り切れない場合、大きいバケットが先に配置される
3. タイ（同じ値）は無視され、均等なバケットサイズが優先される
4. 欠損値（NA）はバケットNAとして扱われる

**関連システム・外部連携**：内部で`row_number()`関数を使用してランキングを計算する。`if_else()`関数を使用してバケット計算を行う。

**権限による制御**：本関数に権限による実行制御は存在しない。

## 関連画面

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

## 機能種別

計算処理 / ランキング関数

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | vector / data.frame | No | グループ分けの基準となるベクトル。省略時はrow_number()を使用 | - |
| n | integer | Yes | 作成するバケット数 | 正の整数であること |

### 入力データソース

Rのベクトル、データフレーム、または省略（行番号を使用）。`mutate()`内で使用される場合は、現在処理中のグループのデータが対象となる。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | integer | 1からnまでの整数ベクトル。各要素が属するバケット番号 |

### 出力先

関数の戻り値として返却される。通常は`mutate()`の結果としてデータフレームの列に格納される。

## 処理フロー

### 処理シーケンス

```
1. 引数の検証
   └─ nが正の整数であることを確認
2. xが指定されている場合
   └─ row_number(x)でランクを計算
3. xが省略されている場合
   └─ 行番号をそのまま使用
4. バケットサイズの計算
   └─ 非NA要素数をnで割った商と余りを計算
   └─ 余りがある場合、先頭のバケットを1要素大きくする
5. バケット番号の割り当て
   └─ 各要素に対してバケット番号を計算
6. 整数ベクトルとして結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: ntile] --> B{nが正の整数か}
    B -->|No| C[エラー: n must be positive]
    B -->|Yes| D{xが指定されているか}
    D -->|Yes| E[row_number でランク計算]
    D -->|No| F[行番号をそのまま使用]
    E --> G[非NA要素数を取得]
    F --> G
    G --> H{要素数 == 0?}
    H -->|Yes| I[すべてNA_integerを返す]
    H -->|No| J[バケットサイズ計算]
    J --> K[大きいバケット数とサイズを計算]
    K --> L[小さいバケット数とサイズを計算]
    L --> M[if_elseでバケット番号計算]
    M --> N[整数ベクトルを返却]
    I --> O[終了]
    N --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-55-01 | 均等分割 | データをできるだけ均等にn個のバケットに分割 | 常に適用 |
| BR-55-02 | 大バケット優先 | 均等に割り切れない場合、大きいバケットを先頭に配置 | 余りがある場合 |
| BR-55-03 | タイ無視 | 同じ値でも異なるバケットに割り当てられる可能性がある | タイ存在時 |
| BR-55-04 | NA処理 | 欠損値にはNAバケット番号を付与 | NA存在時 |
| BR-55-05 | バケット番号範囲 | バケット番号は1からnの範囲 | 常に適用 |

### 計算ロジック

- バケットサイズの計算（SQL Server 2000+の定義に基づく）:
  - `n_larger` = `len %% n`（大きいバケットの数）
  - `n_smaller` = `n - n_larger`（小さいバケットの数）
  - `larger_size` = `ceiling(len / n)`（大きいバケットのサイズ）
  - `smaller_size` = `floor(len / n)`（小さいバケットのサイズ）
- 例: `ntile(1:8, 3)` → `c(1, 1, 1, 2, 2, 2, 3, 3)`
  - 8要素を3バケットに分割
  - バケット1: 3要素、バケット2: 3要素、バケット3: 2要素

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

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

本関数はインメモリのベクトル操作のみを行い、データベース操作は行わない。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 引数エラー | nが正の整数でない | 正の整数を指定する |
| - | 引数エラー | nが0以下 | 1以上の値を指定する |
| - | 引数エラー | nがNA | 有効な整数を指定する |

### リトライ仕様

リトライは不要（同期的なインメモリ処理）。

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

トランザクション管理は不要（ステートレスな関数型処理）。

## パフォーマンス要件

- 計算量: O(n log n)（row_numberによるソートが支配的）
- メモリ使用量: O(n)（出力ベクトル用）
- 整数オーバーフローを防ぐための実装が含まれている

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

特別なセキュリティ考慮事項はない。

## 備考

- SQLの`NTILE()`ウィンドウ関数に相当
- 他のランキング関数と異なり、タイを考慮しない
- 分位数によるグループ分けに最適

---

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

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

### 推奨読解順序

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

ntile関数は、row_number関数とif_else関数を組み合わせて実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | rank.R | `R/rank.R` | ntile関数の定義とドキュメント（79-138行目） |

**読解のコツ**: ドキュメントのURL（119行目）はSQL Serverの実装を参照しており、バケット計算のアルゴリズムの元ネタである。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | rank.R | `R/rank.R` | `ntile()`関数の定義（104-138行目） |

**主要処理フロー**:
1. **104行目**: `ntile(x = row_number(), n)`関数のシグネチャ（デフォルト引数に注目）
2. **107-109行目**: xが指定された場合、row_number(x)でランクを計算
3. **110行目**: 非NA要素数を計算
4. **112-116行目**: nの検証（正の整数であること）
5. **120-137行目**: バケットサイズ計算とバケット番号割り当て

#### Step 3: バケット計算アルゴリズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rank.R | `R/rank.R` | 118-137行目のバケット計算ロジック |

**主要処理フロー**:
- **120行目**: 要素数0の場合の処理
- **123-127行目**: 大小バケットのサイズ計算
- **129行目**: 大きいバケットの閾値計算
- **130-134行目**: if_elseでバケット番号を計算
- **136行目**: floor()で整数に変換

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

```
ntile(x, n)
    │
    ├─ [xが指定] row_number(x) [ランク計算]
    │
    ├─ vec_size(x) [サイズ取得]
    │
    ├─ vec_detect_missing(x) [NA検出]
    │
    ├─ check_number_whole(n) [n検証]
    │
    ├─ vec_cast(n, integer()) [整数変換]
    │
    └─ if_else(...) [バケット番号計算]
           └─ floor() [整数化]
```

### データフロー図

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

x = [5,1,3,2,2]   ───▶  ntile(x, n=2)              ───▶  [2, 1, 2, 1, 1]
      n = 2              │
                         ├─▶ row_number(x) → [5,1,3,2,4]
                         │
                         ├─▶ len = 5, n = 2
                         │   n_larger = 5 %% 2 = 1
                         │   larger_size = 3
                         │   smaller_size = 2
                         │
                         └─▶ if_else で振り分け
                               ├─ ランク1,2,3 → バケット1
                               └─ ランク4,5 → バケット2
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| rank.R | `R/rank.R` | ソース | ntile関数の実装 |
| if-else.R | `R/if-else.R` | ソース | if_else関数の実装 |
| test-rank.R | `tests/testthat/test-rank.R` | テスト | ntileのユニットテスト（54-113行目） |
