Source code for pygeostat.plotting.validation_plot

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""validation_plot plots a scatter plot of cross validation results with relevant statistics"""

#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import numpy as np
import pandas as pd
from scipy import stats
import matplotlib as mpl
import matplotlib.pyplot as plt
from .utils import get_label, get_statblk
from .export_image import export_image
from ..statistics.kde import kde_scipy
from . set_style import set_plot_style


[docs] @set_plot_style def validation_plot(x, y, figsize=None, vlim=None, xlabel=None, ylabel=None, title=None, stat_blk='all', stat_xy=(0.95, 0.05), stat_ha=None, stat_fontsize=None, mc='k', ms=None, plot_style=None, lw=None, grid=None, axis_xy=None, custom_style=None, output_file=None, ax=None, dens=False, rasterized=False, **kwargs): """ This function uses numpy to calculate the regression model and matplotlib to plot the scatter plot, regression line, and 45 degree line. Statistics are calculated using numpy. The only parameters needed are the ``x`` and ``y``. All of the other arguments are optional. If the label parameters are left to their default value of ``None``, the column information will be used to label the axes. Two statistics block sets are available: ``'minimal'`` and the default ``'all'``. The statistics block can be customized to a user defined list and order. Available statistics are as follows: >>> ['ndat', 'ymean', 'ystdev', 'xmean', 'xstdev', 'cov', 'rho', 'mse', 'sor'] Please review the documentation of the :func:`gs.set_style() <pygeostat.plotting.set_style.set_style>` and :func:`gs.export_image() <pygeostat.plotting.export_image.export_image>` functions for details on their parameters so that their use in this function can be understood. Parameters: x: Tidy (long-form) 1D data where a single column of the variable to plot along the x-axis exists with each row is an observation. A pandas dataframe/series or numpy array can be passed. y: Tidy (long-form) 1D data where a single column of the variable to plot along the y-axis exists with each row is an observation. A pandas dataframe/series or numpy array can be passed. figsize (tuple): Figure size (width, height) vlim (float tuple): A single tuple for the minimum and maximum limits of data along both axes. Will not be a symmetrical plot if they are not the same value xlabel (str): X-axis label ylabel (str): Y-axis label title (str): Title for the plot stat_blk (str or list): Indicate what preset statistics block to write or a specific list stat_xy (str or float tuple): X, Y coordinates of the annotated statistics in figure space. stat_ha (str): Horizontal alignment parameter for the annotated statistics. Can be ``'right'``, ``'left'``, or ``'center'``. The value ``None`` can also be used to allow the parameter ``stat_xy`` to determine the alignment automatically. stat_fontsize (float): the fontsize for the statistics block. If None, based on gsParams['plotting.stat_fontsize']. If less than 1, it is the fraction of the matplotlib.rcParams['font.size']. If greater than 1, it the absolute font size. mc (str): Any permissible matplotlib color value for the scatter plot markers ms (float): Size of scatter plot markers grid(bool): plots the major grid lines if True. Based on gsParams['plotting.grid'] if None. axis_xy (bool): converts the axis to GSLIB-style axis visibility (only left and bottom visible) if axis_xy is True. Based on gsParams['plotting.axis_xy'] if None. plot_style (str): Use a predefined set of matplotlib plotting parameters as specified by :class:`gs.GridDef <pygeostat.data.grid_definition.GridDef>`. Use ``False`` or ``None`` to turn it off custom_style (dict): Alter some of the predefined parameters in the ``plot_style`` selected. output_file (str): Output figure file name and location **kwargs: Optional permissible keyword arguments to pass to :func:`gs.export_image() <pygeostat.plotting.export_image.export_image>` Returns: ax (ax): Matplotlib Axes object with the cross validation plot **Examples:** A simple call: .. plot:: import pygeostat as gs data = gs.ExampleData('3d_estimate') gs.validation_plot(data.data['Estimate'], data.data['True'], stat_blk='minimal') | Fixing the value limits, moving the statistics block, and exporting the figure. .. plot:: import pygeostat as gs import numpy as np mean = [0, 0] cov = [[1, 0.8], [0.8, 1]] # diagonal covariance x, y = np.random.multivariate_normal(mean, cov, 5000).T gs.validation_plot(x,y,vlim=(-3.5, 3.5) ,grid=True, stat_xy=(1, 0.68)) | .. codeauthor:: pygeostat development team 2015-08-05 """ import types from .utils import format_plot, _set_stat_fontsize # Convert list to pd.series if isinstance(x, list): x = pd.DataFrame(x) x = x[0] if isinstance(y, list): y = pd.DataFrame(y) y = y[0] if isinstance(x, pd.DataFrame): x = x[x.columns[0]] if isinstance(y, pd.DataFrame): y = y[y.columns[0]] # Set-up figure if ax is None: _, ax = plt.subplots(figsize=figsize) # Configure plot ax.axis('equal') if xlabel is None: xlabel = get_label(x) if xlabel == 0: xlabel = None if xlabel is None: xlabel = 'Estimate' if ylabel is None: ylabel = get_label(y) if ylabel == 0: ylabel = None if ylabel is None: ylabel = 'True' ax = format_plot(ax, xlabel=xlabel, ylabel=ylabel, title=title, grid=grid, axis_xy=axis_xy, xlim=vlim, ylim=vlim) #ax.tick_params(axis='both', pad=2) if isinstance(dens, types.FunctionType): # fucntion takes (x, y, #comps) as positional args and returns `colors` mc = dens(x, y, 8) else: if dens: # plot the points as density colors values = np.vstack([x, y]) mc = kde_scipy(values, values) if lw is None: lw = 0 # Plot the scatter plot ax.scatter(x, y, c=mc, s=ms, lw=lw, rasterized=rasterized) # Get the axis limits determined by plt.scatter() if ax.get_ylim()[1] >= ax.get_xlim()[1]: maxlim = ax.get_ylim() else: maxlim = ax.get_xlim() # Plot the regression model regmodel = np.polyfit(x, y, 1) predicted = np.polyval(regmodel, [maxlim[0], maxlim[1]]) ax.plot([maxlim[0], maxlim[1]], predicted, '-', lw=0.5, color='red') # Plots stats if stat_blk: ndat = len(x) ymean = np.nanmean(y) ystdev = np.nanstd(y) xmean = np.nanmean(x) xstdev = np.nanstd(x) cov = np.cov(x, y)[0, 1] rho = stats.pearsonr(x, y)[0] mse = np.sqrt(((x - y) ** 2).mean()) sor = stats.linregress(x, y)[0] statlist = {'ndat': ('$n = {}$'.format(ndat)), 'ymean': (r'$m_y = {:0.3f}$'.format(ymean)), 'ystdev': (r'$\sigma_y = {:0.3f}$'.format(ystdev)), 'xmean': (r'$m_x = {:0.3f}$'.format(xmean)), 'xstdev': (r'$\sigma_x = {:0.3f}$'.format(xstdev)), 'cov': ('$cov = {:0.3f}$'.format(cov)), 'rho': (r'$\rho = {:0.3f}$'.format(rho)), 'mse': ('$RMSE = {:0.3f}$'.format(mse)), 'sor': ('$SoR = {:0.3f}$'.format(sor)), 'newline': ''} # Default statistic sets statsets = {'minimal': ['ndat', 'rho', 'mse', 'sor'], 'all': ['ndat', 'newline', 'ymean', 'ystdev', 'newline', 'xmean', 'xstdev', 'newline', 'cov', 'rho', 'mse', 'sor'], 'none': None} txtstats, stat_xy, ha, va = get_statblk(stat_blk, statsets, statlist, stat_xy) if stat_ha: ha = stat_ha stat_fontsize = _set_stat_fontsize(stat_fontsize) ax.text(stat_xy[0], stat_xy[1], txtstats, va=va, ha=ha, fontsize=stat_fontsize, transform=ax.transAxes) # Plot the 45 degree line ax.plot(maxlim, maxlim, lw=0.5, color='k') ax.set(xlim=maxlim, ylim=maxlim) # Reset the axis limits as the 45 degree line pushed it out # Export figure if output_file or ('pdfpages' in kwargs): export_image(output_file, **kwargs) return ax