Reputation: 1151
I want to create a contour plot of the log-likelihood of a Gaussian sample with OpenTURNS. Each contour maps to a function value, which is indicated by the legend of the plot. The problem is that the legend somewhat hides the contours: how to setup the plot so that the legend does not hide the content?
Here is an example. I create a Normal
distribution corresponding to the height of man with age between 20 and 79 years old (see "Statistical Abstract of the United States U.S. Census Bureau."
Table 209. 2012). Then I generate a sample from this distribution. I define the log-likelihood function, which takes the vector (mu, sigma) on input and returns a one-dimensional vector containing the log-likelihood on output. I can use the draw
method of this function to create the contour plot.
import openturns as ot
import openturns.viewer as otv
mu = 1.763
sigma = 0.0680
N = ot.Normal(mu, sigma)
sample_size = 100
sample = N.getSample(sample_size)
def loglikelihood_gauss(X):
"""Compute the log-likelihood of a Gaussian sample."""
mu, sigma = X
N = ot.Normal(mu, sigma)
log_pdf = N.computeLogPDF(sample)
sample_size = sample.getSize()
log_likelihood = log_pdf.computeMean() * sample_size
return log_likelihood
# Contour plot
logLikelihoodFunction = ot.PythonFunction(2, 1, loglikelihood_gauss)
ot.ResourceMap_SetAsUnsignedInteger("Contour-DefaultLevelsNumber", 5)
graph = logLikelihoodFunction.draw([1.65, 0.04], [1.85, 0.15], [50]*2)
graph.setXTitle(r"$\mu$")
graph.setYTitle(r"$\sigma$")
graph.setTitle("Log-Likelihood.")
view = otv.View(graph)
This produces:
How to setup the plot so that the legend does not hide the contours?
Upvotes: 1
Views: 171
Reputation: 1151
The first trick is to use the bbox_to_anchor
option of the legend
matplotlib function. This is possible because all OpenTURNS graphics are from Matplotlib at the Python layer. The second trick is to get the figure from the View
object, based on the getFigure
method. Without further code, this produces a second legend which is at the correct location, but duplicates the legend. The third trick is to hide the old legend by setting the content to empty strings.
logLikelihoodFunction = ot.PythonFunction(2, 1, loglikelihood_gauss)
ot.ResourceMap_SetAsUnsignedInteger("Contour-DefaultLevelsNumber", 5)
graph = logLikelihoodFunction.draw([1.65, 0.04], [1.85, 0.15], [50]*2)
graph.setXTitle(r"$\mu$")
graph.setYTitle(r"$\sigma$")
graph.setTitle("Log-Likelihood.")
legends = graph.getLegends()
graph.setLegendPosition("")
view = otv.View(graph)
figure = view.getFigure()
figure.legend(legends, bbox_to_anchor=(1.1, 0.9))
When saving the figure, the legend may be cropped. It might be necessary to use the bbox_inches
option:
figure.savefig("filename.png", bbox_inches="tight")
Upvotes: 2
Reputation: 106
Unfortunately it doesn't work well if you mix different kind of drawables:
import openturns as ot
import openturns.viewer as otv
f = ot.SymbolicFunction(["x", "y"], ["x^4+y^4-8*(x^2+y^2)"])
graph = f.draw([-4.0]*2, [4.0]*2)
graph.setLegendPosition("bottomleft")
graph.add(ot.Curve([[-4.0]*2, [4.0]*2], "red", "dashed", 1.0, "line"))
graph.add(ot.Cloud([[0.0]*2], "black", "fcircle", "point"))
legends = graph.getLegends()
view = otv.View(graph)
figure = view.getFigure()
figure.legend(legends, bbox_to_anchor=(1.0, 1.0))
On the resulting figure you see that the new legend has interverted the labels of the point and the line with the labels of the first two isovalues. The correct way to get the legend labels is:
_, legends = view._ax[0].get_legend_handles_labels()
Upvotes: 0
Reputation: 11
Very nice functionality! I'am often embarrassed with graphs which legend is too big. You can improve your script by using:
graph.setLegendPosition('')
instead of:
graph.setLegends([""] * 5)
It avoids you to count the number of drawables you have in the graph.
Upvotes: 0