File: plot_regression_interval.py

package info (click to toggle)
openturns 1.26-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 67,708 kB
  • sloc: cpp: 261,605; python: 67,030; ansic: 4,378; javascript: 406; sh: 185; xml: 164; makefile: 101
file content (273 lines) | stat: -rw-r--r-- 8,584 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
"""
Compute confidence intervals of a regression model from data
============================================================
"""

# %%
#
# Introduction
# ------------
#
# In this example, we compute the pointwise confidence interval of the
# estimator of the conditional expectation given an input.
# More precisely, we compute confidence intervals of the output of
# a regression model.
# The linear regression model is an order 1 multivariate polynomial.
# This model is built from a data set.
# In this advanced example, we use the :class:`~openturns.DesignProxy`
# and :class:`~openturns.QRMethod` low level classes.
# Another example of this method is presented in
# :doc:`/auto_numerical_methods/general_methods/plot_regression_sinus`.

# %%

import openturns as ot
import openturns.viewer as otv
import numpy as np
from openturns.usecases import linthurst


# %%
palette = ot.Drawable.BuildTableauPalette(5)

# %%
#
# Get the data
# ------------
#
# We consider the so-called :ref:`Linthurst<use-case-linthurst>` data set, which contains measures of
# aerial biomass (BIO) as well as 5 five physico-chemical properties of
# the soil: salinity (SAL), pH, K, Na, and Zn.
# The data set is taken from [rawlings2001]_ table 5.1 page 63.


# %%
# We define the data.
#

# %%
ds = linthurst.Linthurst()


# %%
# Get the input and output samples.

# %%
dimension = ds.data.getDimension() - 1
print("dimension = ", dimension)
sampleSize = ds.data.getSize()
print("sampleSize = ", sampleSize)
inputSample = ds.data[:, 1 : dimension + 1]
print("Input :")
print(inputSample[:5])
outputSample = ds.data[:, 0]
print("Output :")
print(outputSample[:5])

inputDescription = inputSample.getDescription()
outputDescription = outputSample.getDescription()


# %%
# We want to create a linear regression model.
# To do this, we need to create a functional basis.
# We make the choice of using only degree 1 polynomials
# for each marginal input variable.

functionCollection = []
basisFunction = ot.SymbolicFunction(inputDescription, ["1.0"])
functionCollection.append(basisFunction)
for i in range(dimension):
    basisFunction = ot.SymbolicFunction(inputDescription, [inputDescription[i]])
    functionCollection.append(basisFunction)
basis = ot.Basis(functionCollection)


# %%
# We can then print the basis.

basisSize = basis.getSize()
print("Basis size = ", basisSize)
for i in range(basisSize):
    basisFunction = basis.build(i)
    print("Function #", i, basisFunction)


# %%
# Create the least squares model
# ------------------------------
#
# To create the least squares model, we use the :class:`~openturns.DesignProxy` class,
# which defines the design matrix of the linear regression model.
# Then we solve the problem using QR decomposition.

designProxy = ot.DesignProxy(inputSample, basis)
indices = range(basisSize)  # We consider all the functions in the basis
lsqMethod = ot.QRMethod(designProxy, indices)
betaHat = lsqMethod.solve(outputSample.asPoint())
print(betaHat)

# %%
# Based on the solution of the linear least squares problem, we
# can create the metamodel and evaluate the residuals.

metamodel = ot.LinearCombinationFunction(basis, betaHat)

# %%
# Compute residuals, variance and design matrix
# ---------------------------------------------
# We need to estimate the variance of the residuals.
# To do this we evaluate the predictions of the regression model
# on the training sample and compute the residuals.
yHat = metamodel(inputSample).asPoint()
residuals = yHat - outputSample.asPoint()

# %%
# In order to compute confidence intervals of the mean, we need
# to estimate the sample standard deviation.

sigma2Hat = residuals.normSquare() / (sampleSize - basisSize)
print("sigma2Hat", sigma2Hat)
sigmaHat = np.sqrt(sigma2Hat)
print("sigmaHat = ", sigmaHat)

# %%
# We evaluate the design matrix and store it as a :class:`~openturns.Sample`.

designMatrix = lsqMethod.computeWeightedDesign()
designSample = ot.Sample(np.array(ot.Matrix(designMatrix)))


# %%
# Compute confidence intervals
# ----------------------------
#
# The next function will help to compute confidence intervals.
# It is based on regression analysis.


def computeRegressionConfidenceInterval(
    lsqMethod,
    betaHat,
    sigma2Hat,
    designSample,
    alpha=0.95,
    mean=True,
    epsilonSigma=1.0e-5,
):
    """
    Compute a confidence interval for the estimate of the mean.

    Evaluates this confidence interval at points in the design matrix.

    This code is based on (Rawlings, Pantula & David, 1998)
    eq. 3.51 and 3.52 page 90.

    Parameters
    ----------
    lsqMethod: ot.LeastSquaresMethod
        The linear least squares method (e.g. QR or Cholesky).
    betaHat : ot.Point(parameterDimension)
        The solution of the least squares problem.
    sigma2Hat : float > 0.0
        The estimate of the variance.
    designSample : ot.Sample(size, parameterDimension)
        The design matrix of the linear model.
        This is the value of the functional basis depending on the
        input sample.
        Each row represents the input where the confidence interval
        is to be computed.
    alpha : float, in [0, 1]
        The width of the confidence interval.
    mean : bool
        If True, then computes the confidence interval of the mean.
        This interval contains yTrue = E[y|x] with probability alpha.
        Otherwise, computes a confidence interval of the prediction at point x.
        This interval contains y|x with probability alpha.
    epsilonSigma : float > 0.0
        A relatively small number. The minimal value of variance, which
        avoids a singular Normal distribution.

    Reference
    ---------
    - O. Rawlings John, G. Pantula Sastry, and A. Dickey David.
      Applied regression analysis—a research tool. Springer New York, 1998.

    Returns
    -------
    confidenceBounds : ot.Sample(size, 2)
        The first column contains the lower bound.
        The second column contains the upper bound.
    """

    inverseGram = lsqMethod.getGramInverse()
    sampleSize = designSample.getSize()
    confidenceBounds = ot.Sample(sampleSize, 2)
    for i in range(sampleSize):
        x0 = designSample[i, :]
        meanYHat = x0.dot(betaHat)
        sigma2YHat = x0.dot(inverseGram * x0) * sigma2Hat
        if not mean:
            sigma2YHat += sigma2Hat
        sigmaYHat = np.sqrt(sigma2YHat)
        sigmaYHat = max(epsilonSigma, sigmaYHat)  # Prevents a zero s.e.
        distribution = ot.Normal(meanYHat, sigmaYHat)
        interval = distribution.computeBilateralConfidenceInterval(alpha)
        lower = interval.getLowerBound()
        upper = interval.getUpperBound()
        confidenceBounds[i, 0] = lower[0]
        confidenceBounds[i, 1] = upper[0]
    return confidenceBounds


# %%
# We evaluate the value of the basis functions on the test sample.
# This sample is used to compute the confidence interval.

alpha = 0.95
confidenceIntervalMean = computeRegressionConfidenceInterval(
    lsqMethod, betaHat, sigma2Hat, designSample, alpha=alpha
)


# %%
# For a given observation, we can print the input, the observed
# output, the predicted output and the confidence interval of
# the conditional expectation.

i = 5  # The index of the observation
print("x = ", inputSample[i])
print("Y observation = ", outputSample[i])
print("Y prediction = ", yHat[i])
print("Confidence interval of the mean = ", confidenceIntervalMean[i])


# %%
# In order to see how the model fits to the data, we can
# create the validation plot.
# Each vertical bar represents the 95% confidence interval
# of the estimate of the conditional expectation of the linear regression model.

metamodelPredictions = metamodel(inputSample)
validation = ot.MetaModelValidation(outputSample, metamodelPredictions)
graph = validation.drawValidation().getGraph(0, 0)
q2Score = validation.computeR2Score()[0]
graph.setTitle("Q2 = %.2f%%" % (100.0 * q2Score))
graph.setXTitle("Observations")
graph.setYTitle("Metamodel")
for i in range(sampleSize):
    curve = ot.Curve([outputSample[i, 0]] * 2, confidenceIntervalMean[i])
    graph.add(curve)
view = otv.View(graph)

# %%
# We see that the linear regression model is not a very accurate
# metamodel, as can be seen from the relatively low R2 score.
# The metamodel predictions are not very close to observations,
# which is why the points are not close to the diagonal of the plot.
# Hence, the confidence intervals do not cross the diagonal very often.
# In this case, we may want to create a more accurate metamodel.

# %%
# Display all figures
otv.View.ShowAll()