Setting the scale on a Composed Distribution graph

Hi. I am pretty new to OT so if this is a stupid question with an easy fix sorry. I am currently creating a set of graphs using ot.ComposedDistribution. This graph is showing the correlation between a number of input variables and the output response using kernel density. For comparison of the graph it would be very helpful if I could set the scale of the distribution
SIZZ_alpha_flb
SIZZ_alpha_lits
Here are some of the examples of the graphs. It would be great if the scale on the left could be the same for both.
Thanks for the help

Hello!
I apologize, but I’m not sure I understand what you would like to do. Are you trying to make it so that the horizontal axis X has the same range of variation in both graphs? Or are you talking about the underlying scale parameters of the marginals characterizing the composed distribution?
Cheers

Hi thanks for replying. I do mean the underlying scale parameters of the marginals characterizing the composed distribution. I’m trying to set a pre-set range for the scale instead of arbitrary values which are shown in the picture below.
Picture2

Hello again,
Unfortunately, the ot.distribution.drawPDF() method does now allow you to directly specify the levels of the contour lines (if I correctly understood your last message). However you can do that manually by using the ot.Contour() class in the following way:

import numpy as np
import openturns as ot
from openturns.viewer import View

dist = ot.ComposedDistribution([ot.Normal(),ot.Normal()])
x = np.linspace(-4,4,100)
grid = np.meshgrid(x,x)
X1,X2 = grid
X1 = X1.reshape(-1,1)
X2 = X2.reshape(-1,1)
X = np.hstack([X1,X2])

x = ot.Sample(x.reshape(-1,1))
Z = dist.computePDF(X)
# Levels you want to be shown in the plot
lvls = ot.Point([1e-5, 1e-4, 1e-3, 1e-2, 1e-1]) 
desc = ['1e-5', '1e-4', '1e-3', '1e-2', '1e-1']
pal = ot.Contour().BuildDefaultPalette(5)

graph = ot.Graph()
for i in range(lvls.getSize()):
    lv = ot.Point([lvls[i]])
    cont = ot.Contour(x, x, Z, lv, [''], False)
    cont.setColor(pal[i])
    graph.add(cont)

graph.setGrid(True)
graph.setAxes(True)
graph.setLegends(desc)
graph.setLegendPosition('topright')
graph.setXTitle('x1')
graph.setYTitle('x2')

view = View(graph)
view.show()

and you should obtain the following figure:
tempfig

However, please note that the distributions obtained by Kernel Smoothing are ‘non parametric’ distributions. By consequence, you cannot access the marginal scales. Meaning that if you want the two plots you show in your first message to have similar density values, you would need to manually scale the values of input data you provide to the algorithm.
I hope this was useful, cheers!

Hi !
There is an example which shows how to configure the contours of a log-likelihood:
http://openturns.github.io/openturns/master/auto_graphs/plot_graphs_loglikelihood_contour.html

By the way, the help page of the Contour class presents wrong examples:
http://openturns.github.io/openturns/master/auto_graphs/plot_graphs_loglikelihood_contour.html

The first example should write:

import openturns as ot
f = ot.SymbolicFunction(["x", "y"], ["exp(-sin(cos(y)^2*x^2+sin(x)^2*y^2))"])
# Generate the data for the curves to be drawn
nX = 75
nY = 75
levels = [75, 75]
bounds = ot.Interval([[-5.0, -5.0], [5.0, 5.0]])
box = ot.Box(levels, bounds)
inputData = box.generate()
data = f(inputData)
levels = [(0.5 + i) / 5 for i in range(5)]
# Create an empty graph
myGraph = ot.Graph("Complex iso lines", "u1", "u2", True, "")
# Create the contour
myContour = ot.Contour(nX + 2, nY + 2, data)
myContour.setLevels(levels)
myGraph.add(myContour)

The second example shows that the univariate grids x and y have to be recomputed after the call to the Box class. It seems to me that we should create a new constructor of the Box class, which takes into account for the marginal grids.The example would write:

import openturns as ot
g = ot.SymbolicFunction(["R", "S"], ["(R * S)^0.75"])
# Generate the grid for the contour plot
nX = 75
nY = 75
lowerBound = [1.6, 0.0]
upperBound = [6.3, 4.3]
xstep = (upperBound [0] - lowerBound[0])/(nX  - 1)
ystep = (upperBound [1] - lowerBound[1])/(nY  - 1)
x = ot.RegularGrid(lowerBound[0], xstep, nX).getVertices()
y = ot.RegularGrid(lowerBound[1], ystep, nY).getVertices()
# Generate the data for the curves to be drawn
boxExperiment = ot.Box([x, y])  # New constructor!
inputSample = boxExperiment.generate()
outputSample = g(inputSample)
# Create an empty graph
myGraph = ot.Graph("Complex iso lines", "u1", "u2", True, "")
levels = ot.Point([-1.0, 1.0, 3.0, 5.0, 7.0, 9.0])
labels = ["Level 0", "Level 1", "Level 2", "Level 3", "Level 4", "Level 5"]
drawLabels = True
# Create the contour and plot it
myContour = ot.Contour(x, y, outputSample, levels, labels, drawLabels)
myGraph.add(myContour)

This new constructor of Box would allow to create non-uniform grids.
@JPelamatti : would this script be satisfactory for you?
Best regards,
Michaël

PS
The script could be made even more simpler provided a new constructor of RegularGrid is created:

intervalX = ot.Interval([1.6], [6.3])
x = ot.RegularGrid(intervalX, nX).getVertices()  # New constructor!
intervalY = ot.Interval([0.0], [4.3])
y = ot.RegularGrid(intervalY, nY).getVertices()

Hi JPelematti,
Thanks for the advice! sorry its taken me a while to respond. I believe i will have to manually scale the values of input data. May I clarify what you mean by manual scaling. My interpretation is that I would normalizing the output values (the values being subjected to kernel smoothing) between the max and min range-thus the scales are limited between 0-1

Hi Michaël,
Thanks so much for the response! It looks very helpful. I will let you know if i manage to set the levels

Hello Jamie,
I apologize, I think I might have misunderstood your request, due to a different usage of the word scale.
Indeed, if you want the exact same plots you showed in your first post, but with the x and y axes being bound between 0 and 1, then yes normalizing the output values between the max and the min range would be the most straightforward solution.
Alternatively, if you actually want to focus on a specific part of the contour plot you are showing (i.,e., on a specific part of the output space), you can play with the vectors x1 and x2 used to define the grid, as shown in the example below:

import numpy as np
import openturns as ot
from openturns.viewer import View

dist = ot.ComposedDistribution([ot.Normal(),ot.Normal()])
x1 = np.linspace(0.5,2.,100)
x2 = np.linspace(0,1.5,100)
grid = np.meshgrid(x1,x2)
X1,X2 = grid
X1 = X1.reshape(-1,1)
X2 = X2.reshape(-1,1)
X = np.hstack([X1,X2])

Z = dist.computePDF(X)
# Levels you want to be shown in the plot
lvls = ot.Point([1e-2, 3e-2, 6e-2, 9e-2, 1.2e-1]) 
desc = ['1e-5', '1e-4', '1e-3', '1e-2', '1e-1']
pal = ot.Contour().BuildDefaultPalette(5)

graph = ot.Graph()
for i in range(lvls.getSize()):
    lv = ot.Point([lvls[i]])
    cont = ot.Contour(x1.reshape(-1,1), x2.reshape(-1,1), Z, lv, [''], False)
    cont.setColor(pal[i])
    graph.add(cont)

graph.setGrid(True)
graph.setAxes(True)
graph.setLegends(desc)
graph.setLegendPosition('topright')
graph.setXTitle('x1')
graph.setYTitle('x2')

view = View(graph)
view.show()

and you should obtain the following contour plot:
Figure_8

Please note that in your case, the distribution would be the one obtained from the kernel density approach.

I guess my answer comes fairly late, but I still hope it can be useful for future OpenTURNS usages.

Cheers!