@nahisaho/satori 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENCE +0 -0
  2. package/README.md +191 -0
  3. package/bin/satori.js +95 -0
  4. package/package.json +29 -0
  5. package/src/.github/skills/scientific-academic-writing/SKILL.md +361 -0
  6. package/src/.github/skills/scientific-academic-writing/assets/acs_article.md +199 -0
  7. package/src/.github/skills/scientific-academic-writing/assets/elsevier_article.md +244 -0
  8. package/src/.github/skills/scientific-academic-writing/assets/ieee_transactions.md +212 -0
  9. package/src/.github/skills/scientific-academic-writing/assets/imrad_standard.md +181 -0
  10. package/src/.github/skills/scientific-academic-writing/assets/nature_article.md +179 -0
  11. package/src/.github/skills/scientific-academic-writing/assets/qiita_technical_article.md +385 -0
  12. package/src/.github/skills/scientific-academic-writing/assets/science_research_article.md +169 -0
  13. package/src/.github/skills/scientific-bioinformatics/SKILL.md +220 -0
  14. package/src/.github/skills/scientific-biosignal-processing/SKILL.md +357 -0
  15. package/src/.github/skills/scientific-causal-inference/SKILL.md +347 -0
  16. package/src/.github/skills/scientific-cheminformatics/SKILL.md +196 -0
  17. package/src/.github/skills/scientific-data-preprocessing/SKILL.md +413 -0
  18. package/src/.github/skills/scientific-data-simulation/SKILL.md +244 -0
  19. package/src/.github/skills/scientific-doe/SKILL.md +360 -0
  20. package/src/.github/skills/scientific-eda-correlation/SKILL.md +141 -0
  21. package/src/.github/skills/scientific-feature-importance/SKILL.md +208 -0
  22. package/src/.github/skills/scientific-image-analysis/SKILL.md +310 -0
  23. package/src/.github/skills/scientific-materials-characterization/SKILL.md +368 -0
  24. package/src/.github/skills/scientific-meta-analysis/SKILL.md +352 -0
  25. package/src/.github/skills/scientific-metabolomics/SKILL.md +326 -0
  26. package/src/.github/skills/scientific-ml-classification/SKILL.md +265 -0
  27. package/src/.github/skills/scientific-ml-regression/SKILL.md +215 -0
  28. package/src/.github/skills/scientific-multi-omics/SKILL.md +303 -0
  29. package/src/.github/skills/scientific-network-analysis/SKILL.md +257 -0
  30. package/src/.github/skills/scientific-pca-tsne/SKILL.md +235 -0
  31. package/src/.github/skills/scientific-pipeline-scaffold/SKILL.md +331 -0
  32. package/src/.github/skills/scientific-process-optimization/SKILL.md +215 -0
  33. package/src/.github/skills/scientific-publication-figures/SKILL.md +208 -0
  34. package/src/.github/skills/scientific-sequence-analysis/SKILL.md +389 -0
  35. package/src/.github/skills/scientific-spectral-signal/SKILL.md +227 -0
  36. package/src/.github/skills/scientific-statistical-testing/SKILL.md +240 -0
  37. package/src/.github/skills/scientific-survival-clinical/SKILL.md +239 -0
  38. package/src/.github/skills/scientific-time-series/SKILL.md +291 -0
@@ -0,0 +1,235 @@
1
+ ---
2
+ name: scientific-pca-tsne
3
+ description: |
4
+ PCA・t-SNE・UMAP による次元削減と空間マッピングのスキル。化学空間・特徴量空間・
5
+ 多技法融合空間の可視化を行う際に使用。Scientific Skills Exp-02, 03, 05, 07, 11, 13
6
+ で汎用的に使用されたパターン。
7
+ ---
8
+
9
+ # Scientific Dimensionality Reduction & Space Mapping
10
+
11
+ 高次元データを 2D/3D 空間に射影して構造を可視化するスキル。
12
+ PCA(線形)、t-SNE(非線形、局所構造保存)、UMAP(非線形、大域+局所)の
13
+ 3 手法を使い分ける。
14
+
15
+ ## When to Use
16
+
17
+ - 高次元データの構造を 2D で可視化したいとき
18
+ - 材料・化合物・サンプルのクラスター構造を発見したいとき
19
+ - 多技法の測定データを統合したいとき
20
+ - 主成分の寄与率や負荷量を解釈したいとき
21
+
22
+ ## Quick Start
23
+
24
+ ## 手法選択ガイド
25
+
26
+ | 手法 | 特徴 | 推奨場面 |
27
+ |---|---|---|
28
+ | PCA | 線形・解釈容易・寄与率あり | 最初の概観、PC 負荷量の解釈 |
29
+ | t-SNE | 非線形・局所構造保存 | クラスター分離の可視化 |
30
+ | UMAP | 非線形・大域+局所 | 大規模データ、連続的な勾配の可視化 |
31
+
32
+ ## 標準パイプライン
33
+
34
+ ### 1. PCA + スクリープロット
35
+
36
+ ```python
37
+ from sklearn.decomposition import PCA
38
+ from sklearn.preprocessing import StandardScaler
39
+ import matplotlib.pyplot as plt
40
+ import numpy as np
41
+ import pandas as pd
42
+
43
+ def pca_analysis(df, feature_cols, n_components=None, figsize=(14, 5)):
44
+ """PCA を実行し、スクリープロットと PC1-PC2 散布図を描画する。"""
45
+ scaler = StandardScaler()
46
+ X_sc = scaler.fit_transform(df[feature_cols])
47
+
48
+ if n_components is None:
49
+ n_components = min(len(feature_cols), 10)
50
+
51
+ pca = PCA(n_components=n_components)
52
+ pcs = pca.fit_transform(X_sc)
53
+
54
+ # スクリープロット
55
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
56
+
57
+ cumvar = np.cumsum(pca.explained_variance_ratio_) * 100
58
+ ax1.bar(range(1, n_components + 1), pca.explained_variance_ratio_ * 100,
59
+ color="steelblue", edgecolor="black")
60
+ ax1.plot(range(1, n_components + 1), cumvar, "ro-", linewidth=2)
61
+ ax1.set_xlabel("Principal Component")
62
+ ax1.set_ylabel("Explained Variance (%)")
63
+ ax1.set_title("Scree Plot", fontweight="bold")
64
+ ax1.axhline(y=80, color="gray", linestyle="--", alpha=0.5)
65
+
66
+ # PC1 vs PC2
67
+ ax2.scatter(pcs[:, 0], pcs[:, 1], alpha=0.6, s=30, edgecolors="k", linewidth=0.3)
68
+ ax2.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)")
69
+ ax2.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)")
70
+ ax2.set_title("PCA Projection", fontweight="bold")
71
+
72
+ plt.tight_layout()
73
+ plt.savefig("figures/pca_screeplot.png", dpi=300, bbox_inches="tight")
74
+ plt.close()
75
+
76
+ return pca, pcs
77
+
78
+
79
+ def pca_loading_plot(pca, feature_names, figsize=(10, 8)):
80
+ """PC1-PC2 の負荷量(loading)バイプロットを描画する。"""
81
+ loadings = pca.components_[:2].T
82
+
83
+ fig, ax = plt.subplots(figsize=figsize)
84
+ for i, name in enumerate(feature_names):
85
+ ax.arrow(0, 0, loadings[i, 0], loadings[i, 1],
86
+ head_width=0.02, head_length=0.01, fc="red", ec="red", alpha=0.7)
87
+ ax.text(loadings[i, 0] * 1.1, loadings[i, 1] * 1.1, name,
88
+ fontsize=8, ha="center")
89
+ ax.set_xlabel(f"PC1 Loading")
90
+ ax.set_ylabel(f"PC2 Loading")
91
+ ax.set_title("PCA Loading Plot (Biplot)", fontweight="bold")
92
+ ax.axhline(0, color="gray", linewidth=0.5)
93
+ ax.axvline(0, color="gray", linewidth=0.5)
94
+ plt.tight_layout()
95
+ plt.savefig("figures/pca_loadings.png", dpi=300, bbox_inches="tight")
96
+ plt.close()
97
+ ```
98
+
99
+ ### 2. t-SNE 可視化
100
+
101
+ ```python
102
+ from sklearn.manifold import TSNE
103
+
104
+ def tsne_visualization(X_scaled, labels, label_name="Group",
105
+ perplexity=30, random_state=42, figsize=(8, 8)):
106
+ """t-SNE 2D 射影を描画する。"""
107
+ tsne = TSNE(n_components=2, perplexity=perplexity,
108
+ random_state=random_state, n_iter=1000)
109
+ coords = tsne.fit_transform(X_scaled)
110
+
111
+ fig, ax = plt.subplots(figsize=figsize)
112
+ for label in sorted(set(labels)):
113
+ mask = labels == label
114
+ ax.scatter(coords[mask, 0], coords[mask, 1],
115
+ label=label, alpha=0.7, s=40, edgecolors="k", linewidth=0.3)
116
+ ax.set_xlabel("t-SNE 1")
117
+ ax.set_ylabel("t-SNE 2")
118
+ ax.set_title("t-SNE Projection", fontweight="bold")
119
+ ax.legend(title=label_name, bbox_to_anchor=(1.05, 1))
120
+ plt.tight_layout()
121
+ plt.savefig("figures/tsne_projection.png", dpi=300, bbox_inches="tight")
122
+ plt.close()
123
+ return coords
124
+ ```
125
+
126
+ ### 3. PCA + t-SNE 並列パネル(Exp-11, 13 パターン)
127
+
128
+ ```python
129
+ def pca_tsne_panel(df, feature_cols, hue_col, figsize=(16, 7)):
130
+ """PCA と t-SNE を横並びで描画する。"""
131
+ scaler = StandardScaler()
132
+ X_sc = scaler.fit_transform(df[feature_cols])
133
+
134
+ pca = PCA(n_components=2)
135
+ pcs = pca.fit_transform(X_sc)
136
+
137
+ tsne = TSNE(n_components=2, perplexity=30, random_state=42)
138
+ tsne_coords = tsne.fit_transform(X_sc)
139
+
140
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
141
+ labels = df[hue_col].values
142
+
143
+ for label in sorted(set(labels)):
144
+ mask = labels == label
145
+ ax1.scatter(pcs[mask, 0], pcs[mask, 1], label=label,
146
+ alpha=0.7, s=40, edgecolors="k", linewidth=0.3)
147
+ ax2.scatter(tsne_coords[mask, 0], tsne_coords[mask, 1], label=label,
148
+ alpha=0.7, s=40, edgecolors="k", linewidth=0.3)
149
+
150
+ ev = pca.explained_variance_ratio_ * 100
151
+ ax1.set_xlabel(f"PC1 ({ev[0]:.1f}%)")
152
+ ax1.set_ylabel(f"PC2 ({ev[1]:.1f}%)")
153
+ ax1.set_title("(A) PCA", fontweight="bold")
154
+ ax1.legend(title=hue_col)
155
+
156
+ ax2.set_xlabel("t-SNE 1")
157
+ ax2.set_ylabel("t-SNE 2")
158
+ ax2.set_title("(B) t-SNE", fontweight="bold")
159
+ ax2.legend(title=hue_col)
160
+
161
+ plt.tight_layout()
162
+ plt.savefig("figures/pca_tsne_panel.png", dpi=300, bbox_inches="tight")
163
+ plt.close()
164
+
165
+ # 座標を保存
166
+ coords_df = pd.DataFrame({
167
+ "PC1": pcs[:, 0], "PC2": pcs[:, 1],
168
+ "tSNE1": tsne_coords[:, 0], "tSNE2": tsne_coords[:, 1],
169
+ hue_col: labels,
170
+ })
171
+ coords_df.to_csv("results/pca_tsne_coordinates.csv", index=False)
172
+ return pca, tsne_coords
173
+ ```
174
+
175
+ ### 4. 階層的クラスタリング + Silhouette 最適 k
176
+
177
+ ```python
178
+ from scipy.cluster.hierarchy import linkage, fcluster, dendrogram
179
+ from sklearn.metrics import silhouette_score
180
+
181
+ def hierarchical_clustering(X_scaled, method="ward", max_k=10, figsize=(12, 5)):
182
+ """階層的クラスタリングと最適クラスター数の決定。"""
183
+ Z = linkage(X_scaled, method=method)
184
+
185
+ # Silhouette スコアで最適 k を探索
186
+ sil_scores = []
187
+ for k in range(2, max_k + 1):
188
+ labels = fcluster(Z, k, criterion="maxclust")
189
+ sil = silhouette_score(X_scaled, labels)
190
+ sil_scores.append({"k": k, "silhouette": sil})
191
+
192
+ sil_df = pd.DataFrame(sil_scores)
193
+ best_k = sil_df.loc[sil_df["silhouette"].idxmax(), "k"]
194
+
195
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
196
+
197
+ # デンドログラム
198
+ dendrogram(Z, ax=ax1, truncate_mode="lastp", p=30, leaf_rotation=90)
199
+ ax1.set_title("Dendrogram", fontweight="bold")
200
+ ax1.set_ylabel("Distance")
201
+
202
+ # Silhouette スコア
203
+ ax2.plot(sil_df["k"], sil_df["silhouette"], "bo-", linewidth=2)
204
+ ax2.axvline(best_k, color="red", linestyle="--", label=f"Best k={int(best_k)}")
205
+ ax2.set_xlabel("Number of Clusters (k)")
206
+ ax2.set_ylabel("Silhouette Score")
207
+ ax2.set_title("Optimal k (Silhouette)", fontweight="bold")
208
+ ax2.legend()
209
+
210
+ plt.tight_layout()
211
+ plt.savefig("figures/clustering_analysis.png", dpi=300, bbox_inches="tight")
212
+ plt.close()
213
+ return Z, int(best_k)
214
+ ```
215
+
216
+ ## References
217
+
218
+ ### Output Files
219
+
220
+ | ファイル | 形式 |
221
+ |---|---|
222
+ | `results/pca_tsne_coordinates.csv` | CSV |
223
+ | `figures/pca_screeplot.png` | PNG |
224
+ | `figures/pca_loadings.png` | PNG |
225
+ | `figures/tsne_projection.png` | PNG |
226
+ | `figures/pca_tsne_panel.png` | PNG |
227
+ | `figures/clustering_analysis.png` | PNG |
228
+
229
+ #### 参照実験
230
+
231
+ - **Exp-02**: 化学空間 PCA/t-SNE(EGFR 阻害剤)
232
+ - **Exp-03**: PCA / UMAP / t-SNE 3 手法比較
233
+ - **Exp-07**: PLS-DA + PCA メタボロミクス
234
+ - **Exp-11**: ラマンスペクトルの PCA/t-SNE + 階層的クラスタリング
235
+ - **Exp-13**: 多技法融合 PCA/t-SNE(XRD+AFM+電気+光学)
@@ -0,0 +1,331 @@
1
+ ---
2
+ name: scientific-pipeline-scaffold
3
+ description: |
4
+ 科学データ解析パイプラインの基盤スキル。ディレクトリ構造の自動構築、再現性のためのシード管理、
5
+ 進捗ログ出力、実行時間計測、JSON サマリー生成、ダッシュボード総括図の作成を行う際に使用。
6
+ 全 13 実験に共通する足場パターンを統合。
7
+ ---
8
+
9
+ # Scientific Pipeline Scaffold
10
+
11
+ 全 Exp-01〜13 に共通する「足場(scaffold)」パターンを統合したスキル。
12
+ 新しい実験パイプラインを立ち上げる際に最初に適用し、再現性・可読性・構造を保証する。
13
+
14
+ ## When to Use
15
+
16
+ - 新しい解析実験のスクリプトを立ち上げるとき
17
+ - 再現可能な実験パイプラインを構築するとき
18
+ - 解析結果をJSON サマリーとしてエクスポートしたいとき
19
+ - 総括ダッシュボード図を自動生成したいとき
20
+
21
+ ## Quick Start
22
+
23
+ ## 1. スクリプトヘッダーテンプレート
24
+
25
+ ```python
26
+ #!/usr/bin/env python3
27
+ """
28
+ Exp-XX: [実験タイトル]
29
+ Scientific Skills Series
30
+
31
+ Description:
32
+ [1-2行の概要]
33
+
34
+ Author: [名前]
35
+ Date: [YYYY-MM-DD]
36
+ """
37
+
38
+ import warnings
39
+ warnings.filterwarnings("ignore")
40
+
41
+ import matplotlib
42
+ matplotlib.use("Agg") # headless 環境対応
43
+
44
+ import numpy as np
45
+ import pandas as pd
46
+ import matplotlib.pyplot as plt
47
+ import seaborn as sns
48
+ from pathlib import Path
49
+ import json
50
+ import time
51
+
52
+ # === 再現性設定 ===
53
+ SEED = 42
54
+ np.random.seed(SEED)
55
+
56
+ # === ディレクトリ構造 ===
57
+ BASE_DIR = Path(__file__).resolve().parent
58
+ DATA_DIR = BASE_DIR / "data"
59
+ FIG_DIR = BASE_DIR / "figures"
60
+ RESULTS_DIR = BASE_DIR / "results"
61
+
62
+ for d in [DATA_DIR, FIG_DIR, RESULTS_DIR]:
63
+ d.mkdir(parents=True, exist_ok=True)
64
+
65
+ # === 出版品質スタイル ===
66
+ plt.rcParams.update({
67
+ "font.family": "sans-serif",
68
+ "font.sans-serif": ["Arial", "Helvetica", "DejaVu Sans"],
69
+ "font.size": 10,
70
+ "axes.titlesize": 12,
71
+ "axes.labelsize": 11,
72
+ "axes.spines.top": False,
73
+ "axes.spines.right": False,
74
+ "savefig.dpi": 300,
75
+ "savefig.bbox": "tight",
76
+ "figure.facecolor": "white",
77
+ })
78
+ ```
79
+
80
+ ## 2. メイン関数テンプレート
81
+
82
+ ```python
83
+ def main():
84
+ """メインパイプライン実行関数。"""
85
+ start_time = time.time()
86
+ print("=" * 60)
87
+ print("Exp-XX: [実験タイトル]")
88
+ print("=" * 60)
89
+
90
+ # ──── Step 1: データ読み込み / 生成 ────
91
+ print("\n[Step 1] データ読み込み...")
92
+ # df = pd.read_csv(DATA_DIR / "dataset.csv")
93
+ # df = generate_dataset()
94
+ # df.to_csv(DATA_DIR / "dataset.csv", index=False)
95
+
96
+ # ──── Step 2: EDA ────
97
+ print("\n[Step 2] 探索的データ解析...")
98
+ # → scientific-eda-correlation スキル参照
99
+
100
+ # ──── Step 3: 前処理 ────
101
+ print("\n[Step 3] 前処理...")
102
+ # → scientific-data-preprocessing スキル参照
103
+
104
+ # ──── Step 4: モデル学習 ────
105
+ print("\n[Step 4] モデル学習...")
106
+ # → scientific-ml-regression / ml-classification スキル参照
107
+
108
+ # ──── Step 5: 可視化 ────
109
+ print("\n[Step 5] 可視化...")
110
+ # → scientific-publication-figures スキル参照
111
+
112
+ # ──── Step 6: サマリー ────
113
+ elapsed = time.time() - start_time
114
+ print(f"\n[Step 6] サマリー生成... (elapsed: {elapsed:.1f}s)")
115
+ # → generate_summary() 呼び出し
116
+
117
+ print("\n" + "=" * 60)
118
+ print(f"完了! ({elapsed:.1f} 秒)")
119
+ print("=" * 60)
120
+
121
+
122
+ if __name__ == "__main__":
123
+ main()
124
+ ```
125
+
126
+ ## 3. 進捗ログユーティリティ
127
+
128
+ ```python
129
+ class StepLogger:
130
+ """段階的進捗ログを管理するユーティリティ。"""
131
+
132
+ def __init__(self, experiment_name):
133
+ self.experiment_name = experiment_name
134
+ self.step_count = 0
135
+ self.start_time = time.time()
136
+ self.step_times = {}
137
+ print("=" * 60)
138
+ print(f"{experiment_name}")
139
+ print("=" * 60)
140
+
141
+ def step(self, description):
142
+ """新しい Step を開始する。"""
143
+ self.step_count += 1
144
+ step_start = time.time()
145
+ if self.step_count > 1:
146
+ prev = self.step_count - 1
147
+ self.step_times[prev] = time.time() - self._current_step_start
148
+ self._current_step_start = step_start
149
+ print(f"\n[Step {self.step_count}] {description}...")
150
+
151
+ def finish(self):
152
+ """パイプライン完了を記録する。"""
153
+ elapsed = time.time() - self.start_time
154
+ if self.step_count > 0:
155
+ self.step_times[self.step_count] = time.time() - self._current_step_start
156
+ print(f"\n{'=' * 60}")
157
+ print(f"完了! (合計 {elapsed:.1f} 秒)")
158
+ for step_num, t in self.step_times.items():
159
+ print(f" Step {step_num}: {t:.1f}s")
160
+ print(f"{'=' * 60}")
161
+ return elapsed
162
+ ```
163
+
164
+ ## 4. JSON サマリー生成
165
+
166
+ 全実験で共通する `analysis_summary.json` のスキーマ。
167
+
168
+ ```python
169
+ def generate_summary(results_dict, experiment_name, elapsed_seconds,
170
+ output_path=None):
171
+ """
172
+ 標準フォーマットの analysis_summary.json を生成する。
173
+
174
+ results_dict はドメイン固有の結果を含む辞書。
175
+ この関数がメタ情報を自動追加する。
176
+ """
177
+ import datetime
178
+
179
+ summary = {
180
+ "experiment": experiment_name,
181
+ "timestamp": datetime.datetime.now().isoformat(),
182
+ "elapsed_seconds": round(elapsed_seconds, 2),
183
+ "environment": {
184
+ "python": __import__("sys").version,
185
+ "seed": SEED,
186
+ },
187
+ "data": {
188
+ "n_samples": results_dict.get("n_samples"),
189
+ "n_features": results_dict.get("n_features"),
190
+ "source": results_dict.get("data_source", "simulation"),
191
+ },
192
+ "results": results_dict,
193
+ }
194
+
195
+ if output_path is None:
196
+ output_path = RESULTS_DIR / "analysis_summary.json"
197
+
198
+ with open(output_path, "w", encoding="utf-8") as f:
199
+ json.dump(summary, f, indent=2, ensure_ascii=False, default=str)
200
+
201
+ print(f" → Summary saved: {output_path}")
202
+ return summary
203
+ ```
204
+
205
+ ### JSON サマリー標準キー一覧
206
+
207
+ | キー | 型 | 説明 | 必須 |
208
+ |---|---|---|---|
209
+ | `experiment` | str | 実験名 | ✅ |
210
+ | `timestamp` | str | ISO 8601 日時 | ✅ |
211
+ | `elapsed_seconds` | float | 実行時間 | ✅ |
212
+ | `environment.python` | str | Python バージョン | ✅ |
213
+ | `environment.seed` | int | 乱数シード | ✅ |
214
+ | `data.n_samples` | int | サンプル数 | ✅ |
215
+ | `data.n_features` | int | 特徴量数 | ○ |
216
+ | `data.source` | str | データソース | ○ |
217
+ | `results.best_model` | str | 最良モデル名 | ○ |
218
+ | `results.best_r2` / `best_auc` | float | 最良スコア | ○ |
219
+ | `results.n_figures` | int | 生成図数 | ○ |
220
+
221
+ ## 5. 総括ダッシュボードパネル
222
+
223
+ ```python
224
+ import matplotlib.gridspec as gridspec
225
+
226
+ def create_summary_panel(panel_data, experiment_name, figsize=(20, 14)):
227
+ """
228
+ 解析結果の総括ダッシュボードを 1 枚の Figure にまとめる。
229
+
230
+ panel_data: [
231
+ {"type": "table", "title": "...", "data": df_or_dict},
232
+ {"type": "plot_func", "title": "...", "func": callable, "kwargs": {}},
233
+ {"type": "text", "title": "...", "text": "..."},
234
+ {"type": "metrics_bar", "title": "...", "names": [...], "values": [...]},
235
+ ]
236
+ """
237
+ n_panels = len(panel_data)
238
+ ncols = min(3, n_panels)
239
+ nrows = (n_panels + ncols - 1) // ncols
240
+
241
+ fig = plt.figure(figsize=figsize)
242
+ gs = gridspec.GridSpec(nrows, ncols, figure=fig, hspace=0.4, wspace=0.3)
243
+
244
+ for i, panel in enumerate(panel_data):
245
+ row, col = divmod(i, ncols)
246
+ ax = fig.add_subplot(gs[row, col])
247
+
248
+ ptype = panel["type"]
249
+ title = panel.get("title", f"Panel {chr(65 + i)}")
250
+
251
+ if ptype == "metrics_bar":
252
+ ax.barh(panel["names"], panel["values"],
253
+ color="steelblue", edgecolor="black")
254
+ ax.set_xlabel("Value")
255
+
256
+ elif ptype == "text":
257
+ ax.text(0.05, 0.95, panel["text"], transform=ax.transAxes,
258
+ fontsize=9, verticalalignment="top", fontfamily="monospace",
259
+ bbox=dict(boxstyle="round", facecolor="wheat", alpha=0.5))
260
+ ax.axis("off")
261
+
262
+ elif ptype == "table":
263
+ ax.axis("off")
264
+ if isinstance(panel["data"], pd.DataFrame):
265
+ tbl = ax.table(cellText=panel["data"].values,
266
+ colLabels=panel["data"].columns,
267
+ cellLoc="center", loc="center")
268
+ tbl.auto_set_font_size(False)
269
+ tbl.set_fontsize(8)
270
+ elif isinstance(panel["data"], dict):
271
+ rows = [[k, str(v)] for k, v in panel["data"].items()]
272
+ tbl = ax.table(cellText=rows, colLabels=["Metric", "Value"],
273
+ cellLoc="center", loc="center")
274
+ tbl.auto_set_font_size(False)
275
+ tbl.set_fontsize(9)
276
+
277
+ elif ptype == "plot_func":
278
+ panel["func"](ax=ax, **panel.get("kwargs", {}))
279
+
280
+ # パネルラベル (A, B, C, ...)
281
+ ax.set_title(f"({chr(65 + i)}) {title}", fontsize=11, fontweight="bold")
282
+
283
+ fig.suptitle(f"Summary: {experiment_name}", fontsize=14, fontweight="bold")
284
+ plt.savefig(FIG_DIR / "summary_panel.png", dpi=300, bbox_inches="tight")
285
+ plt.close()
286
+ ```
287
+
288
+ ## 6. 共通ユーティリティ
289
+
290
+ ```python
291
+ def save_fig(fig, filename, dpi=300, formats=("png",)):
292
+ """図を保存してクローズする共通関数。"""
293
+ for fmt in formats:
294
+ fig.savefig(FIG_DIR / f"{filename}.{fmt}",
295
+ dpi=dpi, bbox_inches="tight",
296
+ facecolor="white", edgecolor="none")
297
+ plt.close(fig)
298
+ print(f" → Figure saved: {filename}")
299
+
300
+
301
+ def save_results(df, filename, index=False):
302
+ """DataFrame を results/ に保存する共通関数。"""
303
+ path = RESULTS_DIR / filename
304
+ df.to_csv(path, index=index)
305
+ print(f" → Results saved: {filename}")
306
+ ```
307
+
308
+ ## ディレクトリ構造の標準
309
+
310
+ ```
311
+ Exp-XX/
312
+ ├── exp_analysis.py # メインスクリプト
313
+ ├── qiita-exp-analysis.md # Qiita 記事
314
+ ├── data/
315
+ │ └── dataset.csv # 入力データ
316
+ ├── figures/
317
+ │ ├── Fig01_*.png # 個別図(Fig + 連番 + 説明)
318
+ │ ├── Fig02_*.png
319
+ │ └── summary_panel.png # 総括ダッシュボード
320
+ └── results/
321
+ ├── descriptive_statistics.csv
322
+ ├── model_metrics.csv
323
+ ├── feature_importance.csv
324
+ └── analysis_summary.json # JSON サマリー
325
+ ```
326
+
327
+ ## References
328
+
329
+ - **全 13 実験**: ディレクトリ構造、シード管理、warnings 抑制
330
+ - **Exp-12, 13**: `main()` + 実行時間計測 + JSON サマリー + 総括パネル
331
+ - **Exp-10**: `save_fig()` / `write_summary()` ユーティリティ