Contour plots using Matplotlib

Hi Michaël,

Thanks for this script, the levels computation is very handy in this case!
When dealing propagating uncertainties (i.e., central tendency estimation, rare even estimation) it can be useful to plot both a function and a joint distribution. Here is a small class that I wrote starting from your code:

import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
import openturns as ot 

class Draw2DFunctions:
    def __init__(self, lowerbound, upperbound):
        self.grid_size = 100
        mesher = ot.IntervalMesher([self.grid_size-1] * 2)
        interval = ot.Interval(lowerbound, upperbound)
        mesh = mesher.build(interval)
        self.nodes = mesh.getVertices()
        self.X0, self.X1 = np.array(self.nodes).T.reshape(2, self.grid_size, self.grid_size)

    def draw_2D_contour(self, title, function=None, distribution=None, colorbar=cm.coolwarm, nb_isocurves=8, contour_values=True, opt_function_levels=True):
        fig = plt.figure(figsize=(5, 4))
        if distribution is not None:
            Zpdf = np.array(distribution.computePDF(self.nodes)).reshape(self.grid_size, self.grid_size)
            contours = plt.contour(self.X0, self.X1, Zpdf, nb_isocurves, colors='black', alpha=0.6)
            if contour_values:
                plt.clabel(contours, inline=True, fontsize=8)
        if function is not None:
            Z = np.array(function(self.nodes)).reshape(self.grid_size, self.grid_size)
            if opt_function_levels: 
                Z_sample = ot.Sample.BuildFromPoint(Z.flatten())
                # Compute levels
                nb_levels = 10
                epsilon_grid = 5e-2
                regular_grid = np.linspace(epsilon_grid, 1.0 - epsilon_grid, nb_levels)
                computed_levels = np.array(Z_sample.computeQuantile(regular_grid)).flatten()
            else: 
                computed_levels = 20
            plt.contourf(self.X0, self.X1, Z, levels=computed_levels, cmap=colorbar)
            plt.colorbar()
        plt.title(title, fontsize=20)
        plt.xlabel("$x_0$", fontsize=20)
        plt.ylabel("$x_1$", fontsize=20)
        return fig
#
rosenbrock = ot.SymbolicFunction(["x1", "x2"], ["(1 - x1)^2 + 100 * (x2 - x1^2)^2"])
funky_distribution = ot.ComposedDistribution([ot.Normal(0., 0.7)] * 2, ot.ClaytonCopula(2.))
d = Draw2DFunctions([-2.] * 2, [2.] * 2)
d.draw_2D_contour("Rosenbrock function", function=rosenbrock, distribution=funky_distribution, colorbar=cm.plasma)
plt.show()

Best,
Elias

1 Like