Source code for pygeostat.plotting.qq_plot

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

"""A basic quantile by quantile plot to compare two probability distribution"""
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------


from . set_style import set_plot_style


[docs] @set_plot_style def qq_plot(data, reference_data, data_weight=None, reference_weight=None, limits=None, npoints=0, log_scale=None, ax=None, figsize=None, title=None, xlabel=None, ylabel=None, s=None, percent=True, color='k', grid=None, axis_xy=None, plot_style=None, custom_style=None, output_file=None, out_kws=None, line=True, ntickbins=5, **kwargs): """ Plot a QQ plot between the reference. Pretty much the probplt but with 2 datasets and plotting the quantiles between them Parameters: data: Tidy (long-form) 1D data where a single column of the variable exists with each row is an observation. A pandas dataframe/series or numpy array can be passed. reference_data: Tidy (long-form) 1D data or a valid scipy.stats distribution (e.g. "norm"). A pandas dataframe/series or numpy array can be passed. data_weight: 1D dataframe, series, or numpy array of declustering weights for the data. reference_weight: 1D dataframe, series, or numpy array of declustering weights for the data. lower (float): Lower trimming limits upper (float): Upper trimming limits limits (tuple): the min and max value of the axes ax (mpl.axis): Matplotlib axis to plot the figure log_scale (bool): yes or no to log_scale npoints (int): set to 0 to use all points figsize (tuple): Figure size (width, height) xlim (float tuple): Minimum and maximum limits of data along the x axis title (str): Title for the plot xlabel (str): X-axis label. A default value of ``None`` will try and grab a label from the passed ``data``. Pass ``False`` to not have an xlabel. s (int): Size of points color (str or int): Any permissible matplotlib color or a integer which is used to draw a color from the pygeostat color pallet ``pallet_pastel`` (useful for iteration) 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 out_kws (dict): Optional dictionary of permissible keyword arguments to pass to :func:`gs.export_image() <pygeostat.plotting.export_image.export_image>` line (bool): Plot the reference 1:1 line ntickbins (int or tuple): modify the number of ticks. Only works if log_scale == ``True`` **kwargs: Optional permissible keyword arguments to pass to matplotlib's scatter function Returns: ax (ax): matplotlib Axes object with the histogram **Examples:** A simple call: .. plot:: import pygeostat as gs import numpy as np # load some data gs.qq_plot(np.random.randn(1000),np.random.randn(1000)) """ # Import the rest of the packages import numpy as np import pandas as pd from .utils import get_label, format_plot from .export_image import export_image from ..statistics.cdf import cdf import matplotlib.pyplot as plt if xlabel is None: xlabel = get_label(data) if ylabel is None: ylabel = get_label(reference_data) # Coerce the passed data and wt into a single pandas dataframe try: data = pd.DataFrame(data=data) data.reset_index(inplace=True, drop=True) if data.shape[1] != 1: raise ValueError("The passed `data` is not 1-D") except: raise ValueError("Please ensure the passed `data` can be coerced into a pandas dataframe" " (i.e., data = pd.DataFrame(data=data)") if data_weight is not None: try: data_weight = pd.DataFrame(data=data_weight, columns=["wt"]) if data_weight.shape[1] != 1: raise ValueError("The passed `data_weight` is not 1-D") except: raise ValueError("Please ensure the passed `data_weight` can be coerced into a pandas" " dataframe (i.e., data_weight = pd.DataFrame(wt=data_weight)") else: data_weight = pd.DataFrame(data=np.ones((len(data), 1))) data = pd.concat([data, data_weight], axis=1) data.columns = ['data', 'data_weight'] # Do the same for the reference data try: reference_data = pd.DataFrame(data=reference_data) reference_data.reset_index(inplace=True, drop=True) if reference_data.shape[1] != 1: raise ValueError("The passed `reference_data` is not 1-D") except: raise ValueError("Please ensure the passed `reference_data` can be coerced into a pandas dataframe" " (i.e., reference_data = pd.DataFrame(data=reference_data)") if reference_weight is not None: try: reference_weight = pd.DataFrame(data=reference_weight) if reference_weight.shape[1] != 1: raise ValueError("The passed `reference_weight` is not 1-D") except: raise ValueError("Please ensure the passed `reference_weight` can be coerced into a pandas" " dataframe (i.e., reference_weight = pd.DataFrame(reference_weight=reference_weight)") else: reference_weight = pd.DataFrame(data=np.ones((len(reference_data), 1))) reference_data = pd.concat([reference_data, reference_weight], axis=1) reference_data.columns = ['reference_data', 'reference_weight'] # Handle dictionary defaults if out_kws is None: out_kws = dict() # build the cdf's using all bins print(len(data)) mp1, p1 = cdf(data['data'], weights=data['data_weight']) mp2, p2 = cdf(reference_data['reference_data'], weights=reference_data['reference_weight']) if npoints <= 0: npoints = min([len(data), len(reference_data), 1e10]) # generate and interpolate the equally spaced set of quantiles probs = np.linspace(1 / npoints, 1, npoints) x = np.interp(probs, p2, mp2) y = np.interp(probs, p1, mp1) # Set-up plot if no axis is supplied if ax is None: _, ax = plt.subplots(figsize=figsize) # Plot the figure ax.scatter(x, y, s=s, c=color, lw=0, **kwargs) if limits is None: limits = np.minimum(np.min(mp1), np.min(mp2)), np.maximum(np.max(mp1), np.max(mp2)) # apply the log_scale if log_scale: ax.set_xscale('log') ax.set_yscale('log') if line: ax.plot(limits, limits, color='r', lw=0.5) # deal with the labels and formatting if xlabel is None: xlabel = '$ref_{quantiles}$' if ylabel is None: ylabel = '$data_{quantiles}$' ax = format_plot(ax, xlabel=xlabel, ylabel=ylabel, title=title, grid=grid, axis_xy=axis_xy, xlim=limits, ylim=limits) # Export figure if output_file or ('pdfpages' in out_kws): export_image(output_file, **out_kws) return ax