File size: 2,726 Bytes
bf620c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import csv
from collections import defaultdict
from statistics import mean, stdev
import math
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt


def rename_method(label: str) -> str:
    # Use the same short labels as the TikZ version
    mapping = {
        'Spectral': 'Spectral Clustering',
        'Densest': 'Densest Subgraph',
        'QuasiClique': 'QuasiClique',
        'k-core': 'k-core',
        'L-RMC': 'L-RMC (ours)',
    }
    return mapping.get(label, label)


def load_rows(path):
    with open(path, 'r') as f:
        reader = csv.DictReader(f)
        return [row for row in reader]


def aggregate_f1_vs_pout(rows):
    # Group by (Method, p_out) across k and p_in
    group = defaultdict(list)
    for row in rows:
        method = row['Method']
        try:
            pout = float(row['ExternalDensity'])
            f1 = float(row['F1'])
        except (KeyError, ValueError):
            continue
        group[(method, pout)].append(f1)

    summary = defaultdict(dict)  # method -> pout -> (mean, ci95, n)
    for (method, pout), vals in group.items():
        n = len(vals)
        mu = mean(vals)
        if n > 1:
            s = stdev(vals)
        else:
            s = 0.0
        ci95 = 1.96 * s / math.sqrt(max(n, 1))
        summary[method][pout] = (mu, ci95, n)
    return summary


def plot(summary, out_path):
    pouts = sorted({p for m in summary for p in summary[m].keys()})
    methods = sorted(summary.keys())

    plt.figure(figsize=(6.0, 3.4), dpi=200)

    for method in methods:
        means = []
        ci = []
        for p in pouts:
            mu, ci95, _ = summary[method].get(p, (float('nan'), 0.0, 0))
            means.append(mu)
            ci.append(ci95)

        label = rename_method(method)
        plt.plot(pouts, means, marker='o', linewidth=2, markersize=4, label=label)
        # Shaded 95% CI
        lower = [m - c for m, c in zip(means, ci)]
        upper = [m + c for m, c in zip(means, ci)]
        plt.fill_between(pouts, lower, upper, alpha=0.15)

    plt.xlabel(r"$p_{\text{out}}$")
    plt.ylabel("F1")
    plt.title("F1 vs $p_{\\text{out}}$ (avg over $k,p_{\\text{in}}$)")
    plt.grid(True, alpha=0.3)
    plt.legend(fontsize=7, frameon=False)
    plt.tight_layout()
    plt.savefig(out_path, bbox_inches='tight')
    print(f"Saved {out_path}")


if __name__ == '__main__':
    import argparse
    ap = argparse.ArgumentParser()
    ap.add_argument('csv', help='Path to Table1 CSV (per-setting averages)')
    ap.add_argument('--out', default='appendix_f1_vs_pout.png', help='Output PNG path')
    args = ap.parse_args()

    rows = load_rows(args.csv)
    summary = aggregate_f1_vs_pout(rows)
    plot(summary, args.out)