import pandas as pd
from pathlib import Path
import re
from collections import Counter, defaultdict
csv_path = Path('/home/ubuntu/keiba_requirements_wide_research.csv')
out_path = Path('/home/ubuntu/common_requirements_issues_from_wide_research.md')
df = pd.read_csv(csv_path)
cols = list(df.columns)
# Identify likely issue columns and context columns
issue_cols = [c for c in cols if any(k in c.lower() for k in ['risk', 'gap', '課題', '不足', 'risks_and_gaps'])]
axis_cols = [c for c in cols if any(k in c.lower() for k in ['axis', '観点'])]
summary_cols = [c for c in cols if any(k in c.lower() for k in ['summary', 'executive'])]
# Fallback if column names differ
if not issue_cols:
issue_cols = cols
axis_col = axis_cols[0] if axis_cols else cols[0]
texts = []
records = []
for idx, row in df.iterrows():
axis = str(row.get(axis_col, f'分析軸{idx+1}'))
for col in issue_cols:
val = row.get(col, '')
if pd.isna(val):
continue
text = str(val)
if len(text.strip()) < 5:
continue
texts.append(text)
records.append((axis, col, text))
# Define thematic buckets based on recurring issue expressions seen in the wide research output.
themes = {
'要求ID・検証可能性・受入基準の不足': [r'要件ID', r'ID', r'検証', r'受入', r'合否', r'テスト', r'レビュー.*具体', r'判定基準', r'客観'],
'フェーズ移行条件・ロードマップの曖昧さ': [r'フェーズ.*移行', r'移行条件', r'到達条件', r'フェーズ.*曖昧', r'ロードマップ', r'いつ.*進む'],
'属人性・意思決定基準の暗黙化': [r'属人', r'社長.*依存', r'判断基準', r'承認.*基準', r'最終判断', r'不在', r'ブレ'],
'データ取得・外部サービス制約への運用対策不足': [r'JRDB', r'データ取得', r'取得制限', r'外部', r'規約', r'API', r'フェイルセーフ', r'リトライ', r'認証'],
'データ品質・欠損・異常値管理の不足': [r'欠損', r'異常値', r'重複', r'データ品質', r'取得失敗', r'補完', r'監査ログ', r'品質'],
'UI/UXの図解・画面遷移・デザイン基準不足': [r'画面遷移', r'ワイヤーフレーム', r'UI', r'UX', r'デザインガイドライン', r'アクセシビリティ', r'レスポンシブ', r'配置'],
'AI/MLモデルの評価・再学習・リーク対策不足': [r'モデル', r'機械学習', r'AI', r'再学習', r'リーク', r'バックテスト', r'特徴量', r'評価指標', r'過学習'],
'セキュリティ・権限・監査・バックアップの不足': [r'セキュリティ', r'権限', r'アクセス制御', r'監査', r'ログ', r'バックアップ', r'認証情報'],
'非機能要件・運用品質の定量化不足': [r'非機能', r'性能', r'可用性', r'保守性', r'運用', r'障害', r'復旧', r'定量'],
'文章構造・正式文書化・重複整理の不足': [r'誤変換', r'会話ログ', r'重複', r'未整理', r'文章', r'構造', r'正式', r'第三者', r'読み解き'],
'リスク・例外・停止条件の明文化不足': [r'リスク', r'例外', r'停止条件', r'ロールバック', r'失敗時', r'エラーハンドリング', r'対処'],
'KPI・収支指標・ロジック採用基準の具体化不足': [r'回収率', r'KPI', r'指標', r'採用基準', r'ドローダウン', r'的中率', r'資金', r'成績'],
}
bucket_hits = defaultdict(list)
for axis, col, text in records:
for theme, pats in themes.items():
if any(re.search(p, text, flags=re.IGNORECASE) for p in pats):
bucket_hits[theme].append((axis, col, text))
# Rank themes by number of distinct axes and total mentions.
ranked = []
for theme, hits in bucket_hits.items():
axes = sorted(set(a for a,_,_ in hits))
ranked.append((theme, len(axes), len(hits), axes, hits))
ranked.sort(key=lambda x: (x[1], x[2]), reverse=True)
# Also extract frequent keywords.
all_issue_text = '\n'.join(texts)
keywords = ['属人性','フェーズ','移行条件','検証','受入','要件ID','JRDB','データ取得','欠損','異常値','UI','UX','画面遷移','ワイヤーフレーム','アクセシビリティ','セキュリティ','権限','監査','バックアップ','再学習','バックテスト','リーク','停止条件','KPI','採用基準','非機能','規約','リトライ','エラーハンドリング']
kw_counts = Counter({kw: len(re.findall(re.escape(kw), all_issue_text, flags=re.IGNORECASE)) for kw in keywords})
lines = []
lines.append('# 並列分析の生データから抽出した要件定義上の共通課題\n')
lines.append('**対象データ:** `/home/ubuntu/keiba_requirements_wide_research.csv` \n')
lines.append(f'**分析件数:** {len(df)}件の並列分析結果 \n')
lines.append('**抽出方法:** 各分析軸の「リスクと不足」系カラムを中心に、反復出現する課題語彙と意味的に同一の指摘を統合した。\n')
lines.append('## 1. 総括\n')
lines.append('並列分析の生データから見ると、共通課題は「要件の思想が弱い」のではなく、むしろ思想が強すぎるために、第三者が実装・検証・保守できる形式へ落とし切れていない点に集中している。特に、フェーズ移行条件、採用判断基準、検証方法、外部データ制約、UI図解、データ品質、非機能要件が複数の分析軸で繰り返し指摘されている。\n')
lines.append('| 優先度 | 共通課題 | 出現した分析軸数 | 総指摘数 | 要約 |')
lines.append('|---|---|---:|---:|---|')
priority = 1
for theme, axis_count, hit_count, axes, hits in ranked:
summary = {
'要求ID・検証可能性・受入基準の不足':'要件が強く書かれている一方、各要件を合否判定するID・検証方法・受入条件が不足している。',
'フェーズ移行条件・ロードマップの曖昧さ':'フェーズ構成は明確だが、次フェーズへ進む客観条件が十分に定義されていない。',
'属人性・意思決定基準の暗黙化':'社長判断を中核に置く設計は強いが、判断基準が暗黙知化しやすい。',
'データ取得・外部サービス制約への運用対策不足':'外部データ制約の意識は高いが、失敗時処理・リトライ・監査証跡の具体化が必要である。',
'データ品質・欠損・異常値管理の不足':'分析精度を左右する欠損、重複、異常値、取得失敗へのルールが不足している。',
'UI/UXの図解・画面遷移・デザイン基準不足':'画面要素は詳細だが、画面遷移図、ワイヤーフレーム、デザイン基準への落とし込みが不足している。',
'AI/MLモデルの評価・再学習・リーク対策不足':'モデル構想は高度だが、リーク防止、再学習、モデル監視、採用停止基準の形式化が必要である。',
'セキュリティ・権限・監査・バックアップの不足':'運用システムとして必要な権限、ログ、監査、バックアップの定量要件が不足している。',
'非機能要件・運用品質の定量化不足':'壊れない状態を目指しているが、性能、可用性、復旧時間などの数値基準が薄い。',
'文章構造・正式文書化・重複整理の不足':'原文の熱量は高いが、会話ログ的表現や重複を正式要件に変換する必要がある。',
'リスク・例外・停止条件の明文化不足':'失敗時、例外時、停止時、ロールバック時の具体的なルールが不足している。',
'KPI・収支指標・ロジック採用基準の具体化不足':'回収率重視は明確だが、最低検証件数や停止条件などの採用基準が不足している。',
}.get(theme, '複数軸で反復された課題であり、正式要件化が必要である。')
lines.append(f'| {priority} | **{theme}** | {axis_count} | {hit_count} | {summary} |')
priority += 1
lines.append('\n## 2. 頻出キーワード\n')
lines.append('| キーワード | 出現回数 |')
lines.append('|---|---:|')
for kw, cnt in kw_counts.most_common():
if cnt:
lines.append(f'| {kw} | {cnt} |')
lines.append('\n## 3. 共通課題の詳細\n')
for i, (theme, axis_count, hit_count, axes, hits) in enumerate(ranked, 1):
lines.append(f'### {i}. {theme}\n')
lines.append(f'この課題は、**{axis_count}個の分析軸**で確認され、合計**{hit_count}件**の関連指摘があった。関連する分析軸は、{", ".join(axes[:8])}' + (' など' if len(axes) > 8 else '') + 'である。\n')
# representative snippets
lines.append('| 代表的な指摘元 | 指摘の要旨 |')
lines.append('|---|---|')
seen = set()
shown = 0
for axis, col, text in hits:
clean = re.sub(r'\s+', ' ', text).strip()
# choose sentence-like first 140 chars containing keywords
snippet = clean[:180] + ('…' if len(clean) > 180 else '')
key = (axis, snippet[:60])
if key in seen:
continue
seen.add(key)
lines.append(f'| {axis} | {snippet.replace("|", "/")} |')
shown += 1
if shown >= 3:
break
lines.append('')
lines.append('## 4. 要件定義へ反映すべき改善アクション\n')
lines.append('| 改善アクション | 具体的に追加する成果物 | 効果 |')
lines.append('|---|---|---|')
actions = [
('全要件にIDを付与する', 'F-001、UI-001、D-001、ML-001、NFR-001の要件台帳', 'レビュー、実装、テスト、変更管理を追跡可能にする。'),
('フェーズ移行条件を数値化する', 'Phase別Exit Criteria表', '「いつ次に進むか」を主観判断から客観判断へ変える。'),
('社長判断を採用基準へ変換する', 'ロジック採用・停止基準表', '属人性を残しつつ、判断の再現性を上げる。'),
('外部データ取得の障害対応を定義する', '取得スケジュール、禁止時間、リトライ、失敗通知、監査ログ表', '規約違反、欠損、手動復旧漏れを防ぐ。'),
('データ品質ルールを追加する', '欠損・重複・異常値・未来データ混入防止チェックリスト', 'AI/MLの評価信頼性を高める。'),
('UIを図に落とす', '画面遷移図、ワイヤーフレーム、コンポーネント一覧', '実装者ごとの画面解釈のズレを防ぐ。'),
('非機能要件を定量化する', '性能、可用性、RTO/RPO、権限、監査、バックアップ要件表', '「壊れない状態」を測定可能にする。'),
('原文と正式要件を分離する', '原文ログ、正式要件、決定事項、未決事項の4分冊または章分け', '熱量を保持しつつ、第三者実装可能性を上げる。'),
]
for a,b,c in actions:
lines.append(f'| {a} | {b} | {c} |')
lines.append('\n## 5. 最重要課題トップ5\n')
lines.append('最優先で対応すべき課題は、第一に**フェーズ移行条件の明確化**、第二に**ロジック採用・停止基準の明文化**、第三に**要件IDと受入基準の付与**、第四に**データ品質・外部データ取得失敗時の運用設計**、第五に**UIの図解化**である。これらは、要件の内容を変える作業ではなく、すでに存在する強い思想を、第三者が迷わず実装・検証できる形へ変換する作業である。\n')
out_path.write_text('\n'.join(lines), encoding='utf-8')
print(out_path)
print('rows', len(df), 'issue_cols', issue_cols, 'themes', len(ranked))
分析スクリプト: 要件定義の共通課題をCSVから抽出
元ファイル: システム要件定義の分析と汎用化方法/extract_common_issues.py
要約
競馬予想システムの広域リサーチCSV(keiba_requirements_wide_research.csv)を読み込み、リスク・課題列を正規表現で11テーマに分類・集計するPythonスクリプト。要件ID不足・フェーズ移行条件の曖昧さ・属人性などの再発課題を抽出しMarkdownにまとめる。
要点
- pandasでCSVのrisk/gap/課題列を自動検出
- 11テーマに正規表現で分類
- テーマを言及軸数・出現数でランク付け
- 共通課題をMarkdownレポートに出力
- 汎用化対象の要件定義の弱点を体系化