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

1# SPDX-FileCopyrightText: 2026 German Aerospace Center (DLR) 

2# SPDX-License-Identifier: GPL-3.0-or-later 

3 

4"""Generate test-loss performance curves from a pollution experiment CSV file. 

5 

6Plots average test loss vs. proportion of polluted training inputs, grouped 

7by noise intensity, with ±1 standard deviation shading. 

8 

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 

15 

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 

20 

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""" 

26 

27import argparse 

28import os 

29import sys 

30 

31from matplotlib.patches import Patch 

32import matplotlib.pyplot as plt 

33import pandas as pd 

34import seaborn as sns 

35 

36 

37def parse_args() -> argparse.Namespace: 

38 """Parse command-line arguments for the plotting script. 

39 

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() 

76 

77 

78def main() -> None: 

79 """Load CSV, filter by test pollution level, and plot loss curves.""" 

80 args = parse_args() 

81 

82 df = pd.read_csv(args.input) 

83 

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) 

98 

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) 

107 

108 df_filtered["train_perc"] = df_filtered["train_perc"] * 100 

109 

110 pollution_type = args.input.split("_")[1] 

111 test_label = "fully clean" if args.test_perc == 0.0 else "fully polluted" 

112 

113 sns.set(style="whitegrid", context="talk") 

114 palette = sns.color_palette("viridis", n_colors=len(args.intensities)) 

115 

116 plt.figure(figsize=(11, 6)) 

117 

118 for i, intensity in enumerate(args.intensities): 

119 data_subset = df_filtered[df_filtered["intensity"] == intensity].sort_values( 

120 "train_perc" 

121 ) 

122 

123 if data_subset.empty: 

124 print( 

125 f"Warning: no data for intensity={intensity} — skipping.", 

126 file=sys.stderr, 

127 ) 

128 continue 

129 

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() 

133 

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) 

136 

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) 

141 

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 ) 

153 

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() 

161 

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 ) 

169 

170 plt.savefig(save_name, dpi=300) 

171 plt.show() 

172 print(f"Plot saved as: {save_name}") 

173 

174 

175if __name__ == "__main__": 

176 main()