Source code for pygeostat.plotting.drill_plot

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

"""drill_plot for a drill hole data set"""
#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------

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

from . set_style import set_plot_style

[docs] @set_plot_style def drill_plot(z, var, cat=None, categorical_dictionary=None, lw=2, line_color='k', barwidth=None, namelist=None, legend_fontsize=10, title=None, ylabel=None, unit=None, grid=None, axis_xy=None, reverse_y=False, xlim=None, ylim=None, figsize=(2,10), ax=None,plot_style=None, custom_style=None, output_file=None, out_kws=None, **kwargs): ''' A well log plot for both continuous and categorical variables. This plot handles one well log plot at a time and the user can choose to generate subplots and pass the axes to this function if multiple well log plots are required. Parameters: z (Elevation/Depth or distance along the well): 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. var (Variable being plotted): 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. lw (float): line width for log plot of a continuous variable line_color (string): line color for the continuous variable barwidth(float): width of categorical bars categorical_dictionary (dictionary): a dictionary of colors and names for categorical codes. E.g. {1: {'name': 'Sand', 'color': 'gold'}, 2: {'name': 'SHIS','color': 'orange'}, 3: {'name': 'MHIS','color': 'green'}, 4: {'name': 'MUD','color': 'gray'}} legend_fontsize(float): fontsize for the legend plot rleated to the categorical codes. set this parameter to 0 if you do not want to have a legend title (str): title for the variable ylabel (str): Y-axis label, based on ``Parameters['plotting.zname']`` if None. unit (str): Unit to place inside the y-label parentheses, based on ``Parameters['plotting.unit']`` if None. grid (bool): Plots the major grid lines if True. Based on ``Parameters['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 ``Parameters['plotting.axis_xy']`` if None. reverse_y(bool): if true, the yaxis direction is set to reverse(applies to the cases that depth is plotted and not elevation) aspect (str): Set a permissible aspect ratio of the image to pass to matplotlib. xlim (float tuple): X-axis limits ylim (float tuple): Y-axis limits figsize (tuple): Figure size (width, height) ax (mpl.axis): Existing matplotlib axis to plot the figure onto out_kws (dict): Optional dictionary of permissible keyword arguments to pass to :func:`gs.export_image() <pygeostat.plotting.export_image.export_image>` Returns: ax (ax): Matplotlib axis instance which contains the gridded figure Examples: A simple call to plot a continuous variable .. plot:: import pygeostat as gs dat = gs.ExampleData('point3d_ind_mv') data = dat.data[dat.data['HoleID'] == 3] gs.drill_plot(data['Elevation'], data['Sw'], grid = True) | Plot a categorical variable .. plot:: import pygeostat as gs dat = gs.ExampleData('point3d_ind_mv') data = dat.data[dat.data['HoleID'] == 3] gs.drill_plot(data['Elevation'], data['Lithofacies']) | Plot a categorical variable and provide a categorical dictionary .. plot:: import pygeostat as gs dat = gs.ExampleData('point3d_ind_mv') data = dat.data[dat.data['HoleID'] == 3] cat_dict = {1: {'name': 'Sand', 'color': 'gold'}, 3: {'name': 'SHIS','color': 'orange'}, 4: {'name': 'MHIS','color': 'green'}, 5: {'name': 'MUD','color': 'gray'}} gs.drill_plot(data['Elevation'], data['Lithofacies'], categorical_dictionary=cat_dict) ''' from matplotlib.ticker import FormatStrFormatter import matplotlib.patches as mpatches import matplotlib.pyplot as plt from .export_image import export_image import numpy as np import pandas as pd from .. pygeostat_parameters import Parameters from .utils import format_plot, get_label, setup_plot, catcmapfromcontinuous, _spatial_aspect # Sanity checks if not isinstance(z, (np.ndarray, np.generic, pd.Series)): raise ValueError("The parameter `z` must be a numpy array or a pandas series") if not isinstance(var, (np.ndarray, np.generic, pd.Series)): raise ValueError("The parameter `var` must be a numpy array or a pandas series") if title is None: title = get_label(var) if unit is None: unit = Parameters['plotting.unit'] if unit is None or unit == '': unit = '' else: unit = ' ({})'.format(unit) if ylabel: ax.set_ylabel(ylabel) elif ylabel is None: ylabel = Parameters['plotting.zname'] + unit if not out_kws: out_kws = dict() fig, ax, cax = setup_plot(ax, figsize=figsize, aspect = False) # Create a new data frame df = pd.DataFrame() df['Z'] = np.array(z) df['Var'] = np.array(var) df.sort_values('Z', axis=0, ascending=True, inplace=True) df.reset_index(inplace=True, drop=True) if barwidth is None: barwidth = (np.max(df['Z'].values) - np.min(df['Z'].values))/10 if cat is None: if (len(set(df['Var'].values)) < (len(df) / 5)) : cat = True # Start of the plot if not cat: ax.plot(df['Var'], df['Z'], lw=lw, c=line_color, **kwargs) xlim = [np.nanmin(df['Var']), np.nanmax(df['Var'])] ylim = [np.nanmin(df['Z']), np.nanmax(df['Z'])] ax.set_xticks(np.linspace(np.nanmin(df['Var']), np.nanmax(df['Var']), 2)) ax.xaxis.set_major_formatter(FormatStrFormatter('%.1f')) ax.xaxis.set_label_position('top') ax.xaxis.set_ticks_position('top') else: categories = sorted(set(df['Var'].values)) ncat = len(categories) # make sure grid option is not triggered grid = False if categorical_dictionary is None: colorlist = catcmapfromcontinuous("Spectral", ncat).colors namelist = [] for i in range(ncat): string = 'Category Code %i' % (i + 1) namelist.append(string) categorical_dictionary = {} for i, item in enumerate(categories): categorical_dictionary.update({item: {'name': namelist[i], 'color': colorlist[i]} }) else: if len(categorical_dictionary)<ncat: raise ValueError('The provided categorical_dictionary does not match the number of categorical codes') for item in categorical_dictionary.keys(): if item not in categories: raise ValueError('The provided categorical code, {} does not exist in the provided data'.format(item)) # First bar from bottom based on sorting the z column category = df['Var'][0] color = categorical_dictionary[category]['color'] offset = (df['Z'][1] - df['Z'][0])/2 z_b = df['Z'][0] z_t = z_b + offset ax.fill_between([0, barwidth], [z_b, z_b], [z_t,z_t], color = color) for i in range(1,len(df)-1): upper_offset = (df['Z'][i+1] - df['Z'][i])/2 lower_offset = (df['Z'][i-1] - df['Z'][i])/2 z_b = df['Z'][i] + lower_offset z_t = df['Z'][i] + upper_offset category = df['Var'][i] color = categorical_dictionary[category]['color'] ax.fill_between([0, barwidth], [z_b, z_b], [z_t,z_t], color = color) # Last bar from bottom based on sorting the z column category = df['Var'][i+1] color = categorical_dictionary[category]['color'] z_b = z_t z_t = df['Z'][i+1] ax.fill_between([0, barwidth], [z_b, z_b], [z_t,z_t], color = color) Patch_list = [] for item in categorical_dictionary.values(): Patch_list.append(mpatches.Patch(color=item['color'], label=item['name'])) if legend_fontsize > 0: ax.legend(handles=Patch_list, loc='upper left', bbox_to_anchor=(1, 1), fontsize=legend_fontsize) ylim = [np.nanmin(df['Z']), np.nanmax(df['Z'])] xlim = [0, barwidth] ax.xaxis.set_label_position('top') ax.get_xaxis().set_visible(False) format_plot(ax, xlabel='', ylabel = ylabel, title = title, grid = grid, axis_xy = axis_xy, xlim = xlim, ylim = ylim) if reverse_y: ax.invert_yaxis() if output_file or ('pdfpages' in out_kws): export_image(output_file, **out_kws) return ax