| |
| """Analyze raw results and produce processed CSVs and summary statistics.""" |
| import os |
| import sys |
| import json |
| import glob |
| import argparse |
| import yaml |
| import numpy as np |
| import pandas as pd |
| from scipy import stats |
| from datetime import datetime |
|
|
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| from src.utils import ensure_dir |
|
|
|
|
| def load_all_results(raw_dir='results/raw'): |
| """Load all JSONL result files.""" |
| records = [] |
| for fpath in sorted(glob.glob(os.path.join(raw_dir, '*.jsonl'))): |
| with open(fpath) as f: |
| for line in f: |
| line = line.strip() |
| if line: |
| try: |
| records.append(json.loads(line)) |
| except json.JSONDecodeError: |
| pass |
| return records |
|
|
|
|
| def process_synthetic(df): |
| """Process synthetic results into summary tables.""" |
| if len(df) == 0: |
| return pd.DataFrame() |
| |
| |
| group_cols = ['graph_type', 'prior_strength', 'K'] |
| available = [c for c in group_cols if c in df.columns] |
| if not available: |
| return pd.DataFrame() |
| |
| agg_dict = {} |
| for col in ['chi_seed_max', 'chi_seed_sum', 'empirical_decay_mu', 'empirical_decay_r2', |
| 'rel_error_R1', 'rel_error_R2', 'rel_error_R3', 'rel_error_R4', |
| 'runtime_exact', 'runtime_local_R2', 'runtime_local_R4', |
| 'interference_cosine_R2', 'error_warm_start', 'rel_error_warm_start', |
| 'error_one_step', 'rel_error_one_step']: |
| if col in df.columns: |
| agg_dict[col] = ['mean', 'median', 'std', 'count'] |
| |
| if not agg_dict: |
| return pd.DataFrame() |
| |
| summary = df.groupby(available).agg(agg_dict) |
| summary.columns = ['_'.join(c) for c in summary.columns] |
| summary = summary.reset_index() |
| |
| return summary |
|
|
|
|
| def compute_correlations(df): |
| """Compute correlation table between proxies and error metrics. |
| |
| Computes within-regime correlations (controlling for graph structure) |
| and also log-transformed chi correlations. |
| """ |
| rows = [] |
| |
| proxy_cols = ['chi_seed_max', 'chi_seed_sum', 'seed_degree'] |
| log_proxy_cols = ['log_chi_max', 'log_chi_sum'] |
| target_cols = ['rel_error_R2', 'rel_error_R4', 'interference_cosine_R2'] |
| |
| |
| df_copy = df.copy() |
| if 'chi_seed_max' in df_copy.columns: |
| df_copy['log_chi_max'] = np.log1p(df_copy['chi_seed_max'].clip(lower=0)) |
| if 'chi_seed_sum' in df_copy.columns: |
| df_copy['log_chi_sum'] = np.log1p(df_copy['chi_seed_sum'].clip(lower=0)) |
| |
| all_proxies = proxy_cols + log_proxy_cols |
| |
| |
| if 'regime' in df_copy.columns: |
| regime_groups = df_copy.groupby('regime') |
| elif 'dataset_name' in df_copy.columns: |
| regime_groups = df_copy.groupby('dataset_name') |
| else: |
| regime_groups = [('all', df_copy)] |
| |
| for grp_name, grp_df in regime_groups: |
| for proxy in all_proxies: |
| for target in target_cols: |
| if proxy in grp_df.columns and target in grp_df.columns: |
| x = grp_df[proxy].dropna() |
| y = grp_df[target].dropna() |
| common = x.index.intersection(y.index) |
| x, y = x.loc[common], y.loc[common] |
| |
| mask = np.isfinite(x) & np.isfinite(y) |
| x, y = x[mask], y[mask] |
| |
| if len(x) >= 5: |
| try: |
| pr, pp = stats.pearsonr(x, y) |
| sr, sp = stats.spearmanr(x, y) |
| except: |
| continue |
| if np.isnan(pr) or np.isnan(sr): |
| continue |
| rows.append({ |
| 'dataset_regime': grp_name, |
| 'model_family': grp_df['model_family'].iloc[0] if 'model_family' in grp_df.columns else 'unknown', |
| 'proxy': proxy, |
| 'target': target, |
| 'pearson_r': round(pr, 4), |
| 'spearman_r': round(sr, 4), |
| 'pearson_p': round(pp, 6), |
| 'spearman_p': round(sp, 6), |
| 'n_deletions': len(x), |
| }) |
| |
| return pd.DataFrame(rows) |
|
|
|
|
| def build_method_comparison(df): |
| """Build method comparison table.""" |
| rows = [] |
| |
| if 'dataset_name' in df.columns: |
| groups = df.groupby('dataset_name') |
| else: |
| groups = [('all', df)] |
| |
| for grp_name, grp_df in groups: |
| |
| if 'runtime_exact' in grp_df.columns: |
| rows.append({ |
| 'dataset_regime': grp_name, |
| 'method': 'exact', |
| 'radius': None, |
| 'mean_error': 0.0, |
| 'median_error': 0.0, |
| 'mean_runtime': grp_df['runtime_exact'].mean(), |
| 'speedup_vs_exact': 1.0, |
| }) |
| t_exact = grp_df['runtime_exact'].mean() |
| else: |
| t_exact = 1.0 |
| |
| |
| for R in [1, 2, 3, 4]: |
| err_col = f'rel_error_R{R}' |
| rt_col = f'runtime_local_R{R}' |
| if err_col in grp_df.columns and rt_col in grp_df.columns: |
| mean_rt = grp_df[rt_col].mean() |
| rows.append({ |
| 'dataset_regime': grp_name, |
| 'method': 'local', |
| 'radius': R, |
| 'mean_error': grp_df[err_col].mean(), |
| 'median_error': grp_df[err_col].median(), |
| 'mean_runtime': mean_rt, |
| 'speedup_vs_exact': t_exact / max(mean_rt, 1e-6), |
| }) |
| |
| |
| if 'rel_error_warm_start' in grp_df.columns: |
| mean_rt_ws = grp_df['runtime_warm_start'].mean() if 'runtime_warm_start' in grp_df.columns else 0 |
| rows.append({ |
| 'dataset_regime': grp_name, |
| 'method': 'warm_start', |
| 'radius': None, |
| 'mean_error': grp_df['rel_error_warm_start'].mean(), |
| 'median_error': grp_df['rel_error_warm_start'].median(), |
| 'mean_runtime': mean_rt_ws, |
| 'speedup_vs_exact': t_exact / max(mean_rt_ws, 1e-6), |
| }) |
| |
| |
| if 'rel_error_one_step' in grp_df.columns: |
| mean_rt_os = grp_df['runtime_one_step'].mean() if 'runtime_one_step' in grp_df.columns else 0 |
| rows.append({ |
| 'dataset_regime': grp_name, |
| 'method': 'one_step', |
| 'radius': None, |
| 'mean_error': grp_df['rel_error_one_step'].dropna().mean(), |
| 'median_error': grp_df['rel_error_one_step'].dropna().median(), |
| 'mean_runtime': mean_rt_os, |
| 'speedup_vs_exact': t_exact / max(mean_rt_os, 1e-6) if mean_rt_os > 0 else float('inf'), |
| }) |
| |
| return pd.DataFrame(rows) |
|
|
|
|
| def save_table(df, base_path, table_name): |
| """Save table as CSV and markdown.""" |
| ensure_dir(os.path.dirname(base_path)) |
| |
| csv_path = base_path + '.csv' |
| md_path = base_path + '.md' |
| |
| df.to_csv(csv_path, index=False) |
| |
| with open(md_path, 'w') as f: |
| f.write(f'# {table_name}\n\n') |
| f.write(df.to_markdown(index=False)) |
| f.write('\n\n') |
| |
| |
| f.write('## LaTeX\n\n```latex\n') |
| f.write(df.to_latex(index=False, float_format='%.4f')) |
| f.write('```\n') |
| |
| print(f" Saved: {csv_path}, {md_path}") |
| return csv_path, md_path |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('--config', type=str, default='config/default.yaml') |
| args = parser.parse_args() |
| |
| print("Loading results...") |
| records = load_all_results() |
| if not records: |
| print("No results found in results/raw/") |
| return |
| |
| df = pd.DataFrame(records) |
| print(f"Loaded {len(df)} records") |
| |
| |
| drop_cols = ['influence_by_distance', 'influence_by_distance_full', 'deletion_edge'] |
| for col in drop_cols: |
| if col in df.columns: |
| df_clean = df.drop(columns=[col]) |
| else: |
| df_clean = df |
| |
| |
| proc_dir = ensure_dir('results/processed') |
| df_clean.to_csv(os.path.join(proc_dir, 'all_results.csv'), index=False) |
| print(f"Saved processed CSV: results/processed/all_results.csv") |
| |
| |
| syn_df = df[df['dataset_type'] == 'synthetic'] if 'dataset_type' in df.columns else df |
| real_df = df[df['dataset_type'] == 'real'] if 'dataset_type' in df.columns else pd.DataFrame() |
| |
| tables_dir = ensure_dir('results/tables') |
| |
| |
| if len(syn_df) > 0: |
| syn_summary = process_synthetic(syn_df) |
| if len(syn_summary) > 0: |
| save_table(syn_summary, os.path.join(tables_dir, 'table_synthetic_regimes'), |
| 'Synthetic Regime Summary') |
| |
| |
| if len(real_df) > 0: |
| real_summary = process_synthetic(real_df) |
| if len(real_summary) > 0: |
| save_table(real_summary, os.path.join(tables_dir, 'table_real_datasets'), |
| 'Real Dataset Summary') |
| |
| |
| from src.metrics import compute_bootstrap_summary |
| |
| metric_cols = ['empirical_decay_mu', 'rel_error_R1', 'rel_error_R2', 'rel_error_R3', |
| 'rel_error_R4', 'chi_seed_max', 'interference_cosine_R2', |
| 'rel_error_warm_start', 'rel_error_one_step'] |
| |
| if len(syn_df) > 0: |
| boot_syn = compute_bootstrap_summary( |
| syn_df, ['graph_type', 'prior_strength', 'K'], metric_cols) |
| if len(boot_syn) > 0: |
| save_table(boot_syn, os.path.join(tables_dir, 'table_synthetic_bootstrap'), |
| 'Synthetic Bootstrap CIs') |
| |
| if len(real_df) > 0: |
| boot_real = compute_bootstrap_summary( |
| real_df, ['dataset_name', 'K'], metric_cols) |
| if len(boot_real) > 0: |
| save_table(boot_real, os.path.join(tables_dir, 'table_real_bootstrap'), |
| 'Real Data Bootstrap CIs') |
| |
| if 'model_family' in df.columns and df['model_family'].nunique() > 1: |
| boot_mf = compute_bootstrap_summary( |
| df[df['dataset_type'] == 'synthetic'], |
| ['model_family', 'graph_type'], metric_cols) |
| if len(boot_mf) > 0: |
| save_table(boot_mf, os.path.join(tables_dir, 'table_model_family_bootstrap'), |
| 'Model Family Bootstrap CIs') |
| |
| |
| corr_df = compute_correlations(df) |
| if len(corr_df) > 0: |
| save_table(corr_df, os.path.join(tables_dir, 'table_correlations'), |
| 'Correlation Summary') |
| |
| |
| method_df = build_method_comparison(df) |
| if len(method_df) > 0: |
| save_table(method_df, os.path.join(tables_dir, 'table_method_comparison'), |
| 'Method Comparison') |
| |
| |
| if 'model_family' in df.columns: |
| mf_df = df[df['model_family'].notna()] |
| if len(mf_df) > 0: |
| |
| mf_group_cols = ['model_family', 'graph_type', 'prior_strength'] |
| avail = [c for c in mf_group_cols if c in mf_df.columns] |
| |
| agg_cols = {} |
| for col in ['chi_seed_max', 'empirical_decay_mu', 'rel_error_R2', 'rel_error_R4', |
| 'runtime_exact', 'runtime_local_R2']: |
| if col in mf_df.columns: |
| agg_cols[col] = ['mean', 'median'] |
| |
| if agg_cols and avail: |
| mf_summary = mf_df.groupby(avail).agg(agg_cols) |
| mf_summary.columns = ['_'.join(c) for c in mf_summary.columns] |
| mf_summary = mf_summary.reset_index() |
| save_table(mf_summary, os.path.join(tables_dir, 'table_model_family_summary'), |
| 'Model Family Summary') |
| |
| |
| mf_corr = compute_correlations(mf_df) |
| if len(mf_corr) > 0: |
| save_table(mf_corr, os.path.join(tables_dir, 'table_model_family_correlations'), |
| 'Model Family Correlations') |
| |
| print("\nAnalysis complete.") |
|
|
|
|
| if __name__ == '__main__': |
| main() |
|
|