def plot_residuals()

in experiments/compare_conjugate_gradient.py [0:0]


    def plot_residuals(self, exp_name, save_path=None):
        """
        Plots residuals over iterations and time.

        Creates one plot with all sketches.
        """
        # https://stackoverflow.com/a/6117124/9978618
        rep = {" (median)": "", " (1st quartile)": "", " (3rd quartile)": ""}
        rep = dict((re.escape(k), v) for k, v in rep.items())
        pattern = re.compile("|".join(rep.keys()))

        run_names = {
            # col for col in self.residual_norms_df.columns
            pattern.sub(lambda m: rep[re.escape(m.group(0))], col)
            for col in self.residual_norms_df.columns
        }
        run_names = list(run_names)
        run_names.sort()
        # print("\n~~~~ run_names: ")
        # for n in run_names:
        #     print(n)
        # print()

        formatted_run_names = format_run_names_exp(run_names)
        # print("~~~~ formatted_run_names: ")
        # for n in formatted_run_names:
        #     print(n)
        # print("\n")

        # Iteration plot
        fig_iter = plot_runs_over_iterations(
            run_names, formatted_run_names, self.residual_norms_df,
        )

        if save_path:
            file_name = exp_name
            file_name = re.sub("[()]", "", file_name)
            full_path = os.path.join(save_path, f"{file_name}_iterations.pdf")
            fig_iter.write_image(full_path)
        else:
            fig_iter.show()

        # Time plot
        fig_iter = plot_runs_over_time(
            run_names, formatted_run_names, self.times_df, self.residual_norms_df,
        )

        if save_path:
            file_name = exp_name
            file_name = re.sub("[()]", "", file_name)
            full_path = os.path.join(save_path, f"{file_name}_time.pdf")
            fig_iter.write_image(full_path)
        else:
            fig_iter.show()