機能 #316
完了ノーツレーダー細分化 Phase 3: B分類パターン軸の 0–200 anchor 実データ較正と本体正規化
説明
目的¶
#313 設計の数値後決め事項(§8)のうち、B分類パターン軸の 0–200 正規化を担う後続チケット。Phase 2 (#315) で算出済みの 15 個の B分類サブ軸 raw 値に対し、実データ分布から 0–200 anchor テーブルを確定し、既存6主軸と同様に本体 NotesRadarCalculator で正規化する。
親設計: #313。前提: Phase 2 (#315) の raw 算出・伝送・保持縦串。
方針確認(ユーザー指示, 2026-06-19): anchor は server 側ではなく本後続チケットで決定する。設計 §8-9 の推奨(本体で anchor 正規化+raw も併送)に沿う。
対象 raw サブ軸(#315 で送出済み・全て raw)¶
- NOTES:
notes_density_peak_10s/notes_endgame - PATTERN(新規親):
pattern_jack/pattern_trill/pattern_kaidan/pattern_randa - CHORD:
chord_gachioshi/chord_denim/chord_jika - SCRATCH:
scratch_single/scratch_ren/scratch_complex - RHYTHM(新規親):
rhythm_24th/rhythm_henpyoshi/rhythm_irregular
スコープ¶
- 実データ収集: 実譜面群を全更新で再計算し、各 raw サブ軸の分布(分位点)を採取(#145 と同方式のバックフィル)。
-
anchor テーブル確定: 既存6軸(
NOTES_DENSITY_ANCHORS/NOTES_RADAR_ANCHORS等)と同型の*_SCORE_ANCHORS/*_RADAR_ANCHORSを raw 分位点から決定。raw が count か density(/s) か(duration 正規化)も合わせて確定。 -
本体正規化の実装:
NotesRadarCalculator.calculateで各サブ軸をradarFromAnchors(...)で 0–200 化。raw も併送を維持(#315 のfeature_*raw 伝送は残す)。PATTERN / RHYTHM の親軸 0–200 値を新設するかも本チケットで決定(ドリルダウン用、#293 接続)。 - テスト: anchor 区間の境界(直下/直上)・単調性・0/200 クランプ、既存6軸と raw 値の不変(回帰)。
数値後決め事項(設計 §8 のうち本チケット該当)¶
- §8-1: 全パターン軸の 0–200 anchor テーブル(実 raw 分布の分位点)。
- §8-2: jack/trill/階段/連皿の ms 閾値を固定 ms か BPM 相対か(再調整)。
- §8-3: 乱打エントロピー閾値・窓長 K の再調整。
- §8-4: 皿複合の随伴窓
T_fukugo・寄与重み。 - §8-5: デニム/ガチ押し size 閾値・密度ゲート。
- §8-6: リズム off-grid 許容
T_offgrid_ratio・24分許容幅。 - §8-9: パターン軸 raw を本体正規化(本チケットで本体採用)+raw 併送の責務分界。
関連¶
- #313(設計)/ #314(Phase 1)/ #315(Phase 2: raw 算出)
- #293(弱点分析・地力推定パネル)への接続(親→サブのドリルダウン)。
備考¶
Phase 2 で導入した暫定閾値(T_JACK_MS=110 等)と暫定アルゴリズムは、本チケットの実データ較正で確定値へ差し替える。閾値は定数差し替えで完結する設計。
エージェント ちゃん さんが17日前に更新
- 期日 を 2026/06/24 にセット
- 開始日 を 2026/06/19 から 2026/06/24 に変更
- 次のチケットに後続 機能 #315: ノーツレーダー細分化 Phase 2: B分類(パターン認識)サブ軸の新規解析実装 を追加
エージェント ちゃん さんが17日前に更新
- ステータス を 未着手 から 実装中 に変更
- 進捗率 を 0 から 10 に変更
実装に着手しました。
- ブランチ:
feature/316 - 対応方針: 実データ待ちの間に、データ非依存の**構造部分(15サブ軸の0–200正規化値・PATTERN/RHYTHM親軸の新設・raw併送維持・縦串配線・構造テスト)**を暫定線形anchorで先行実装。anchor数値の実データ較正(§8-1)と閾値再調整(§8-2〜8-6)はローカル採取CSV到着後に同ブランチで後続コミットする(定数差し替えで完結する設計)。
エージェント ちゃん さんが17日前に更新
サーバー側(izu-ir)引き継ぎ資料 — ノーツレーダー全特徴量の伝送仕様¶
本体(beatoraja)側で #314/#315/#316 を通じて実装した特徴量を、スコア送信時に IRChartData.values(Map<String,String>)として feature_* キー81個で IR へ送出する。サーバーは集計・保持・正規化・#293接続が責務。以下はサーバー実装に必要な全情報。
0. 最重要の注意(命名の非対称)¶
-
6主軸は歴史的経緯で
_radar接尾辞なしだが 0–200 正規化済み:feature_notes_density/feature_peak_density/feature_scratch_density/feature_charge/feature_chord/feature_soflan。 -
サブ軸は raw=接尾辞なし、正規化=
_radar接尾辞。例:feature_pattern_jack=raw、feature_pattern_jack_radar=0–200 正規化。 - したがって「
_radarが付くもの+6主軸」が 0–200 値、それ以外(6主軸を除く接尾辞なしfeature_*)は raw。
1. 81キーの分類¶
(A) 0–200 正規化値=33個(サーバーは原則これを再正規化しない)
- 6主軸(接尾辞なし):
feature_notes_density/_peak_density/_scratch_density/_charge/_chord/_soflan - 新規親軸2個:
feature_pattern_radar/feature_rhythm_radar(子サブ軸_radarの単純平均) - A分類サブ軸
_radar10個 / B分類サブ軸_radar15個
(B) raw 値=48個(サーバーが独自正規化したい場合に利用可)
- 6主軸の生入力・中間量23個(
feature_notes_total/_duration_seconds/_peak_window_notes/_scratch_total/_scratch_peak_window_notes/_ln_total/_ln_seconds_total/_charge_notes_during_ln/_charge_notes_during_ln_peak_window/_max_simultaneous_ln/_chord_timeline_count/_max_chord_size/_chord_peak_window_score/_chord_peak_window_notes/_speed_change_count/_stop_count/_scroll_change_count/_soflan_weighted_notes/_soflan_cluster_count/_soflan_affected_notes/_soflan_cluster_score/_soflan_max_change_rate/_soflan_stop_time_total) - A分類 raw 10個・B分類 raw 15個(下表)
2. ドリルダウン木構造(#293 親→サブ)¶
| 親軸(0–200キー) | 子サブ軸(raw / 正規化=_radar) |
|---|---|
NOTES feature_notes_density
|
notes_density_peak_1s, notes_local_hardness, notes_density_peak_10s, notes_endgame |
PEAK feature_peak_density
|
peak_burst_1s, peak_sustained_3s |
SCRATCH feature_scratch_density
|
scratch_bss, scratch_single, scratch_ren, scratch_complex |
CHARGE feature_charge
|
(サブ軸なし) |
CHORD feature_chord
|
chord_max_size, chord_gachioshi, chord_denim, chord_jika |
SOF-LAN feature_soflan
|
soflan_accel, soflan_decel, soflan_stop, soflan_visual |
PATTERN(新規)feature_pattern_radar |
pattern_jack, pattern_trill, pattern_kaidan, pattern_randa |
RHYTHM(新規)feature_rhythm_radar |
rhythm_24th, rhythm_henpyoshi, rhythm_irregular |
親軸値:feature_pattern_radar = (jack+trill+kaidan+randa の各 _radar)/4、feature_rhythm_radar = (24th+henpyoshi+irregular の各 _radar)/3。NOTES/PEAK/SCRATCH/CHORD/SOF-LAN は既存6主軸値がそのまま親。
3. 正規化方式(0–200)¶
-
既存6主軸と完全に同一方式:raw を分位点アンカー(
*_SCORE_ANCHORS)→ 固定14点 radar カーブ{0, 14.29, 35.71, 71.43, 100, 116.95, 133.90, 150.85, 167.80, 184.75, 191.53, 194.92, 198.31, 200}へ線形補間。実装はNotesRadarCalculator.radarFromAnchors。 -
パーセンタイル方式:アンカーは実譜面 52,449件の raw 分布の分位点。採用分位点列 ≈
[0, 12.3, 34.2, 64.7, 82.5, 85.9, 88.9, 91.8, 94.4, 96.8, 97.9, 98.3, 99.0, 99.3]%(既存 NOTES 軸アンカーから逆算し全軸で共有)。 - 意味:radar 値は「母集団内の難度順位」を表し、全軸で radar≈100 が同じパーセンタイル帯に対応=軸横断比較が可能(#293 の弱点抽出前提)。
-
外れ値耐性:最上位アンカー(≈99.3%ile)超は 200 にクランプ。min-max ではないので極端譜面でスケールが歪まない。導出は本体リポジトリ
scripts/analyze_notes_radar.py(B分類)/scripts/analyze_notes_radar_aclass.py(A分類)で再現可能。
4. raw の count / density 区分(正規化入力)¶
正規化は軸ごとに「累積イベント数=秒速 density(/duration)」「rate/ratio/size=raw のまま(intensive)」を使い分け(duration 相関分析に基づく)。サーバーが raw から独自に再正規化する場合は同じ区分を踏襲すること。
- DENSITY(/秒):notes_endgame, pattern_jack/trill/kaidan/randa, chord_gachioshi/denim/jika, scratch_single/ren/complex, rhythm_24th, soflan_stop, soflan_visual, scratch_bss
- INTENSIVE(raw):notes_density_peak_10s, rhythm_henpyoshi, rhythm_irregular, notes_density_peak_1s, notes_local_hardness, peak_burst_1s, peak_sustained_3s, chord_max_size, soflan_accel, soflan_decel
5. 特例・留意点¶
-
soflan_visualは非ゼロが全体の約0.1%。母集団分位点だと全アンカー0=任意正値が200飽和(退化)するため、この軸だけ非ゼロ部分集合で較正(アンカー約47譜面ベース)。サーバーが集計する際もこの軸はサンプル数が極小である点に留意。 - 疎な軸(例 chord_denim 非ゼロ21%、soflan_accel/decel 約20%):母集団パーセンタイル正規化の性質上、「そのパターンを少しでも持つ=少数派」なので radar が高めに出る。仕様どおりだが #293 の閾値設定時に考慮を。
-
アンカーは母集団(採取譜面群)依存:将来 anchor を更新する場合は
gradle updateAllSongs(--all)で全件再計算→songdata.dbのsongテーブルからfeature_*を CSV エクスポート→上記 scripts 実行→定数差し替え、で完結する設計(本体側作業)。 - ms 閾値(T_JACK_MS=110 等)は設計§5の暫定値を据え置き。raw 算出ロジックは固定。
6. 集計責務(サーバー側)¶
- 本体は 0–200 正規化+raw 併送まで。サーバーは既存6軸と同じ集計(達成率重み
min(exscore/(notes*2), 1.0)・軸ごと上位N平均・EAV保持)をサブ軸へ拡張する想定。 -
既に 0–200 のキー(6主軸+
_radar+親軸)を二重正規化しないこと。 独自正規化したい場合のみ raw キー(§1-B)を使用。 - 各サブ軸の集計上位N(設計§8-7)、soflan accel/decel/visual の二重計上回避重み(§8-8)はサーバー側で後決めの未確定事項。
7. 値の性質¶
- 全
feature_*は 譜面ごとに決定的(プレイ内容に依存しない譜面特徴)。文字列化はDouble.toString。 - 旧スキーマ/旧クライアント互換:未知キーは EAV なので無痛。本体DBも auto-ALTER で列追加のみ(既存値不変)。
8. 参照¶
エージェント ちゃん さんが17日前に更新
- ステータス を 実装中 から 完了 に変更
- 進捗率 を 10 から 100 に変更
対応が完了しました。
概要¶
ノーツレーダー細分化 Phase 3。#314(A分類10)/#315(B分類15) で raw のみだった全サブ軸を、既存6軸と同一のパーセンタイル方式で実データ較正し 0–200 正規化。raw 併送維持。主軸6+全サブ軸25 が「raw+0–200」で揃い、サーバー(izu-ir) へ完全なデータを渡せる状態になった。
変更点¶
- B分類15軸+親軸2(PATTERN/RHYTHM)を 0–200 正規化(Phase 3a 構造 → 3b 実データ較正)。
-
A分類10軸を 0–200 正規化(Phase 3c)。
NotesRadarFeature54→81 component。A分類は既存主軸配下のため新規親軸なし。 - 較正は実譜面 52,449件 のローカル全更新バックフィルの raw 分布から。既存 NOTES 軸アンカーのパーセンタイル方式を逆算し、同方式・同14点 radar カーブを全サブ軸へ適用(軸横断で radar 値が比較可能)。
- count/density を軸ごとに個別判断(duration 相関)。累積カウントは density(/s)、rate/ratio/size は raw(intensive)。
-
soflan_visualのみ非ゼロ部分集合較正(非ゼロ0.1%で母集団だと全アンカー0=200飽和の退化を回避)。 -
既存6軸は完全に不変(develop とバイト一致を検証、回帰テスト green)。IR 送出
feature_*キーは 81。 - 変更ファイル:
NotesRadarCalculator.java/NotesRadarFeature.java/SQLiteSongDatabaseAccessor.java/SongDataDto.java/SongDataResultSetHandler.java/IRChartData.java+テスト3+scripts/analyze_notes_radar.py/analyze_notes_radar_aclass.py+docs/notes-radar-subdivision-design.md(§8') +.gitignore - サーバー引き継ぎ資料(81キーの分類・ドリルダウン木・集計責務・再較正手順)は本チケットの前コメントに記載済み。
テスト¶
-
xvfb-run -a ./gradlew test: 成功(全 green)。各サブ軸の正規化値を feature 自身の raw+duration から再計算し一致検証、アンカー→radarカーブ一致、単調性・クランプ・親=子平均・既存6軸&raw不変回帰・EMPTY・DTO往復・旧スキーマ0埋め・IRキー81。
関連 MR¶
- !70 (feature/316 → develop, マージ済み)
エージェント ちゃん さんが16日前に更新
- 次のチケットに先行 機能 #317: ノーツレーダー細分化: CHARGE/LN サブ軸の新設と BSS の LN 再分類 を追加