Coverage for src/crackmnist_eccv/plot_seaborn_automated.py: 0%
57 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-19 08:00 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-19 08:00 +0000
1# SPDX-FileCopyrightText: 2026 German Aerospace Center (DLR)
2# SPDX-License-Identifier: GPL-3.0-or-later
4"""Generate test-loss performance curves from a pollution experiment CSV file.
6Plots average test loss vs. proportion of polluted training inputs, grouped
7by noise intensity, with ±1 standard deviation shading.
9Usage
10-----
11 # Fully polluted test set, default intensities:
12 python plot_seaborn_automated.py \
13 --input 128_F9_salt_pepper_radius8.csv \
14 --test_perc 1.0 --intensities 0.05 0.1 0.2
16 # Clean test set, custom intensities:
17 python plot_seaborn_automated.py \
18 --input 128_F9_gaussian_radius8.csv \
19 --test_perc 0.0 --intensities 0.1 0.3 0.5
21 # Custom output path:
22 python plot_seaborn_automated.py \
23 --input 128_F9_blob_radius8.csv \
24 --test_perc 1.0 --intensities 0.05 0.1 0.2 --output my_plot.png
25"""
27import argparse
28import os
29import sys
31from matplotlib.patches import Patch
32import matplotlib.pyplot as plt
33import pandas as pd
34import seaborn as sns
37def parse_args() -> argparse.Namespace:
38 """Parse command-line arguments for the plotting script.
40 Returns:
41 Parsed argument namespace with input, test_perc, intensities, and output fields.
42 """
43 parser = argparse.ArgumentParser(
44 description="Plot avg test loss curves from a pollution experiment CSV."
45 )
46 parser.add_argument(
47 "--input",
48 "-i",
49 required=True,
50 help=("Path to the input CSV file (e.g. 128_F9_salt_pepper_radius8.csv)."),
51 )
52 parser.add_argument(
53 "--test_perc",
54 type=float,
55 required=True,
56 help=("Test pollution level to plot: 0.0 (clean) or 1.0 (fully polluted)."),
57 )
58 parser.add_argument(
59 "--intensities",
60 type=float,
61 nargs="+",
62 required=True,
63 help=("One or more noise intensity values to include (e.g. 0.05 0.1 0.2)."),
64 )
65 parser.add_argument(
66 "--output",
67 "-o",
68 default=None,
69 help=(
70 "Path for the saved PNG file. "
71 "Defaults to "
72 "'final_<pollution>_plot_on_<test_label>_<suffix>.png'."
73 ),
74 )
75 return parser.parse_args()
78def main() -> None:
79 """Load CSV, filter by test pollution level, and plot loss curves."""
80 args = parse_args()
82 df = pd.read_csv(args.input)
84 required_cols = {
85 "test_perc",
86 "train_perc",
87 "intensity",
88 "avg_test_loss",
89 "std_test_loss",
90 }
91 missing = required_cols - set(df.columns)
92 if missing:
93 print(
94 f"Error: CSV is missing required columns: {missing}",
95 file=sys.stderr,
96 )
97 sys.exit(1)
99 df_filtered = df[df["test_perc"] == args.test_perc].copy()
100 if df_filtered.empty:
101 print(
102 f"Error: no rows found with test_perc == {args.test_perc}. "
103 f"Available values: {sorted(df['test_perc'].unique())}",
104 file=sys.stderr,
105 )
106 sys.exit(1)
108 df_filtered["train_perc"] = df_filtered["train_perc"] * 100
110 pollution_type = args.input.split("_")[1]
111 test_label = "fully clean" if args.test_perc == 0.0 else "fully polluted"
113 sns.set(style="whitegrid", context="talk")
114 palette = sns.color_palette("viridis", n_colors=len(args.intensities))
116 plt.figure(figsize=(11, 6))
118 for i, intensity in enumerate(args.intensities):
119 data_subset = df_filtered[df_filtered["intensity"] == intensity].sort_values(
120 "train_perc"
121 )
123 if data_subset.empty:
124 print(
125 f"Warning: no data for intensity={intensity} — skipping.",
126 file=sys.stderr,
127 )
128 continue
130 x = data_subset["train_perc"].to_numpy()
131 y = data_subset["avg_test_loss"].to_numpy()
132 std = data_subset["std_test_loss"].to_numpy()
134 plt.plot(x, y, label=f"{intensity}", color=palette[i], marker="o")
135 plt.fill_between(x, y - std, y + std, color=palette[i], alpha=0.3)
137 first_legend = plt.legend(
138 title="Noise intensity", loc="upper right", fontsize=14, title_fontsize=14
139 )
140 plt.gca().add_artist(first_legend)
142 shaded_patch = Patch(facecolor="gray", alpha=0.3, label="±1 standard deviation")
143 plt.legend(
144 handles=[shaded_patch],
145 title="Shaded area",
146 loc="upper center",
147 bbox_to_anchor=(0.6, 0.99),
148 frameon=True,
149 fontsize=14,
150 title_fontsize=14,
151 borderaxespad=0.5,
152 )
154 plt.title(
155 f"Effect of training noise on {test_label} test performance",
156 fontsize=14,
157 )
158 plt.xlabel("Proportion of polluted training inputs (%)", fontsize=14)
159 plt.ylabel("Average test loss", fontsize=14)
160 plt.tight_layout()
162 if args.output:
163 save_name = args.output
164 else:
165 suffix = os.path.splitext(args.input)[0].split("_")[-1]
166 save_name = (
167 f"final_{pollution_type}_plot_on_{test_label.replace(' ', '_')}_{suffix}.png"
168 )
170 plt.savefig(save_name, dpi=300)
171 plt.show()
172 print(f"Plot saved as: {save_name}")
175if __name__ == "__main__":
176 main()