Source code for pygeostat.plotting.grid_slice_plot

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Function that emulates the GSLIB program plotem when used with slice_plot"""
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------

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

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid

from . set_style import set_plot_style
from .. pygeostat_parameters import Parameters


[docs] @set_plot_style def grid_slice_plot(data, griddef = None, super_xlabel=True, super_ylabel=True, super_title=None, ncol=None, nrow=None, start_slice=None, end_slice=None, figsize=None, n_slice=None, slice_title=True, unit=None, plot_style=None, custom_style=None, output_file=None, out_kws=None, cbar_label=None, axpad=0.15, cbar_cats=None, ntickbins=None, axfuncs=None, label_mode='L', **kwargs): """ grid_slice_plot can be used to automatically generate set of slices through a 3D gridded realization. Given some target number of rows, columns, orientation and slice ranges through the realization, this function will automatically generate the slice_plot slices and arrange them according to the specified dimensions. It is possible to pass keyword arguments for slice_plot to this function in order to specify the format of the slice_plots. So, adding data locations, different colormaps, and other slice_plot formatting is permitted for all subplots by passing those slice_plot arguments to this function. See :func:`gs.slice_plot()<pygeostat.plotting.slice_plot>` for the full list of permissable kwargs. Updates April 2016 - use a ImageGrid subplots to get the job done Parameters: data (array, dataframe) : array of data, passed directly to slice_plot() griddef (pygeostat griddef) : pygeostat grid definitions, passed directly to slice_plot() super_xlabel (str) : super x axis label super_ylabel (str) : super y axis label super_title (str) : super title for the subplots ncol (int) : the number of columns considered for the subplots (may change) nrow (int) : the number of rows considered for the subplots (may change) start_slice (int) : the starting slice to be plotted end_slice (int) : the end slice to be plotted figsize (tuple) : size of the figure to be created n_slice (int) : the number of desired slices slice_title (bool) : either plot the orientation and slice no on top of each slice, or dont! unit (str): Unit to place inside the axis label parentheses 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.exportimg() <pygeostat.plotting.exportimg.exportimg>` cbar_label (str): colorbar title axpad (float): figure points padding applied to the axes, vertical padding is further modified to account for the slice titles, if required ntickbins (int or tuple): The number of tick bins for both axes, or the (x, y) respectively axfuncs (function or list of functions): External function(s) that takes `ax` `slice_number` and `orient` as keyword arguments, does not return anything label_mode (str): default `'L'`, or `'all'`, passed to the ImageGrid() constructor **kwargs : **NOTE** the arguments here are either valid slice_plot (including all keyword) dictionary arguments, or valid imshow and valid imshow keyword arguments. If errors are thrown from invalid arguments it is likely that something that shouldnt have been passed to imshow was passed. Check and double check those \**kwargs! Returns: fig (plt.figure) : figure handle **Examples:** A simple call: generates a set of slices through the model .. plot:: import pygeostat as gs # load some data data = gs.ExampleData('3d_grid',griddef = gs.GridDef([40,1,2,40,1,2,40,0.5,1])) # plot the grid slices _ = gs.grid_slice_plot(data) | Possible to specify the orientation and the number of slices: .. plot:: import pygeostat as gs # load some data data = gs.ExampleData('3d_grid',griddef = gs.GridDef([40,1,2,40,1,2,40,0.5,1])) # plot the grid slices _ = gs.grid_slice_plot(data, orient='xz', n_slice=5) | Can specify the number of rows or columns required for the slices: .. plot:: import pygeostat as gs # load some data data = gs.ExampleData('3d_grid',griddef = gs.GridDef([40,1,2,40,1,2,40,0.5,1])) # plot the grid slices _ = gs.grid_slice_plot(data, orient='xz', n_slice=6, ncol=2, nrow=3) | Also able to specify slice_plot kwargs using this function, so we can apply consistent custom formatting to all of the subplots: .. plot:: import pygeostat as gs # load some data data = gs.ExampleData('3d_grid',griddef = gs.GridDef([40,1,2,40,1,2,40,0.5,1])) # plot the grid slices _ = gs.grid_slice_plot(data, nrow=2, ncol=5, start_slice=10, end_slice=25, n_slice=10, cmap='hot', vlim=(-3,3)) """ from . slice_plot import slice_plot from .utils import (addxticks, get_contcbarargs, get_supaxislocs) from ..datautils.utils import slice_grid from ..utility.logging import printerr from .export_image import export_image from ..data.data import DataFile if axfuncs is not None and not isinstance(axfuncs, list): axfuncs = [axfuncs] if out_kws is None: out_kws = dict() if griddef is None: if isinstance(data, DataFile): griddef = data.griddef else: raise ValueError ('griddef must be provided') if isinstance(data, DataFile): data = data.data if 'orient' in kwargs: ori = kwargs.get('orient') del kwargs['orient'] else: ori = 'xy' if slice_title and 'title' in kwargs: del kwargs['title'] if 'slice_number' in kwargs: del kwargs['slice_number'] if 'ax' in kwargs: del kwargs['ax'] if 'catdata' in kwargs: catdata = kwargs.get('catdata') else: if len(np.unique(data)) <= 12: catdata = True else: catdata = False if start_slice is None or start_slice < 0: start_slice = 0 if hasattr(data, 'values'): data = np.array(data) di = ori[1] if end_slice is None or end_slice > getattr(griddef, 'n' + di): if ori == 'xy': end_slice = griddef.nz elif ori == 'xz': end_slice = griddef.ny elif ori == 'yz': end_slice = griddef.nx if end_slice <= 0: print('Bad end_slice parameter, defaulting to 1') end_slice = 1 if n_slice is None: n_slice = 4 if nrow is None: addrows = True nrow = 2 else: addrows = False if ncol is None: addcols = True ncol = 2 else: addcols = False # Try to automatically expand the rows,columns if we dont have enough for the number of slices alternate = 1 if not addrows and not addcols and nrow * ncol < n_slice: printerr('Please ensure there are enough rows and columns explicitly defined for ' 'the target number of plots! (n_slice = %i, nrow*ncol = %i)' % (n_slice, nrow * ncol)) raise Exception() while ncol * nrow < n_slice: if alternate == 1: if addcols: ncol += 1 alternate = 0 else: if addrows: nrow += 1 alternate = 1 # Deal with some of the kwarg inputs (add any other defaults that are needed?) if 'sigfigs' in kwargs: sigfigs = kwargs.get('sigfigs') else: sigfigs = 3 # set the vlim to the min max of the entire dataset if none is provided if 'vlim' in kwargs: vlim = kwargs.get('vlim') del kwargs['vlim'] else: vlim = (np.nanmin(data), np.nanmax(data)) # Turn off slice_plot axis labels if we want super axis labels: if super_xlabel: kwargs['xlabel'] = False if super_ylabel: kwargs['ylabel'] = False # Create the subplots on the new ImageGrid if figsize is None: figsize = mpl.rcParams.get("figure.figsize") # but also needed for later fig = plt.figure(figsize=figsize) # this should just pull from rcParams? if slice_title: if isinstance(mpl.rcParams['axes.titlesize'], str): scalar = 1.1 else: # assume this operation will work.. scalar = mpl.rcParams['axes.titlesize'] / 8 vaxpad = (axpad * scalar) + 0.1 else: vaxpad = axpad imgrid = ImageGrid(fig, 111, nrows_ncols=(nrow, ncol), axes_pad=(axpad, vaxpad), label_mode=label_mode, direction='row', cbar_mode='single', cbar_size=0.15) axind = 0 # ensure a suitable number of slices steps = max(int(np.floor((end_slice - start_slice) / n_slice)), 1) slrange = np.arange(start_slice, end_slice, steps)[0:n_slice] num_slices = 0 num_errors = 0 for sl in slrange: num_slices += 1 if slice_title: ptitle = ori + ' slice at: ' + str(round(griddef.get_slice_coordinate(ori, sl), 1)) else: ptitle = "" if axind < nrow * ncol: try: ax, plot = slice_plot(data, griddef, ax=imgrid[axind], slice_number=sl, orient=ori, vlim=vlim, title=ptitle, cbar=False,return_plot=True, plot_style=False, output_file=None, out_kws=None, **kwargs) if ntickbins is not None: if isinstance(ntickbins, tuple): ax.locator_params(axis='x', nbins=ntickbins[0]) ax.locator_params(axis='y', nbins=ntickbins[1]) else: ax.locator_params(nbins=ntickbins) if axfuncs: for func in axfuncs: func(slice_number=sl, ax=imgrid[axind], orient=ori) axind += 1 except ValueError as error: # hopefully this error was raised by an empty slice.. num_errors += 1 err = error pass if num_errors == num_slices: raise ValueError(err) # remove unused axes from the figure [x.set_axis_off() for x in imgrid[axind:]] # Add xticklabels to the last subplot in a column when it is not in the last row addxticks(fig, nrow, ncol, axind, griddef.extents(orient=ori[0])) # the vlim is either given or set to the max range for the entire array of data.. # could get more clever about this by preanalyzing the data slices and determining the # correct range from all the slices... if catdata: # this section is straight from the slice_plot section for categorical variables ticklabels = np.unique(data[~np.isnan(data)]) if isinstance(cbar_cats, dict): ticklabels = [cbar_cats[cat] for cat in ticklabels] else: ticklabels = [int(x) for x in ticklabels] ncat = len(ticklabels) vlim = (0, ncat) ticklocs = np.arange(ncat) + 0.5 else: vlims, ticklocs, ticklabels = get_contcbarargs(slice_grid(data, griddef, ori, sl), sigfigs, vlim) # get the min and max colorbar arguments cbar = fig.colorbar(plot, imgrid.cbar_axes[0], ticks=ticklocs) cbar.ax.set_yticklabels(ticklabels, ha='left') cbar.ax.tick_params(axis='y', pad=2) if cbar_label is not None: cbar.set_label(cbar_label, ha='center', va='top', labelpad=2) # Get the figure coordates for the superaxis titles and the figure title if super_xlabel or super_ylabel or super_title: xmin, xmid, ymin, ymid, ymax = get_supaxislocs(fig, nrow, ncol, figsize, 0.01) if unit is None: unit = Parameters['plotting.unit'] if unit is None or unit == '': unit = '' else: unit = ' ({})'.format(unit) if super_xlabel: if not isinstance(super_xlabel, str): if ori == 'xy': super_xlabel = Parameters['plotting.xname'] + unit elif ori == 'xz': super_xlabel = Parameters['plotting.xname'] + unit elif ori == 'yz': super_xlabel = Parameters['plotting.yname'] + unit plt.draw() fig.text(xmid, ymin, super_xlabel, va='top', ha='center') if super_ylabel: if not isinstance(super_ylabel, str): if ori == 'xy': super_ylabel = Parameters['plotting.yname'] + unit elif ori == 'xz': super_ylabel = Parameters['plotting.zname'] + unit elif ori == 'yz': super_ylabel = Parameters['plotting.zname'] + unit plt.draw() fig.text(xmin, ymid, super_ylabel, ha='right', rotation=90, va='center') if super_title: plt.draw() fig.text(xmid, ymax, super_title, va='bottom', ha='center') # Export figure if output_file or ('pdfpages' in out_kws): export_image(output_file=output_file, **out_kws) return fig