1#===============================================================================
2# Copyright (c) 2012-2015, GPy authors (see AUTHORS.txt).
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are met:
7#
8# * Redistributions of source code must retain the above copyright notice, this
9#   list of conditions and the following disclaimer.
10#
11# * Redistributions in binary form must reproduce the above copyright notice,
12#   this list of conditions and the following disclaimer in the documentation
13#   and/or other materials provided with the distribution.
14#
15# * Neither the name of GPy nor the names of its
16#   contributors may be used to endorse or promote products derived from
17#   this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29#===============================================================================
30
31import numpy as np
32
33from . import plotting_library as pl
34from .plot_util import helper_for_plot_data, update_not_existing_kwargs, \
35    helper_predict_with_model, get_which_data_ycols, get_x_y_var
36from .data_plots import _plot_data, _plot_inducing, _plot_data_error
37
38def plot_mean(self, plot_limits=None, fixed_inputs=None,
39              resolution=None, plot_raw=False,
40              apply_link=False, visible_dims=None,
41              which_data_ycols='all',
42              levels=20, projection='2d',
43              label='gp mean',
44              predict_kw=None,
45              **kwargs):
46    """
47    Plot the mean of the GP.
48
49    You can deactivate the legend for this one plot by supplying None to label.
50
51    Give the Y_metadata in the predict_kw if you need it.
52
53
54
55    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
56    :type plot_limits: np.array
57    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
58    :type fixed_inputs: a list of tuples
59    :param int resolution: The resolution of the prediction [defaults are 1D:200, 2D:50]
60    :param bool plot_raw: plot the latent function (usually denoted f) only?
61    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
62    :param array-like which_data_ycols: which columns of y to plot (array-like or list of ints)
63    :param int levels: for 2D plotting, the number of contour levels to use is
64    :param {'2d','3d'} projection: whether to plot in 2d or 3d. This only applies when plotting two dimensional inputs!
65    :param str label: the label for the plot.
66    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
67    """
68    canvas, kwargs = pl().new_canvas(projection=projection, **kwargs)
69    X = get_x_y_var(self)[0]
70    helper_data = helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resolution)
71    helper_prediction = helper_predict_with_model(self, helper_data[2], plot_raw,
72                                          apply_link, None,
73                                          get_which_data_ycols(self, which_data_ycols),
74                                          predict_kw)
75    plots = _plot_mean(self, canvas, helper_data, helper_prediction,
76                       levels, projection, label, **kwargs)
77    return pl().add_to_canvas(canvas, plots)
78
79def _plot_mean(self, canvas, helper_data, helper_prediction,
80              levels=20, projection='2d', label=None,
81              **kwargs):
82
83    _, free_dims, Xgrid, x, y, _, _, resolution = helper_data
84    if len(free_dims)<=2:
85        mu, _, _ = helper_prediction
86        if len(free_dims)==1:
87            # 1D plotting:
88            update_not_existing_kwargs(kwargs, pl().defaults.meanplot_1d)  # @UndefinedVariable
89            plots = dict(gpmean=[pl().plot(canvas, Xgrid[:, free_dims], mu, label=label, **kwargs)])
90        else:
91            if projection.lower() in '2d':
92                update_not_existing_kwargs(kwargs, pl().defaults.meanplot_2d)  # @UndefinedVariable
93                plots = dict(gpmean=[pl().contour(canvas, x[:,0], y[0,:],
94                                               mu.reshape(resolution, resolution).T,
95                                               levels=levels, label=label, **kwargs)])
96            elif projection.lower() in '3d':
97                update_not_existing_kwargs(kwargs, pl().defaults.meanplot_3d)  # @UndefinedVariable
98                plots = dict(gpmean=[pl().surface(canvas, x, y,
99                                               mu.reshape(resolution, resolution),
100                                               label=label,
101                                               **kwargs)])
102    elif len(free_dims)==0:
103        pass # Nothing to plot!
104    else:
105        raise RuntimeError('Cannot plot mean in more then 2 input dimensions')
106    return plots
107
108def plot_confidence(self, lower=2.5, upper=97.5, plot_limits=None, fixed_inputs=None,
109              resolution=None, plot_raw=False,
110              apply_link=False, visible_dims=None,
111              which_data_ycols='all', label='gp confidence',
112              predict_kw=None,
113              **kwargs):
114    """
115    Plot the confidence interval between the percentiles lower and upper.
116    E.g. the 95% confidence interval is $2.5, 97.5$.
117    Note: Only implemented for one dimension!
118
119    You can deactivate the legend for this one plot by supplying None to label.
120
121    Give the Y_metadata in the predict_kw if you need it.
122
123
124    :param float lower: the lower percentile to plot
125    :param float upper: the upper percentile to plot
126    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
127    :type plot_limits: np.array
128    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
129    :type fixed_inputs: a list of tuples
130    :param int resolution: The resolution of the prediction [default:200]
131    :param bool plot_raw: plot the latent function (usually denoted f) only?
132    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
133    :param array-like visible_dims: which columns of the input X (!) to plot (array-like or list of ints)
134    :param array-like which_data_ycols: which columns of the output y (!) to plot (array-like or list of ints)
135    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
136    """
137    canvas, kwargs = pl().new_canvas(**kwargs)
138    ycols = get_which_data_ycols(self, which_data_ycols)
139    X = get_x_y_var(self)[0]
140    helper_data = helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resolution)
141    helper_prediction = helper_predict_with_model(self, helper_data[2], plot_raw, apply_link,
142                                                 (lower, upper),
143                                                 ycols, predict_kw)
144    plots = _plot_confidence(self, canvas, helper_data, helper_prediction, label, **kwargs)
145    return pl().add_to_canvas(canvas, plots, legend=label is not None)
146
147def _plot_confidence(self, canvas, helper_data, helper_prediction, label, **kwargs):
148    _, free_dims, Xgrid, _, _, _, _, _ = helper_data
149    update_not_existing_kwargs(kwargs, pl().defaults.confidence_interval)  # @UndefinedVariable
150    if len(free_dims)<=1:
151        if len(free_dims)==1:
152            percs = helper_prediction[1]
153            fills = []
154            for d in range(helper_prediction[0].shape[1]):
155                fills.append(pl().fill_between(canvas, Xgrid[:,free_dims[0]], percs[0][:,d], percs[1][:,d], label=label, **kwargs))
156            return dict(gpconfidence=fills)
157        else:
158            pass #Nothing to plot!
159    else:
160        raise RuntimeError('Can only plot confidence interval in one input dimension')
161
162
163def plot_samples(self, plot_limits=None, fixed_inputs=None,
164              resolution=None, plot_raw=True,
165              apply_link=False, visible_dims=None,
166              which_data_ycols='all',
167              samples=3, projection='2d', label='gp_samples',
168              predict_kw=None,
169              **kwargs):
170    """
171    Plot the mean of the GP.
172
173    You can deactivate the legend for this one plot by supplying None to label.
174
175    Give the Y_metadata in the predict_kw if you need it.
176
177
178
179    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
180    :type plot_limits: np.array
181    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
182    :type fixed_inputs: a list of tuples
183    :param int resolution: The resolution of the prediction [defaults are 1D:200, 2D:50]
184    :param bool plot_raw: plot the latent function (usually denoted f) only? This is usually what you want!
185    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
186    :param array-like visible_dims: which columns of the input X (!) to plot (array-like or list of ints)
187    :param array-like which_data_ycols: which columns of y to plot (array-like or list of ints)
188    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
189    :param int levels: for 2D plotting, the number of contour levels to use is
190    """
191    canvas, kwargs = pl().new_canvas(projection=projection, **kwargs)
192    ycols = get_which_data_ycols(self, which_data_ycols)
193    X = get_x_y_var(self)[0]
194    helper_data = helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resolution)
195    helper_prediction = helper_predict_with_model(self, helper_data[2], plot_raw, apply_link,
196                                                 None,
197                                                 ycols, predict_kw, samples)
198    plots = _plot_samples(self, canvas, helper_data, helper_prediction,
199                          projection, label, **kwargs)
200    return pl().add_to_canvas(canvas, plots)
201
202def _plot_samples(self, canvas, helper_data, helper_prediction, projection,
203              label, **kwargs):
204    _, free_dims, Xgrid, x, y, _, _, resolution = helper_data
205    samples = helper_prediction[2]
206
207    if len(free_dims)<=2:
208        if len(free_dims)==1:
209            # 1D plotting:
210            update_not_existing_kwargs(kwargs, pl().defaults.samples_1d)  # @UndefinedVariable
211            plots = [pl().plot(canvas, Xgrid[:, free_dims], samples[:, :, s], label=label if s==0 else None, **kwargs) for s in range(samples.shape[-1])]
212        elif len(free_dims)==2 and projection=='3d':
213            update_not_existing_kwargs(kwargs, pl().defaults.samples_3d)  # @UndefinedVariable
214            plots = [pl().surface(canvas, x, y, samples[:, :, s].reshape(resolution, resolution), **kwargs) for s in range(samples.shape[-1])]
215        else:
216            pass # Nothing to plot!
217        return dict(gpmean=plots)
218    else:
219        raise RuntimeError('Cannot plot mean in more then 1 input dimensions')
220
221
222def plot_density(self, plot_limits=None, fixed_inputs=None,
223              resolution=None, plot_raw=False,
224              apply_link=False, visible_dims=None,
225              which_data_ycols='all',
226              levels=35, label='gp density',
227              predict_kw=None,
228              **kwargs):
229    """
230    Plot the confidence interval between the percentiles lower and upper.
231    E.g. the 95% confidence interval is $2.5, 97.5$.
232    Note: Only implemented for one dimension!
233
234    You can deactivate the legend for this one plot by supplying None to label.
235
236    Give the Y_metadata in the predict_kw if you need it.
237
238    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
239    :type plot_limits: np.array
240    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
241    :type fixed_inputs: a list of tuples
242    :param int resolution: The resolution of the prediction [default:200]
243    :param bool plot_raw: plot the latent function (usually denoted f) only?
244    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
245    :param array-like visible_dims: which columns of the input X (!) to plot (array-like or list of ints)
246    :param array-like which_data_ycols: which columns of y to plot (array-like or list of ints)
247    :param int levels: the number of levels in the density (number bigger then 1, where 35 is smooth and 1 is the same as plot_confidence). You can go higher then 50 if the result is not smooth enough for you.
248    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
249    """
250    canvas, kwargs = pl().new_canvas(**kwargs)
251    X = get_x_y_var(self)[0]
252    helper_data = helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resolution)
253    helper_prediction = helper_predict_with_model(self, helper_data[2], plot_raw,
254                                          apply_link, np.linspace(2.5, 97.5, levels*2),
255                                          get_which_data_ycols(self, which_data_ycols),
256                                          predict_kw)
257    plots = _plot_density(self, canvas, helper_data, helper_prediction, label, **kwargs)
258    return pl().add_to_canvas(canvas, plots)
259
260def _plot_density(self, canvas, helper_data, helper_prediction, label, **kwargs):
261    _, free_dims, Xgrid, _, _, _, _, _ = helper_data
262    mu, percs, _ = helper_prediction
263
264    update_not_existing_kwargs(kwargs, pl().defaults.density)  # @UndefinedVariable
265
266    if len(free_dims)<=1:
267        if len(free_dims)==1:
268            # 1D plotting:
269            fills = []
270            for d in range(mu.shape[1]):
271                fills.append(pl().fill_gradient(
272                    canvas, Xgrid[:, free_dims[0]], [p[:,d] for p in percs],
273                    label=label, **kwargs)
274                )
275            return dict(gpdensity=fills)
276        else:
277            pass # Nothing to plot!
278    else:
279        raise RuntimeError('Can only plot density in one input dimension')
280
281def plot(self, plot_limits=None, fixed_inputs=None,
282              resolution=None,
283              plot_raw=False, apply_link=False,
284              which_data_ycols='all', which_data_rows='all',
285              visible_dims=None,
286              levels=20, samples=0, samples_likelihood=0, lower=2.5, upper=97.5,
287              plot_data=True, plot_inducing=True, plot_density=False,
288              predict_kw=None, projection='2d', legend=True, **kwargs):
289    """
290    Convenience function for plotting the fit of a GP.
291
292    You can deactivate the legend for this one plot by supplying None to label.
293
294    Give the Y_metadata in the predict_kw if you need it.
295
296
297    If you want fine graned control use the specific plotting functions supplied in the model.
298
299    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
300    :type plot_limits: np.array
301    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
302    :type fixed_inputs: a list of tuples
303    :param int resolution: The resolution of the prediction [default:200]
304    :param bool plot_raw: plot the latent function (usually denoted f) only?
305    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
306    :param which_data_ycols: when the data has several columns (independant outputs), only plot these
307    :type which_data_ycols: 'all' or a list of integers
308    :param which_data_rows: which of the training data to plot (default all)
309    :type which_data_rows: 'all' or a slice object to slice self.X, self.Y
310    :param array-like visible_dims: which columns of the input X (!) to plot (array-like or list of ints)
311    :param int levels: the number of levels in the density (number bigger then 1, where 35 is smooth and 1 is the same as plot_confidence). You can go higher then 50 if the result is not smooth enough for you.
312    :param int samples: the number of samples to draw from the GP and plot into the plot. This will allways be samples from the latent function.
313    :param int samples_likelihood: the number of samples to draw from the GP and apply the likelihood noise. This is usually not what you want!
314    :param float lower: the lower percentile to plot
315    :param float upper: the upper percentile to plot
316    :param bool plot_data: plot the data into the plot?
317    :param bool plot_inducing: plot inducing inputs?
318    :param bool plot_density: plot density instead of the confidence interval?
319    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
320    :param {2d|3d} projection: plot in 2d or 3d?
321    :param bool legend: convenience, whether to put a legend on the plot or not.
322    """
323    X = get_x_y_var(self)[0]
324    helper_data = helper_for_plot_data(self, X, plot_limits, visible_dims, fixed_inputs, resolution)
325    xmin, xmax = helper_data[5:7]
326    free_dims = helper_data[1]
327
328    if not 'xlim' in kwargs:
329        kwargs['xlim'] = (xmin[0], xmax[0])
330    if not 'ylim' in kwargs and len(free_dims) == 2:
331        kwargs['ylim'] = (xmin[1], xmax[1])
332
333    canvas, _ = pl().new_canvas(projection=projection, **kwargs)
334    helper_prediction = helper_predict_with_model(self, helper_data[2], plot_raw,
335                                          apply_link, np.linspace(2.5, 97.5, levels*2) if plot_density else (lower,upper),
336                                          get_which_data_ycols(self, which_data_ycols),
337                                          predict_kw, samples)
338    if plot_raw and not apply_link:
339        # It does not make sense to plot the data (which lives not in the latent function space) into latent function space.
340        plot_data = False
341    plots = {}
342    if hasattr(self, 'Z') and plot_inducing:
343        plots.update(_plot_inducing(self, canvas, free_dims, projection, 'Inducing'))
344    if plot_data:
345        plots.update(_plot_data(self, canvas, which_data_rows, which_data_ycols, free_dims, projection, "Data"))
346        plots.update(_plot_data_error(self, canvas, which_data_rows, which_data_ycols, free_dims, projection, "Data Error"))
347    plots.update(_plot(self, canvas, plots, helper_data, helper_prediction, levels, plot_inducing, plot_density, projection))
348    if plot_raw and (samples_likelihood > 0):
349        helper_prediction = helper_predict_with_model(self, helper_data[2], False,
350                                      apply_link, None,
351                                      get_which_data_ycols(self, which_data_ycols),
352                                      predict_kw, samples_likelihood)
353        plots.update(_plot_samples(canvas, helper_data, helper_prediction, projection, "Lik Samples"))
354    return pl().add_to_canvas(canvas, plots, legend=legend)
355
356
357def plot_f(self, plot_limits=None, fixed_inputs=None,
358              resolution=None,
359              apply_link=False,
360              which_data_ycols='all', which_data_rows='all',
361              visible_dims=None,
362              levels=20, samples=0, lower=2.5, upper=97.5,
363              plot_density=False,
364              plot_data=True, plot_inducing=True,
365              projection='2d', legend=True,
366              predict_kw=None,
367              **kwargs):
368    """
369    Convinience function for plotting the fit of a GP.
370    This is the same as plot, except it plots the latent function fit of the GP!
371
372    If you want fine graned control use the specific plotting functions supplied in the model.
373
374    You can deactivate the legend for this one plot by supplying None to label.
375
376    Give the Y_metadata in the predict_kw if you need it.
377
378
379    :param plot_limits: The limits of the plot. If 1D [xmin,xmax], if 2D [[xmin,ymin],[xmax,ymax]]. Defaluts to data limits
380    :type plot_limits: np.array
381    :param fixed_inputs: a list of tuple [(i,v), (i,v)...], specifying that input dimension i should be set to value v.
382    :type fixed_inputs: a list of tuples
383    :param int resolution: The resolution of the prediction [default:200]
384    :param bool apply_link: whether to apply the link function of the GP to the raw prediction.
385    :param which_data_ycols: when the data has several columns (independant outputs), only plot these
386    :type which_data_ycols: 'all' or a list of integers
387    :param which_data_rows: which of the training data to plot (default all)
388    :type which_data_rows: 'all' or a slice object to slice self.X, self.Y
389    :param array-like visible_dims: an array specifying the input dimensions to plot (maximum two)
390    :param int levels: the number of levels in the density (number bigger then 1, where 35 is smooth and 1 is the same as plot_confidence). You can go higher then 50 if the result is not smooth enough for you.
391    :param int samples: the number of samples to draw from the GP and plot into the plot. This will allways be samples from the latent function.
392    :param float lower: the lower percentile to plot
393    :param float upper: the upper percentile to plot
394    :param bool plot_data: plot the data into the plot?
395    :param bool plot_inducing: plot inducing inputs?
396    :param bool plot_density: plot density instead of the confidence interval?
397    :param dict predict_kw: the keyword arguments for the prediction. If you want to plot a specific kernel give dict(kern=<specific kernel>) in here
398    :param dict error_kwargs: kwargs for the error plot for the plotting library you are using
399    :param kwargs plot_kwargs: kwargs for the data plot for the plotting library you are using
400    """
401    return plot(self, plot_limits, fixed_inputs, resolution, True,
402         apply_link, which_data_ycols, which_data_rows,
403         visible_dims, levels, samples, 0,
404         lower, upper, plot_data, plot_inducing,
405         plot_density, predict_kw, projection, legend, **kwargs)
406
407
408
409def _plot(self, canvas, plots, helper_data, helper_prediction, levels, plot_inducing=True, plot_density=False, projection='2d'):
410        plots.update(_plot_mean(self, canvas, helper_data, helper_prediction, levels, projection, 'Mean'))
411
412        try:
413            if projection=='2d':
414                if not plot_density:
415                    plots.update(_plot_confidence(self, canvas, helper_data, helper_prediction, "Confidence"))
416                else:
417                    plots.update(_plot_density(self, canvas, helper_data, helper_prediction, "Density"))
418        except RuntimeError:
419            #plotting in 2d
420            pass
421
422        if helper_prediction[2] is not None:
423            plots.update(_plot_samples(self, canvas, helper_data, helper_prediction, projection, "Samples"))
424        return plots
425