Source code for geotecha.plotting.one_d

# geotecha - A software suite for geotechncial engineering
# Copyright (C) 2018  Rohan T. Walker (rtrwalker@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/gpl.html.

"""Routines to produce some matplotlib plots for one dimesional data.

One dimensional data is basically x-y data. (i.e. no contour plot needed)

"""

from __future__ import division, print_function

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import brewer2mpl
import random
import itertools
import geotecha.mathematics.transformations as transformations
from geotecha.piecewise.piecewise_linear_1d import PolyLine
import geotecha.piecewise.piecewise_linear_1d as pwise
import warnings


[docs]def rgb_shade(rgb, factor=1, scaled=True): """Apply shade (darken) to a red, green, blue (rgb) triplet If rgba tuple is given the 'a' value is not altered. Parameters ---------- rgb : tuple Triplet of rgb values. Note can be an rgba value. factor : float Between 0 and 1. Factor by which to shade. Default factor=1. scaled : bool If True then assumes RGB values aare scaled between 0, 1. If False rgb values are between 0 and 255. Default scaled=True. Returns ------- rgb_new : tuple New tuple of rgb (or rgba) values. See Also -------- rgb_tint : Lighten an rgb triplet. Examples -------- >>> x=rgb_shade((0.4, 0.8, 0.2), factor=0.5) >>> '{:.2f}, {:.2f}, {:.2f}'.format(*x) '0.20, 0.40, 0.10' """ #calc from http://stackoverflow.com/a/6615053/2530083 if factor<0 or factor>1: raise ValueError('factor must be between 0 and 1. ' 'You have {:g}'.format(factor)) rgb_new = [v * factor for v in rgb[:3]] if len(rgb)==4: rgb_new.append(rgb[3]) return tuple(rgb_new)
[docs]def rgb_tint(rgb, factor=0, scaled=True): """Apply tint (lighten) to a red, green, blue (rgb) triplet If rgba tuple is given the 'a' value is not altered. Parameters ---------- rgb : tuple Triplet of rgb values. Note can be an rgba value. factor : float Between 0 and 1. Factor by which to tint. Default factor=0. scaled : bool If True then assumes RGB values aare scaled between 0, 1. If False rgb values are between 0 and 255. Default scaled=True. Returns ------- rgb_new : tuple New tuple of rgb ( or rgba). See Also -------- rgb_shade : Darken an rgb triplet. Examples -------- >>> x=rgb_tint((0.4, 0.8, 0.2), factor=0.5) >>> '{:.2f}, {:.2f}, {:.2f}'.format(*x) '0.70, 0.90, 0.60' >>> x=rgb_tint((155, 205, 55), factor=0.5, scaled=False) >>> '{:.2f}, {:.2f}, {:.2f}'.format(*x) '205.00, 230.00, 155.00' """ #calc from http://stackoverflow.com/a/6615053/2530083 if factor<0 or factor>1: raise ValueError('factor must be between 0 and 1. You have {:g}'.format(factor)) if scaled: black=1.0 else: black=255 rgb_new = [v + (black-v) * factor for v in rgb[:3]] if len(rgb)==4: rgb_new.append(rgb[3]) return tuple(rgb_new)
[docs]def copy_dict(source_dict, diffs): """Returns a copy of source_dict, updated with the new key-value pairs in diffs dict. Parameters ---------- source_dict : dict Source dictionary. diffs : dict Dictionary with which to update `source_dict`. Returns ------- out : dict Shallow copy of `source_dict` updated with `diffs` dict. References ---------- http://stackoverflow.com/a/5551706/2530083 Examples -------- >>> d = copy_dict({'a':7, 'b':12}, {'c':13}) >>> d['a']; d['b']; d['c'] 7 12 13 >>> d = copy_dict({'a':7, 'b':12}, {'a':21, 'c':13}) >>> d['a']; d['b']; d['c'] 21 12 13 """ #http://stackoverflow.com/a/5551706/2530083 result=dict(source_dict) # Shallow copy, see addendum below result.update(diffs) return result
[docs]class MarkersDashesColors(object): """Nice looking markers, dashed lines, and colors for matplotlib line plots Use this object to create a list of style dictionaries. Each style dict can be unpacked when passed to the matplotlib.plot command. Each dict contains the appropriate keywords to set the marker, dashes, and color properties. You can turn the list into a cycle using itertools.cycle To see the marker, dashes, and color options run the `demo_options' method. To choose a subset of styles see the `__call__` method. To view what the chosen styles actually look like run the `construct_styles' and the `demo_styles` method Parameters ---------- **kwargs : keyword arguments key value pairs to override the default_marker. e.g. color=(1.0,0,0) would change the default line and marker color to red. markersize=8 would change all marker sizes to 8. Most likely value to change are color, markersize, alpha. Defult values of the default_marker are: ================= =========================================== key value ================= =========================================== markersize 5 markeredgecolor `almost_black`, '#262626' markeredgewidth 1 markerfacecolor `almost_black`, '#262626' alpha 0.9 color `almost_black`, '#262626' ================= =========================================== Attributes ---------- almost_black : str hex string representing the default color, almost black '#262626'. color : matplotlib color Default color of markers and lines. Default color=`almost_black`. dashes : list of tuples List of on, off tuples for use with matplotlib dashes. See `construct_dashes` method. colors : list of tuples List of rgb tuples describing colors. See `construct_dashes`. markers : list of dict List of dict describing marker properties to pass to matplotlib.plot command. See `construct_markers` method. default_marker : dict Default properties for markers. These defaults will be overridden by the values in `markers`. Before initialization `default_marker` has the following keys. """ almost_black = '#262626' def __init__(self, **kwargs): """Initialization of MarkersDashesColors object""" self.color = kwargs.get('color', self.almost_black) self.default_marker={#'marker':'o', 'markersize':5, 'markeredgecolor': self.color, 'markeredgewidth':1, 'markerfacecolor': self.color, 'alpha':0.9, 'color': self.color } self.default_marker.update(kwargs) self.construct_dashes() self.construct_colors() self.construct_markers() self.merge_default_markers() self.styles=[] def __call__(self, markers=None, dashes=None, marker_colors=None, line_colors=None): """List of styles to unpack in matplotlib.plot for pleasing lines/markers If `markers`, `dashes`, `marker_colors`, and `line_colors` are all ``None`` then styles will be a cycle through combos of markers, colors, and lines such that each default marker, line, color appears at least once. Parameters ---------- markers : sequence List of ``int`` specifying index of self.markers. Default markers=None, i.e. no markers. dashes : sequence List of ``int`` specifying index of self.dashes. Default dashes=None i.e. no line. marker_colors : sequence List of ``int`` specifying index of self.colors to apply to each marker. Default marker_colors=None i.e. use self.color for all markers. line_colors : sequence List of ``int`` specifying index of self.colors. Default line_colors=None i.e. use self.color for lines. """ n = 0 try: n=max([len(v) for v in [markers, dashes, marker_colors, line_colors] if v is not None]) except ValueError: pass if n==0: markers = list(range(len(self.markers))) dashes = list(range(len(self.dashes))) marker_colors = list(range(len(self.colors))) line_colors = list(range(len(self.colors))) n = max([len(v) for v in [markers, dashes, marker_colors, line_colors] if v is not None]) if markers is None: # no markers markers=itertools.cycle([None]) else: markers=itertools.cycle(markers) if dashes is None: # no lines dashes=itertools.cycle([None]) else: dashes=itertools.cycle(dashes) if marker_colors is None: #default color marker_colors=itertools.cycle([None]) else: marker_colors = itertools.cycle(marker_colors) if line_colors is None: #defult color line_colors = itertools.cycle([None]) else: line_colors = itertools.cycle(line_colors) styles=[dict() for i in range(n)] for i in range(n): m = next(markers) mc = next(marker_colors) if m is None: styles[i]['marker'] = 'none' else: styles[i].update(self.markers[m]) if mc is None: # styles[i].update({'markeredgecolor': self.color, # 'markerfacecolor': self.color}) if self.markers[m]['markeredgecolor'] != 'none': styles[i]['markeredgecolor']= self.color if self.markers[m]['markerfacecolor'] != 'none': styles[i]['markerfacecolor'] = self.color else: if self.markers[m]['markeredgecolor'] != 'none': styles[i]['markeredgecolor']= self.colors[mc] if self.markers[m]['markerfacecolor'] != 'none': styles[i]['markerfacecolor'] = self.colors[mc] d = next(dashes) if d is None: styles[i]['linestyle'] = 'None' else: styles[i]['dashes'] = self.dashes[d] lc = next(line_colors) if lc is None: styles[i]['color'] = self.color else: styles[i]['color'] = self.colors[lc] return styles
[docs] def merge_default_markers(self): """Merge self.default_marker dict with each dict in self.markers""" self.markers=[copy_dict(self.default_marker, d) for d in self.markers] return
[docs] def construct_dashes(self): """List of on, off tuples for use with matplotlib dashes""" self.dashes=[(None, None), (10,3), (3,4,10,4), (2,2), (10,4,3,4,3,4), (10,10), ]#, (7,3,7,3,7,3,2,3),(20,20) ,(5,5)] return
[docs] def tint_colors(self, factor=1): "Lighten all colors by factor" self.colors = [rgb_tint(v, factor) for v in self.colors] return
[docs] def shade_colors(self, factor=1): "Darken all colors by factor" self.colors = [rgb_shade(v, factor) for v in self.colors] return
[docs] def construct_colors(self): """Populate self.colors: a list of rgb colors""" # These are from brewer2mpl.get_map('Set2', 'qualitative', 8).mpl_colors #see http://colorbrewer2.org/ self.colors = [(0.4, 0.7607843137254902, 0.6470588235294118), #turqoise (0.9882352941176471, 0.5529411764705883, 0.3843137254901961), #orange (0.5529411764705883, 0.6274509803921569, 0.796078431372549), #blue (0.9058823529411765, 0.5411764705882353, 0.7647058823529411), #pink (0.6509803921568628, 0.8470588235294118, 0.32941176470588235), #lime green # (1.0, 0.8509803921568627, 0.1843137254901961), # yellow (0.8980392156862745, 0.7686274509803922, 0.5803921568627451), #light brown (0.7019607843137254, 0.7019607843137254, 0.7019607843137254)] #grey self.shade_colors(0.8)
[docs] def construct_markers(self): """Populate self.markers: a list of dict that define a matplotlib marker run `self.merge_default_markers` after running `self.construct_markers` so that default properties are applied properly. """ #marker (numsides,style, angle) # theses markers cannot have fillstyle ngon=0 star=1 asterisk=2 circle=3 #fillstyle, 'full', 'left', 'right', 'bottom', 'top', 'none' #anything in self.makers self.markers=[ #mix of matplotlib, named latex, and STIX font unicode # http://www.stixfonts.org/allGlyphs.html {'marker': 'o'}, {'marker': 's', 'fillstyle': 'bottom'}, {'marker': 'D', 'markerfacecolor': 'none'}, {'marker': '^'}, {'marker': 'o', 'fillstyle': 'left'}, {'marker': 's', 'markerfacecolor': 'none'}, {'marker': 'D', 'fillstyle': 'top'}, {'marker': '^', 'fillstyle': 'left'}, {'marker': 'o', 'markerfacecolor': 'none'}, {'marker': 's'}, {'marker': 'D', 'fillstyle': 'left'}, {'marker': 'v', 'markerfacecolor': 'none'}, {'marker': r'$\boxplus$'}, {'marker': 'D'}, {'marker': 'v', 'fillstyle': 'bottom'}, {'marker': '^', 'markerfacecolor': 'none'}, {'marker': u'$\u25E9$'}, {'marker': u'$\u2b2d$'}, {'marker': 'h'}, {'marker': '^', 'fillstyle': 'bottom'}, {'marker': r'$\otimes$'}, {'marker': 'v'}, {'marker': 'h', 'fillstyle': 'right'}, {'marker': 'o', 'fillstyle': 'bottom'}, {'marker': 's', 'fillstyle': 'left'}, {'marker': 'h', 'markerfacecolor': 'none'}, {'marker': 'H', 'fillstyle': 'top'}, {'marker': (6, asterisk, 0)}, {'marker': u'$\u29bf$'}, {'marker': u'$\u29c7$'}, {'marker': u'$\u29fe$'}, {'marker': u'$\u27E1$'}, ] # #didn't make the cut # {'marker': 'H', # 'fillstyle': 'bottom'}, # {'marker': 'D', # 'markerfacecolor': 'none', # 'fillstyle': 'top'}, # {'marker': 'o', # 'markerfacecolor': 'none', # 'fillstyle': 'top'}, # {'marker': 's', # 'markerfacecolor': 'none', # 'fillstyle': 'top'}, # {'marker': 'h', # 'markerfacecolor': 'none', # 'fillstyle': 'top'}, # {'marker': '^', # 'markerfacecolor': 'none', # 'fillstyle': 'top'}, # # {'marker': '^', # 'markerfacecolor': 'none', # 'fillstyle': 'right'}, # {'marker': 's', # 'markerfacecolor': 'none', # 'fillstyle': 'right'}, # {'marker': 'o', # 'markerfacecolor': 'none', # 'fillstyle': 'right'}, # {'marker': 'h', # 'markerfacecolor': 'none', # 'fillstyle': 'right'}, # {'marker': 'D', # 'markerfacecolor': 'none', # 'fillstyle': 'right'}, # # #default matplotlib markers that didn't make the cut # {'marker': 0}, # {'marker': 1}, # {'marker': 2}, # {'marker': 3}, # {'marker': 4}, # {'marker': 6}, # {'marker': 7}, # {'marker': '|'}, # {'marker': ''}, # {'marker': 'None'}, # {'marker': None}, # {'marker': 'x'}, # {'marker': 5}, # {'marker': '_'}, # {'marker': ' '}, # {'marker': 'd'}, # {'marker': 'd', # 'fillstyle': 'bottom'}, # {'marker': 'd', # 'fillstyle': 'left'}, # {'marker': '+'}, # {'marker': '*'}, # {'marker': ','}, # {'marker': '.'}, # {'marker': '1'}, # {'marker': 'p'}, # {'marker': '3'}, # {'marker': '2'}, # {'marker': '4'}, # {'marker': 'H'}, # {'marker': 'v', # 'fillstyle': 'left'}, # {'marker': '8'}, # {'marker': '<'}, # {'marker': '>'}, # {'marker': (6, star, 0)}, # #named latex that didn't make the cut # {'marker': r'$\boxtimes$'}, # {'marker': r'$\boxdot$'}, # {'marker': r'$\oplus$'}, # {'marker': r'$\odot$'}, # {'marker': u'$\smile$'}, #STIX FONTS unicode that didn't make the cut # {'marker': u'$\u29b8$'}, # {'marker': u'$\u29bb$'}, # {'marker': u'$\u29be$'}, # {'marker': u'$\u29c4$'}, # {'marker': u'$\u29c5$'}, # {'marker': u'$\u29c6$'}, # {'marker': u'$\u29c8$'}, # {'marker': u'$\u29d0$'}, # {'marker': u'$\u29d1$'}, # {'marker': u'$\u29d2$'}, # {'marker': u'$\u29d3$'}, # {'marker': u'$\u29d6$'}, # {'marker': u'$\u29d7$'}, # {'marker': u'$\u29e8$'}, # {'marker': u'$\u29e9$'}, # {'marker': u'$\u2b12$'}, # {'marker': u'$\u2b13$'}, # {'marker': u'$\u2b14$'}, # {'marker': u'$\u2b15$'}, # {'marker': u'$\u2b16$'}, # {'marker': u'$\u2b17$'}, # {'marker': u'$\u2b18$'}, # {'marker': u'$\u2b19$'}, # {'marker': u'$\u2b1f$'}, # {'marker': u'$\u2b20$'}, # {'marker': u'$\u2b21$'}, # {'marker': u'$\u2b22$'}, # {'marker': u'$\u2b23$'}, # {'marker': u'$\u2b24$'}, # {'marker': u'$\u2b25$'}, # {'marker': u'$\u2b26$'}, # {'marker': u'$\u2b27$'}, # {'marker': u'$\u2b28$'}, # {'marker': u'$\u2b29$'}, # {'marker': u'$\u2b2a$'}, # {'marker': u'$\u2b2b$'}, # {'marker': u'$\u2b2c$'}, # {'marker': u'$\u2b2e$'}, # {'marker': u'$\u2b2f$'}, # {'marker': u'$\u272a$'}, # {'marker': u'$\u2736$'}, # {'marker': u'$\u273d$'}, # {'marker': u'$\u27c1$'}, # {'marker': u'$\u25A3$'}, # {'marker': u'$\u25C8$'}, # {'marker': u'$\u25D0$'},#strange straight ine on upper right # {'marker': u'$\u25D1$'},#strange straight ine on upper right # {'marker': u'$\u25D2$'},#strange straight ine on upper right # {'marker': u'$\u25D3$'},#strange straight ine on upper right # {'marker': u'$\u25E7$'}, # {'marker': u'$\u25E8$'}, # {'marker': u'$\u25EA$'}, # {'marker': u'$\u25EC$'}, # {'marker': u'$\u27D0$'}, # {'marker': u'$\u2A39$'}, # {'marker': u'$\u2A3B$'}, # {'marker': u'$\u22C7$'}, # {'marker': u'$\u$'}, #matplotlib markers #marker description #"." point #"," pixel #"o" circle #"v" triangle_down #"^" triangle_up #"<" triangle_left #">" triangle_right #"1" tri_down #"2" tri_up #"3" tri_left #"4" tri_right #"8" octagon #"s" square #"p" pentagon #"*" star #"h" hexagon1 #"H" hexagon2 #"+" plus #"x" x #"D" diamond #"d" thin_diamond #"|" vline #"_" hline #TICKLEFT tickleft #TICKRIGHT tickright #TICKUP tickup #TICKDOWN tickdown #CARETLEFT caretleft #CARETRIGHT caretright #CARETUP caretup #CARETDOWN caretdown #"None" nothing #None nothing #" " nothing #"" nothing return
[docs] def construct_styles(self, markers=None, dashes=None, marker_colors=None, line_colors=None): """Calls self.__call__ and assigns results to self.styles""" self.styles = self(markers, dashes, marker_colors, line_colors) return
[docs] def demo_styles(self): """Show a figure of all the styles in self.styles""" fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(111, frame_on=True) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) if len(self.styles)==0: ax.set_title("No styles set. Use the 'construct_styles' method.") else: for i, style in enumerate(self.styles): x = (i % 5)*2 y = i//5 ax.plot(np.array([x,x+1]), np.array([y,y+0.3]), **style) s=i ax.annotate(s, xy=(x+1,y+0.3), xytext=(18, 0), textcoords='offset points', horizontalalignment='left', verticalalignment='center') ax.set_xlim(-1, 11) ax.set_ylim(-1, y+1) ax.set_xlabel('styles') plt.show() return
[docs] def demo_options(self): """Show a figure with all the marker, dashes, color options available""" #markers gs = gridspec.GridSpec(3,1) fig=plt.figure(num=1,figsize=(12,10)) ax1=fig.add_subplot(gs[0]) for i, m in enumerate(self.markers): x = (i % 5)*2 y = i//5 ax1.plot(np.array([x,x+1]), np.array([y,y+0.3]), **m) s=repr(m['marker']).replace('$','').replace("u'\\u","") s=i ax1.annotate(s, xy=(x+1,y+0.3), xytext=(18, 0), textcoords='offset points', horizontalalignment='left', verticalalignment='center') ax1.set_xlim(-1, 11) ax1.set_ylim(-1, y+1) ax1.set_xlabel('Markers') ax1.get_xaxis().set_ticks([]) ax1.get_yaxis().set_ticks([]) ax1.set_title('Options for MarkersDashesColors object') #dashes ax2=fig.add_subplot(gs[1]) for i, d in enumerate(self.dashes): x = 0 y = i ax2.plot(np.array([x,x+1]), np.array([y,y+0.3]), dashes=d,color=self.almost_black) s=i ax2.annotate(s, xy=(x+1,y+0.3), xytext=(18, 0), textcoords='offset points', horizontalalignment='left', verticalalignment='center', color = self.almost_black ) ax2.set_xlim(-0.1,1.2) ax2.set_ylim(-1,y+1) ax2.set_xlabel('Dashes') ax2.get_xaxis().set_ticks([]) ax2.get_yaxis().set_ticks([]) #colors ax3 = fig.add_subplot(gs[2]) for i, c in enumerate(self.colors): x = 0 y = i ax3.plot(np.array([x,x+1]), np.array([y,y+0.3]), color=c,ls='-') s=i ax3.annotate(s, xy=(x+1,y+0.3), xytext=(18, 0), textcoords='offset points', horizontalalignment='left', verticalalignment='center', color=self.almost_black ) ax3.set_xlim(-0.1,1.2) ax3.set_ylim(-1,y+1) ax3.set_xlabel('colors') ax3.get_xaxis().set_ticks([]) ax3.get_yaxis().set_ticks([]) plt.show() return
[docs]def pleasing_defaults(): """Alter some matplotlib rcparams defaults to be visually more appealing References ---------- Many of the ideas/defaults/code come from Olga Botvinnik [1]_, and her `prettyplot` package [2]_. .. [1] http://blog.olgabotvinnik.com/post/58941062205/prettyplotlib-painlessly-create-beautiful-matplotlib .. [2] http://olgabot.github.io/prettyplotlib/ """ # Get Set2 from ColorBrewer, a set of colors deemed colorblind-safe and # pleasant to look at by Drs. Cynthia Brewer and Mark Harrower of Pennsylvania # State University. These colors look lovely together, and are less # saturated than those colors in Set1. For more on ColorBrewer, see: # - Flash-based interactive map: # http://colorbrewer2.org/ # - A quick visual reference to every ColorBrewer scale: # http://bl.ocks.org/mbostock/5577023 set2 = brewer2mpl.get_map('Set2', 'qualitative', 8).mpl_colors # Another ColorBrewer scale. This one has nice "traditional" colors like # reds and blues set1 = brewer2mpl.get_map('Set1', 'qualitative', 9).mpl_colors mpl.rcParams['axes.color_cycle'] = set2 # Set some commonly used colors almost_black = '#262626' light_grey = np.array([float(248) / float(255)] * 3) reds = mpl.cm.Reds reds.set_bad('white') reds.set_under('white') blues_r = mpl.cm.Blues_r blues_r.set_bad('white') blues_r.set_under('white') # Need to 'reverse' red to blue so that blue=cold=small numbers, # and red=hot=large numbers with '_r' suffix blue_red = brewer2mpl.get_map('RdBu', 'Diverging', 11, reverse=True).mpl_colormap # Default "patches" like scatterplots mpl.rcParams['patch.linewidth'] = 0.75 # edge width in points # Default empty circle with a colored outline mpl.rcParams['patch.facecolor'] = 'none' mpl.rcParams['patch.edgecolor'] = set2[0] # Change the default axis colors from black to a slightly lighter black, # and a little thinner (0.5 instead of 1) mpl.rcParams['axes.edgecolor'] = almost_black mpl.rcParams['axes.labelcolor'] = almost_black mpl.rcParams['axes.linewidth'] = 0.5 # Make the default grid be white so it "removes" lines rather than adds mpl.rcParams['grid.color'] = 'white' # change the tick colors also to the almost black mpl.rcParams['ytick.color'] = almost_black mpl.rcParams['xtick.color'] = almost_black # change the text colors also to the almost black mpl.rcParams['text.color'] = almost_black
[docs]def iterable_method_call(iterable, method, unpack, *args): """Call a method on each element of an iterable iterable[i].method(arg) Parameters ---------- iterable : sequence etc. Iterable whos members will have thier attribute changed. method : string Method to call. unpack : bool If True then each member of `*args` will be unpacked before passing to method. *args : positional arguments If a single positional argument then all members of `iterable` will have their method called with the same single argument. If more that one positional argument then iterable[0].method will be called with args[0], iterable[1].method will be set to args[1] etc. Skip elements by having corresponding value of *args=None. """ if len(args)==0: for i in iterable: getattr(i, method)() return if len(args)==1: if not args[0] is None: if unpack: for i in iterable: getattr(i, method)(*args[0]) else: for i in iterable: getattr(i, method)(args[0]) return if len(args)>1: for i, a in enumerate(args): if unpack: if not a is None: getattr(iterable[i],method)(*a) else: if not a is None: getattr(iterable[i],method)(a) return
[docs]def xylabel_subplots(fig, y_axis_labels=None, x_axis_labels=None): """Set x-axis label and y-axis label for each sub plot in figure Note: labels are applied to axes in the order they were created, which is not always the way they appear in the figure. Parameters ---------- fig : matplotlib.Figure Figure to apply labels to. y_axis_labels : sequence Label to place on y-axis of each subplot. Use None to skip a subplot. Default y_axis_label=None x_axis_labels : sequence Label to place on x-axis of each subplot. Use None to skip a subplot. Default x_axis_label=None. """ if not y_axis_labels is None: for i, label in enumerate(y_axis_labels): if not label is None: fig.axes[i].set_ylabel(label) if not x_axis_labels is None: for i, label in enumerate(x_axis_labels): if not label is None: fig.axes[i].set_xlabel(label) return
[docs]def row_major_order_reverse_map(shape, index_steps=None, transpose=False): """Map an index to a position in a row-major ordered array by reversing dims :: e.g. shape=(3,3) |2 1 0| |0 1 2| |5 4 3| --> |3 4 5| |8 7 6| |6 7 8| need 0-->2, 1-->1, 2-->0. i.e. [2 1 0 5 4 3 8 7 6]. Use row_major_order_reverse_map((3,3), (1,-1)) Use this to essentially change the subplot ordering in a matplotlib figure. Say I wanted sub_plot ordering as in the left arrangement of the above example. use mymap = row_major_order_reverse_map((3,3), (1,-1)). If I wanted to plot in my position 5 I would pass mymap[5] to the figure. Parameters ---------- shape : tuple Shape of array, e.g. (rows, columns) index_steps : list of 1 or -1, optional Traverse each array dimension in steps f `index_steps`. Default index_steps=None i.e. all dims traversed in normal order. e.g. for 3 d array, index_steps=(1,-1, 1) would mean 2nd dimension would be reversed. transpose : True/False, optional When True, transposes indexes (final operation). Default transpose=False. Returns ------- pos : 1d ndarray Array that maps index to position in row-major ordered array Notes ----- A use for this is positioning subplots in a matplotlib gridspec Examples -------- >>> row_major_order_reverse_map(shape=(3, 3), index_steps=None, transpose=False) array([0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> row_major_order_reverse_map(shape=(3, 3), index_steps=(-1, 1), transpose=False) array([6, 7, 8, 3, 4, 5, 0, 1, 2]) >>> row_major_order_reverse_map(shape=(3, 3), index_steps=(1, -1), transpose=False) array([2, 1, 0, 5, 4, 3, 8, 7, 6]) >>> row_major_order_reverse_map(shape=(3, 3), index_steps=(-1, -1), transpose=False) array([8, 7, 6, 5, 4, 3, 2, 1, 0]) >>> row_major_order_reverse_map(shape=(3, 3), index_steps=None, transpose=True) array([0, 3, 6, 1, 4, 7, 2, 5, 8]) """ shape=np.asarray(shape) if index_steps is None: index_steps=np.ones_like(shape,dtype=int) pos=np.arange(np.product(shape)).reshape(shape) a=[slice(None,None,i) for i in index_steps] pos[...]=pos[a] if transpose: return pos.T.flatten() else: return pos.flatten() return
[docs]def split_sequence_into_dict_and_nondicts(*args): """Separate dict and non-dict items. Merge dicts and merge non-dicts Elements are combined in the order that they appear. i.e. non-dict items will be appended to a combined list as they are encounterd. Repeated dict keys will be overridded by the latest value. Parameters ---------- *args : one or more positional items Mixture of dict and non-dict. Returns ------- merged_non_dict : list List of non dictionary items. merged_dict : dict Merged dictionary. """ merged_non_dict=[] merged_dict=dict() for v in args: if isinstance(v, dict): merged_dict.update(v) #http://stackoverflow.com/a/39437/2530083 else: merged_non_dict.append(v) return merged_non_dict, merged_dict
[docs]def plot_data_in_grid(fig, data, gs, gs_index=None, sharex=None, sharey=None): """Make a subplot for each set of data Parameters ---------- fig : matplotlib.Figure Figure to create subplots in. data : sequence of sequence of 2 element sequence data[i] = Data for the ith subplot. data[i][j] = jth (x, y) data set for the ith subplot. Each set of (x, y) data will be plotted using matplotlib.plot fn e.g. data=[([x0, y0],), ([x1, y1],), ([x2a, y2a], [x2b, x2b])] Note that data[i][j] will be split into list of all the non-dict items and a merged dict of all the dict items. Both the list and the merged dict will be unpacked and passed to the `plot_type` function. This allows passing of keyword arguments. If one of the dict keys is 'plot_type' it's value should be a string indicating a method of matplotlib.Axes that can be used to create the subplot. If 'plot_type' is not found then the default matplotlib.Axes.plot will be used. Be careful when using () to group data if there is only one item in the (item) then they are just parentheses and you are just saying 'item'; put a comma after the item to make it a tuple which is usually what you want when specifying data in this function. gs : matplotlib.gridspec.GridSpec instance Defines the grid in which subplots will be created. gs_index : List of int or list of slice, optional Specifies the position within gs that each data set will be plotted Positions can be specified by 1) an integer which will correspond to row-major ordering in the grid (e.g. for a 3x3 grid, index 3 will be second row, first column), or 2) a tuple of (row,column), or 3) a slice (e.g. index of np.s_[:1,:1] will span from first row, first column to second row, second column). Another slice method is slice(3,7) which will span from position 3 to position 7. Default gs_index=None subplots are added in row-major ordering. sharex : sequence of int Subplot index to share x-axis with. Default sharex=None i.e. no sharing. To skip a subplot put None as the corresponding element of sharex. If only one value is given and ther is more than one data set then all subplots will share the given axis. Note that the axis to share must already have been created. sharey : sequence of int Subplot index to share y-axis with. Default sharey=None i.e. no sharing. To skip a subplot put None as the corresponding element of sharey. If only one value is given and ther is more than one data set then all subplots will share the given axis. Note that the axis to share must already have been created. Returns ------- ax : List of :class:`matplotlib.pyplot.Axes` instances. Don't be confused with normal meaning of 'ax', this `ax` is a list of Axes, not just one. Notes ----- You may be wondering how to apply axes labels and such. Do something like [a.set_xlabel(v) for a, v in zip(fig.get_axes(), [xlabel1, xlabel2, ...])] """ # gridspec_prop: dict # dictionary of keyword arguments to pass to matplotlib.gridspec.GridSpec # object. Any attribute will correspond to the convential positioning, # i.e. gs_index will be ignored. Default=dict(). # e.g. gridspec_prop=dict(width_ratios=[1,2], # height_ratios=[4,1], left=0.55, right=0.98, hspace=0.05, wspace=0.02) # plot_type: sequence of str, optional # list of matplotlib.pyplot methods to use for each data set. # default=None which uses 'plot' # i.e. x-y plot. e.g. plot_type='scatter' gives a scatter plot. if gs_index is None: gs_index = np.arange(len(data)) # if plot_type is None: # plot_type = ['plot' for i in data] # elif len(plot_type) == 1: # plot_type = [plot_type[0] for i in data] if sharex is None: sharex = [None for i in data] elif len(sharex) == 1: i = sharex[0] sharex = [i for j in data] sharex[i] = None if sharey is None: sharey = [None for i in data] elif len(sharey) == 1: i = sharey[0] sharey = [i for j in data] sharey[i] = None ax = [] for i, subplot_data in enumerate(data): #subplot_data is all [x,y,dict] for each subplot j = gs_index[i] if sharex[i] is None: shx=None else: shx=ax[sharex[i]] if sharey[i] is None: shy=None else: shy=ax[sharey[i]] ax.append(fig.add_subplot(gs[j])) for j, xy_etc in enumerate(subplot_data): #xy_etc is a single [x,y,dict] to send to plt.plot, or plt.plot_type args_, kwargs_ = split_sequence_into_dict_and_nondicts(*xy_etc) plot_type = kwargs_.pop('plot_type', 'plot') getattr(ax[-1], plot_type)(*args_,**kwargs_) #http://stackoverflow.com/a/3071/2530083 # if suplot_data is None: # ax[-1].plot() ## getattr(ax[-1], plot_type[i])() ## ax[-1].axis('off')#removes axes instance # ax[-1].set_axis_bgcolor('none') # ax[-1].set_frame_on(False) # ax[-1].get_xaxis().set_ticks([])#http://stackoverflow.com/a/2176591/2530083 # ax[-1].get_yaxis().set_ticks([]) # else: # ## ax[-1].set_ylabel(i) #use for debugging return ax
[docs]def apply_dict_to_object(obj, dic): """Apply a dict of properties to a matplotlib object. Note the object must support set_<property_name> methods If obj and d are lists then the each `dic` will be applied to the corresponding `obj`. Parameters ---------- obj : matplotlib object or list of Object to set properties in. Typically a matplotlib.lines.Line2D instance. dic : dict or list of dict A dictionary or properties to apply to the object. e.g for a matplotlib.lines.Line2D dict keys might be 'marker', or 'linestyle' etc. i.e any kwarg you would pass to plt.plot. Note that if a key in dic does not correspond to a obj.set_key method then it will be ignored. """ if not isinstance(obj, list): obj = [obj] if not isinstance(dic, list): dic = [dic] if len(obj)!=len(dic): raise ValueError('obj and d must be the same lenght. {} vs ' '{}'.format(len(obj), len(dic))) for o, d in zip(obj, dic): if d is None: continue for key, value in d.items(): s = 'set_{0}'.format(key) if hasattr(o, s): getattr(o, s)(value) return
[docs]def plot_generic_loads(load_triples, load_names, ylabels=None, trange = None, H = 1.0, RLzero=None, prop_dict={}): """Plot loads that come in load_vs_time-load_vs_depth-omega_phase form For each load_triple (i.e. each load type) two plots will be made side by side: a load_vs_time plot; and a load_vs_depth plot. the different load types will appear one under the other. :: Load Magnitude Depth ^ ^ | ..... | * | . | * | . . . . | * | . | * | . | * --------------------->Time --------->Load factor Parameters ---------- load_triples : list of list of 3 element tuples (load_vs_time, load_vs_depth, load_omega_phase) PolyLines. load_triples[i] will be a list of load triples for the ith plot load_triples[i][j] will be the jth load triple for the ith plot. The load_vs_depth can also be a two element tuple containing a list/array of depth values and a list/array of load values. The load_vs_time can also be a two element tuple containing a list/array of time values and a list/array of load values. load_names : list of string string to prepend to legend entries for each load ylabels : list of string, optional y labels for each of the axes, Default ylabels=None i.e. y0, y1, y2 etc. trange : 2 element tuple, optional (tmin, tmax) max and min times to plot loads for. Default trange=None i.e. t limits will be worked out from data. H : float, optional Height of soil profile. Default H=1.0. Used to transform normalised depth to actual depth. RLzero : float, optional Reduced level of the top of the soil layer. If RLzero is not None then all depths (in plots and results) will be transformed to an RL by RL = RLzero - z*H. If RLzero is None (i.e. the default) then all actual depths will be reported z*H (i.e. positive numbers). prop_dict : dict of dict, optional Dictionary containing certain properties used to set various plot options. ================== ============================================ prop_dict option description ================== ============================================ fig_prop dict of prop to pass to plt.figure. Defaults include: figsize=(7.05, 1.57 * no.of.loads) styles List of dict. Each dict is for one line. Each dict contains kwargs for plt.plot. SeeMarkersDashesColors. Defaults give black and white markersize 5. time_axis_label Label for x axis in load_vs_time plots. Default='Time' depth_axis_label Label for y axis in load_vs_depth plot Default="Depth, z" or "RL" depending on RLzero. has_legend True or False. Default = True. legend_prop dict of prop to pass to ax.legend. Defaults include: title='Load' fontsize=9 ================== ============================================ Returns ------- fig : matplolib.Figure Figure with plots. """ fig_prop = copy_dict({'figsize':(18/2.54, (18/1.61/2.54)/ 2.8 *len(load_triples))}, prop_dict.get('fig_prop', {})) # fig_prop = prop_dict.get('fig_prop', {'figsize':(18/2.54, (18/1.61/2.54)/ 2.8 *len(load_triples)) }) legend_prop = copy_dict({'title': 'Load:', 'fontsize': 9}, prop_dict.get('legend_prop',{})) # legend_prop = prop_dict.get('legend_prop', # {'title': 'Load:', 'fontsize': 9}) styles = prop_dict.get('styles', None) if styles is None: mcd = MarkersDashesColors( #color = 'black', markersize= 7) mcd.construct_styles(markers = list(range(32)), dashes=[0], marker_colors=None, line_colors=None) styles = itertools.cycle(mcd.styles) else: styles = itertools.cycle(styles) has_legend = prop_dict.get('has_legend', True) xlabel1 = prop_dict.get('time_axis_label', 'Time') xlabel2 = prop_dict.get('depth_axis_label', 'Load factor') n = len(load_triples) gs = mpl.gridspec.GridSpec(n,2, width_ratios=[5,1]) fig = plt.figure(**fig_prop) #plt.subplot(gs[0]) if ylabels is None: ylabels = ['y{:d}'.format(v) for v in range(n)] #determine tmax, tmin if trange is None: for i, (triples, name, ylabel) in enumerate(zip(load_triples, load_names, ylabels)): for j, (vs_time, vs_depth, omega_phase) in enumerate(triples): if i==0 and j==0: tmin = np.min(vs_time.x) tmax = np.max(vs_time.x) else: tmin = min(tmin, np.min(vs_time.x)) tmax = max(tmax, np.max(vs_time.x)) else: tmin, tmax = trange ax1 = [] ax2 = [] for i, (triples, name, ylabel) in enumerate(zip(load_triples, load_names, ylabels)): sharex1 = None sharex2 = None sharey1 = None sharey2 = None if i != 0: sharex1 = ax1[0] sharex2 = ax2[0] sharey1 = ax1[0] sharey2 = ax2[0] ax1.append(plt.subplot(gs[i, 0], sharex=sharex1, sharey=sharey1)) ax2.append(plt.subplot(gs[i, 1], sharex=sharex2, sharey=sharey2 )) for j, (vs_time, vs_depth, omega_phase) in enumerate(triples): style = next(styles) if vs_time is None: #allow for fixed ppress vs_time = PolyLine([tmin, tmax], [0.0, 0.0]) if not isinstance(vs_time, PolyLine): x_, y_ =vs_time vs_time = PolyLine(x_,y_) dx = (tmax-tmin)/20.0 if not omega_phase is None: omega, phase = omega_phase dx = min(dx, 1/(omega/(2*np.pi))/40) # print(dx, omega) # x = [np.linspace(x1, x2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_time.x[:-1], vs_time.x[1:], vs_time.y[:-1], vs_time.y[1:])] # #if abs(y2-y1) > 1e-5 and abs(x2-x1) > 1e-5] # # y = [np.linspace(y1, y2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_time.x[:-1], vs_time.x[1:], vs_time.y[:-1], vs_time.y[1:])] # #if abs(y2-y1) > 1e-5 and abs(x2-x1) > 1e-5] # # x = np.array([val for subl in x for val in subl]) # y = np.array([val for subl in y for val in subl]) x, y = pwise.subdivide_x_y_into_segments(x=vs_time.x, y=vs_time.y, dx=dx, min_segments=4) if not omega_phase is None: y *= np.cos(omega * x + phase) linename = name + str(j) # ax1[-1].plot(x, y, label=linename, markevery=markevery, **style) ax1[-1].plot(x, y, label=linename, **style) apply_markevery_to_sequence_of_lines(ax1[-1].get_lines(), markevery=0.1, random_start=True, seed=1) # try: # random.seed(1) # [apply_dict_to_object(line, d) # for line, d in # zip(ax1[-1].get_lines(), # [{'markevery': (random.random()* 0.1, 0.1)} for v in ax1[-1].get_lines()])] # except ValueError: # pass #TODO: add some more points in the z direction, account for when only one point if isinstance(vs_depth, PolyLine): dx = (np.max(vs_depth.x)-np.min(vs_depth.x))/8 # x = [np.linspace(x1, x2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_depth.x[:-1], vs_depth.x[1:], vs_depth.y[:-1], vs_depth.y[1:])] # #if abs(y2-y1) > 1e-5 and abs(x2-x1) > 1e-5] # # y = [np.linspace(y1, y2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_depth.x[:-1], vs_depth.x[1:], vs_depth.y[:-1], vs_depth.y[1:])] # # x = np.array([val for subl in x for val in subl]) # y = np.array([val for subl in y for val in subl]) x, y = pwise.subdivide_x_y_into_segments(x=vs_depth.x, y=vs_depth.y, dx=dx, min_segments=4) else: # assume a tuple of x and y values x, y = vs_depth x = np.atleast_1d(x) y = np.atleast_1d(y) z = transformations.depth_to_reduced_level(x, H, RLzero) ax2[-1].plot(y, z, label=linename, **style) #load_vs_time plot stuff if i==len(load_triples)-1: ax1[-1].set_xlabel(xlabel1) ax1[-1].set_ylabel(ylabel) if not trange is None: ax1[-1].set_xlim((tmin, tmax)) if has_legend: leg = ax1[-1].legend(**legend_prop) leg.draggable(True) plt.setp(leg.get_title(),fontsize=legend_prop['fontsize']) #load_vs_depth plot stuff if i==len(load_triples)-1: ax2[-1].set_xlabel(xlabel2) if RLzero is None: ax2[-1].invert_yaxis() ylabel = prop_dict.get('depth_axis_label', 'Depth, z') else: ylabel = prop_dict.get('depth_axis_label', 'RL') ax2[-1].set_ylabel(ylabel) ax2[-1].set_xlim((0,1.01)) ax2[-1].set_xticks([0,0.5,1]) fig.tight_layout() return fig
[docs]def plot_vs_time(t, y, line_labels=None, prop_dict={}): """Plot y vs t with some options Originally used for plotting things like average excess pore pressure vs time. :: y ^ | ....... | . | . . . . *** | . * * * | .* ***** --------------------->Time Parameters ---------- t : np.array Time values. y : one or two dimensional ndarray y values to plot. basically plt.plot(t,y) will be used. line_labels : list of string, optional Label for each line in y. Defaultline_labels=None, i.e. no labels. prop_dict : dict of dict, optional Dictionary containing certain properties used to set various plot options. ================== ============================================ prop_dict option description ================== ============================================ fig_prop dict of prop to pass to plt.figure. Defaults include: figsize=(7.05, 4.4) styles List of dict. Each dict is for one line. Each dict contains kwargs for plt.plot See MarkersDashesColors defaults give black and white markersize 5 xlabel x-axis label. default='Time'. ylabel y-axis label. default = 'y'. has_legend True or False. default = True. legend_prop dict of prop to pass to ax.legend Defaults include: title='Depth interval' fontsize=9 ================== ============================================ Returns ------- fig : matplolib.Figure Figure with plots. """ fig_prop = copy_dict({'figsize':(18/2.54, 18/1.61/2.54)}, prop_dict.get('fig_prop', {})) # fig_prop = prop_dict.get('fig_prop', {'figsize':(18/2.54, 18/1.61/2.54)}) legend_prop = copy_dict({'title': 'Depth interval:', 'fontsize': 9}, prop_dict.get('legend_prop',{})) # legend_prop = prop_dict.get('legend_prop', # {'title': 'Depth interval:', 'fontsize': 9}) styles = prop_dict.get('styles', None) if styles is None: mcd = MarkersDashesColors( #color = 'black', markersize= 7) mcd.construct_styles(markers = list(range(32)), dashes=[0], marker_colors=None, line_colors=None) styles = itertools.cycle(mcd.styles) else: styles = itertools.cycle(styles) #z = transformations.depth_to_reduced_level(z, H, RLzero) #t = self.tvals[self.ppress_z_tval_indexes] fig = plt.figure(**fig_prop) plt.plot(t, y) xlabel = prop_dict.get('xlabel', 'Time, t') plt.xlabel(xlabel) ylabel = prop_dict.get('ylabel', 'y') plt.ylabel(ylabel) #apply style to each line [apply_dict_to_object(line, d) for line, d in zip(fig.gca().get_lines(), styles)] #apply markevery to each line apply_markevery_to_sequence_of_lines(fig.gca().get_lines(), markevery=0.1, random_start=True, seed=1) # try: # random.seed(1) # [apply_dict_to_object(line, d) # for line, d in # zip(fig.gca().get_lines(), # [{'markevery': (random.random()* 0.1, 0.1)} for v in y])] # except ValueError: # pass if (not line_labels is None): labels = [{'label': v} for v in line_labels] [apply_dict_to_object(line, d) for line, d in zip(fig.gca().get_lines(), labels)] has_legend = prop_dict.get('has_legend', True) else: has_legend=False if has_legend: leg = fig.gca().legend(**legend_prop) leg.draggable(True) plt.setp(leg.get_title(),fontsize=legend_prop['fontsize']) return fig
[docs]def plot_single_material_vs_depth(z_x, xlabels, H = 1.0, RLzero=None, prop_dict={}): """Plot side by side property vs depth graphs :: x1 x2 x3 -----------> -----------> ------------> | . | . | . | | . | . | . | | . | . | . | | . | . | . | | . | . | . | | . | . | . | v v v depth Parameters ---------- z_x : list of PolyLine List of value_vs_depth PolyLines. xlabels : list of string List of x-axis labels. H : float, optional Height of soil profile. Default H=1.0. Used to transform normalised depth to actual depth. RLzero : float, optional Reduced level of the top of the soil layer. If RLzero is not None then all depths (in plots and results) will be transformed to an RL by RL = RLzero - z*H. If RLzero is None (i.e. the default) then all depths will be reported z*H (i.e. positive numbers). prop_dict : dict of dict, optional Dictionary containing certain properties used to set various plot options: ================== ============================================ prop_dict option description ================== ============================================ fig_prop dict of prop to pass to plt.figure. defaults include: figsize=(7.05, 4.4) styles List of dict. Each dict is for one line. Each dict contains kwargs for plt.plot See MarkersDashesColors defaults give black and white markersize 5 xlabel x-axis label. default = 'Time' ylabel y-axis label. default = 'Depth, z' or 'RL' depending on RLzero. ================== ============================================ """ n = len(z_x) fig_prop = copy_dict({'figsize':(2 * n, 18/1.61/2.54)}, prop_dict.get('fig_prop', {})) # fig_prop = prop_dict.get('fig_prop', {'figsize':(2 * n, 18/1.61/2.54)}) styles = prop_dict.get('styles', None) if styles is None: mcd = MarkersDashesColors( #color = 'black', markersize= 7) mcd.construct_styles(markers = list(range(32)), dashes=[0], marker_colors=None, line_colors=None) styles = itertools.cycle(mcd.styles) else: styles = itertools.cycle(styles) gs = mpl.gridspec.GridSpec(1,n, width_ratios=None, wspace=0.13) gs.update(left=0.1, right=0.98, bottom=0.05, top=0.9) fig = plt.figure(**fig_prop) ax1=[] style = next(styles) for i, (vs_depth, xlabel) in enumerate(zip(z_x, xlabels)): sharey1 = None if i != 0: #share the y axis sharex1 = ax1[0] sharey1 = ax1[0] ax1.append(plt.subplot(gs[i], sharey=sharey1)) if not isinstance(vs_depth, PolyLine): # assume a tuple of x and y values x_, y_ =vs_depth vs_depth = PolyLine(x_,y_) dx = (np.max(vs_depth.x)-np.min(vs_depth.x)) / 8 # # x = [np.linspace(x1, x2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_depth.x[:-1], vs_depth.x[1:], vs_depth.y[:-1], vs_depth.y[1:])] # #if abs(y2-y1) > 1e-5 and abs(x2-x1) > 1e-5] # # y = [np.linspace(y1, y2, max(int((x2-x1)//dx), 4)) for # (x1, x2, y1, y2) in zip(vs_depth.x[:-1], vs_depth.x[1:], vs_depth.y[:-1], vs_depth.y[1:])] # # x = np.array([val for subl in x for val in subl]) # y = np.array([val for subl in y for val in subl]) x, y = pwise.subdivide_x_y_into_segments(x=vs_depth.x, y=vs_depth.y, dx=dx, min_segments=4) z = transformations.depth_to_reduced_level(x, H, RLzero) ax1[-1].plot(y, z, **style) ax1[-1].set_xlabel(xlabel, multialignment='center') ax1[-1].xaxis.set_label_position('top') ax1[-1].xaxis.tick_top() ax1[-1].xaxis.set_major_locator( plt.MaxNLocator(4)) ax1[-1].xaxis.set_minor_locator( plt.AutoLocator()) cur_xlim = ax1[-1].get_xlim() ax1[-1].set_xlim([0, cur_xlim[1]]) #ax1[-1].xaxis.set_minor_locator(ml) #ax1[-1].set_xticks(ax1[-1].get_xticks()[1:]) if i != 0: #ax1[-1].yaxis.set_ticklabels([]) plt.setp(ax1[-1].get_yticklabels(), visible=False) if RLzero is None: plt.gca().invert_yaxis() ylabel = prop_dict.get('ylabel', 'Depth, z') else: ylabel = prop_dict.get('ylabel', 'RL') ax1[0].set_ylabel(ylabel) #ax1[-1].yaxis.set_ticklabels([]) #fig.tight_layout() return fig
[docs]def apply_markevery_to_sequence_of_lines(lines, markevery=None , random_start=True, seed=None): """Apply `markevery` property to sequence of matplotlib lines Allows for a random start so that marker on different lines do not line up. Parameters ---------- lines : sequence of matplotlib lines Lines to apply markevery to. markevery : int or float, optional Value of markevery property. Default markevery=None, i.e. markevery will be turned off and all points will have markers. random_start : True/False If True then first marker shown will be random between start marker and the markevery property. Default random_start=True. seed : int, optional Random seed. Default seed=None which does not specify the random seed. """ import random if isinstance(markevery, int): if random_start: if not seed is None: random.seed(seed) [apply_dict_to_object(line, d) for line, d in zip(lines,[{'markevery': (random.randint(0, markevery), markevery)} for v in lines])] else: [apply_dict_to_object(line, d) for line, d in zip(lines,[{'markevery': markevery} for v in lines])] elif isinstance(markevery, float): try: from matplotlib.lines import _mark_every_path # the above import should fail for matplotlib <1.4. # However, since it was my addition to matplotlib I may have hardwired # all the markevery code it in to my own matplotlib.lines file. if random_start: if not seed is None: random.seed(seed) [apply_dict_to_object(line, d) for line, d in zip(lines,[{'markevery': (random.random()*markevery, markevery)} for v in lines])] else: [apply_dict_to_object(line, d) for line, d in zip(lines,[{'markevery': markevery} for v in lines])] except ImportError: warnings.warn('markevery=float not available, markevery ignored') else: #straight application of markevery [apply_dict_to_object(line, d) for line, d in zip(lines,[{'markevery': markevery} for v in lines])]
[docs]def plot_vs_depth(x, z, line_labels=None, H = 1.0, RLzero=None, prop_dict={}): """Plot z vs x for various t values Originally used for plotting things like excess pore pressure vs depth :: --------------------> value |.* | . * | . * | . * | . * | . * v depth Parameters ---------- x : one or two dimensional ndarray y values to plot. Basically plt.plot(t, y) will be used z : one d array of float Depth values. line_labels : list of string, optional Label for each line in y. Default line_labels=None, i.e. no line labels. H : float, optional Height of soil profile. Default H=1.0. Used to transform normalised depth to actual depth. RLzero : float, optional Reduced level of the top of the soil layer. If RLzero is not None then all depths (in plots and results) will be transformed to an RL by RL = RLzero - z*H. If RLzero is None (i.e. the default) then actual depths will be reported z*H (i.e. positive numbers). prop_dict : dict of dict, optional Dictionary containing certain properties used to set various plot options. ================== ============================================ prop_dict option description ================== ============================================ fig_prop dict of prop to pass to plt.figure. defaults include: figsize=(7.05, 4.4) styles List of dict. Each dict is for one line. Each dict contains kwargs for plt.plot See MarkersDashesColors defaults give black and white markersize 5 xlabel x-axis label. Default = 'x'. ylabel y-axis label. Default = 'Depth, z' or 'RL'. depending on RLzero. has_legend True or False. default is True legend_prop dict of prop to pass to ax.legend defaults include: title='Depth interval' fontsize=9 ================== ============================================ Returns ------- fig : matplolib.Figure Figure with plots. """ fig_prop = copy_dict({'figsize':(18/2.54, 18/1.61/2.54)}, prop_dict.get('fig_prop', {})) # fig_prop = prop_dict.get('fig_prop', {'figsize':(18/2.54, 18/1.61/2.54)}) legend_prop = copy_dict({'title': 'time:', 'fontsize': 9}, prop_dict.get('legend_prop', {})) # legend_prop = prop_dict.get('legend_prop', # {'title': 'time:', 'fontsize': 9}) styles = prop_dict.get('styles', None) if styles is None: mcd = MarkersDashesColors( #color = 'black', markersize= 7) mcd.construct_styles(markers = list(range(32)), dashes=[0], marker_colors=None, line_colors=None) styles = itertools.cycle(mcd.styles) else: styles = itertools.cycle(styles) z = transformations.depth_to_reduced_level(z, H, RLzero) fig = plt.figure(**fig_prop) plt.plot(x, z) xlabel = prop_dict.get('xlabel', 'x') plt.xlabel(xlabel) if RLzero is None: plt.gca().invert_yaxis() ylabel = prop_dict.get('ylabel', 'Depth, z') else: ylabel = prop_dict.get('ylabel', 'RL') plt.ylabel(ylabel) #apply style to each line [apply_dict_to_object(line, d) for line, d in zip(fig.gca().get_lines(), styles)] #apply markevery to each line apply_markevery_to_sequence_of_lines(fig.gca().get_lines(), markevery=0.1, random_start=True, seed=1) # try: # from matplotlib.lines import _mark_every_path # # the above import should fail for matplotlib <1.4. # # However, since it was my addition to matplotlib I may have hardwired # # all the markevery code it in to my own matplotlib.lines file. # random.seed(1) # [apply_dict_to_object(line, d) # for line, d in # zip(fig.gca().get_lines(), # [{'markevery': (random.random()* 0.1, 0.1)} for v in x])] # except ImportError: # pass #apply label to each line #line_labels = [{'label': '{:.3g}'.format(v)} for v in t] if (not line_labels is None): labels = [{'label': v} for v in line_labels] [apply_dict_to_object(line, d) for line, d in zip(fig.gca().get_lines(), labels)] has_legend = prop_dict.get('has_legend', True) else: has_legend=False # [apply_dict_to_object(line, d) # for line, d in zip(fig.gca().get_lines(), line_labels)] # # has_legend = prop_dict.get('has_legend', True) if has_legend: leg = fig.gca().legend(**legend_prop) leg.draggable(True) plt.setp(leg.get_title(),fontsize=legend_prop['fontsize']) return fig
[docs]def save_figure(fig, fname='fig', ext=['pdf', 'eps', 'png'], dpi=1200): """Save a figure to a file in multiple formats Figure will be saved in as fname.ext where ext is each of the extenstions in the ext parameter. Parameters ---------- fig : matplotlib.Figure object Figure to save fname : str, optional filepath to save to WITHOUT extension, default fname='fig'. ext : list of str, optional List of file extensions to save. Must be one of the matplolib save as file types. default ext=[['pdf','eps','png']. dpi : int, optional dpi setting to save png etc figures as. Default dpi=1200. """ for ex in ext: if ex in ['png']: d = {'dpi': dpi} d['dpi'] = dpi else: d={} fig.savefig('{}.{}'.format(fname, ex), format=ex, **d)
[docs]def figure_from_source_code(obj, figsize=None, font=None): """Put source code of object into a matplotlib figure Parameters --------- obj : python object object to display source code of figsize : tuple, optional figsize in inches, default = None i.e. use matplotlib default font : matplotlib .fontmanager optional font to be applied to the annotation object default=None Returns ------- fig : matplotlib.Figure figure of text """ #TODO: needs testing import inspect code = "".join(inspect.getsourcelines(obj)[0]) fig = plt.figure(figsize=figsize) plt.axis('off') ax = plt.gca() annot1 = ax.annotate(code, xy=(0.01, 0.99), xycoords='figure fraction', xytext=(0.01, 0.99), textcoords='figure fraction', va="top", ha="left", arrowprops=None) # font = matplotlib.font_manager.FontProperties(family='times new roman', style='italic', size=16) if not font is None: annot1.set_font_properties(font) return fig
if __name__ == '__main__': import nose nose.runmodule(argv=['nose', '--verbosity=3', '--with-doctest']) # nose.runmodule(argv=['nose', '--verbosity=3']) # a = MarkersDashesColors() # a.color=(0.5, 0, 0) # a.default_marker={ 'markersize':5, # 'markeredgecolor': a.color, # 'markeredgewidth':1, # 'markerfacecolor': a.color, # 'alpha':0.9, # 'color': a.color # } # # a.markers=[{'marker': 'o', 'markerfacecolor': 'none'}, # {'marker': 's'}, # {'marker': '^'},] # a.colors=[(0, 0.5, 0), (0, 0, 0.5)] # # a.dashes=[(None, None), [4, 4]] # # a.merge_default_markers() # # a.construct_styles() # a.demo_options() # import doctest # doctest.testmod() # a=MarkersDashesColors() # a.demo_options() # a.construct_styles() # a.construct_styles(markers=[0,5,6,2]) # a.construct_styles(markers=[0,5,6,2], dashes=[0,3]) # a.construct_styles(markers=[0,5,6,2], dashes=[0,3], marker_colors=[0,1], line_colors=[2,3]) # a.demo_styles() #plot_data_in_grid(None,[(1,2),(4,5)],[(3,5)]) #flat = [x for sublist in nested for x in sublist] #http://stackoverflow.com/a/2962856/2530083 # if 1: # pleasing_defaults() # fig=plt.figure() # x = np.linspace(-np.pi,np.pi,100) # d2 = np.linspace(0,1,4) # d3 = np.linspace(0,1,2) # label1= ['{0:.3g}'.format(i) for i in d2] # y1 = np.sin(x) # y2 = 1000*np.cos(x[:,np.newaxis]-d2) # y3 = 1e-3*np.sin(x[:,np.newaxis]-d3) # # # data= [[[x,y2]],[[2*x, y1, dict(plot_type='scatter')]], [[1.5*x,y3]]] # y_axis_labels=['0','1', '2'] # x_axis_labels=['Time', None, None] # # #gs = gridspec.GridSpec(shape, hspace=0.08, wspace=0.1) # shape=(3,3) # gs = gridspec.GridSpec(*shape) # transpose = True # index_steps=(1,-1) # sharey=None # sharex=[None,0,None] # # #plot_type=['plot','plot','scatter','plot'] # gs_index = row_major_order_reverse_map(shape=shape,index_steps=index_steps, transpose=transpose) # gs_index = [np.s_[:2,:2],2,8] # #gs_index=[1,2,3,0] # ax = plot_data_in_grid(fig, data=data, gs=gs, # gs_index=gs_index, # sharex=sharex, sharey=sharey) # # # styles = MarkersDashesColors()() # # # [apply_dict_to_object(line, d) for line,d in zip(ax[0].get_lines(), styles)] # # # # # # # #apply ylabels to each axis # [plt.setp(ax[i], ylabel=label) # for i, label in enumerate(y_axis_labels) if not label is None] # #apply xlabels to each axis # [plt.setp(ax[i], xlabel=label) # for i,label in enumerate(x_axis_labels) if not label is None] # # turn x tick labels on or off # [plt.setp(ax[i].get_xticklabels(), visible=value) # for i, value in enumerate([True,False,False]) if not value is None] # #gs.tight_layout(fig) # # fig.tight_layout() # ##Note: axes.flat # ## [fig.axes[i]._shared_x_axes.join(fig.axes[i], value) for ## i,value in enumerate(sharex) if not value is None] # # # [print(sorted(map(tuple, fig.axes[i]._shared_x_axes))) for # i,value in enumerate(sharex) if not value is None] # #print(sorted(map(tuple, fig.axes[i]._shared_x_axes))) # # #make the joins # [fig.axes[i]._shared_x_axes.join(fig.axes[i], value) for # i,value in enumerate(sharex) if not value is None] # # [print(sorted(map(tuple, fig.axes[i]._shared_x_axes))) for # i,value in enumerate(sharex) if not value is None] # #print(sorted(map(tuple, fig.axes[i]._shared_x_axes))) ## print([fig.axes[i] in fig.axes[i]._shared_x_axes for ## i,value in enumerate(sharex) if not value is None] ) ## # [fig.axes[i].apply_aspect() for # i,value in enumerate(sharex) if not value is None] # plt.Axes.get_shared_x_axes() # matplot # self._shared_x_axes.join(self, sharex) # print(plt.getp(fig.axes[i], 'sharex')) # [plt.setp(fig.axes[i], sharex=value) # for i, value in enumerate(sharex) if not value is None] # iterable_method_call(fig.axes, 'set_ylabel', *y_axis_labels) # iterable_method_call(fig.axes, 'set_xlabel', *x_axis_labels) # iterable_method_call(fig.axes, 'set_xlabel', *x_axis_labels) # xylabel_subplots(fig, y_axis_labels,x_axis_labels) # plt.show() # if 0: # x = np.linspace(-np.pi,np.pi,100) # d = np.linspace(0,1,4) # # label1= ['{0:.3g}'.format(i) for i in d] # y1 = np.sin(x) # y2 = np.cos(x[:,np.newaxis]+d) # y3 = np.sin(x[:,np.newaxis]+d) # # a= plot_common_x([[[x,y2]],[[x,y1]],[[x,y3]]], # # x_axis_label='x', y_axis_labels=['$\sigma$', 'load', None], legend_labels=[label1,['surcharge'],label1], # hspace=0.1, height_ratios=[2,1,1], # plot_type='plot', # kwargs_figure=dict(num=3, figsize=(10,10))) # # plt.show() # bbox_args = None#dict(boxstyle="round,pad=0.4", fc="yellow",alpha=0.3) # bbox_args2 = dict(boxstyle="round,pad=0.6", fc=None, alpha=0) # arrow_args = dict(arrowstyle="->") # fig=plt.figure() # ax = fig.add_subplot(111) # np.random.seed(2) # x=np.random.randn(100) # y=np.random.randn(100) # scatter = ax.scatter(x, y, label='h') # # legend=ax.legend() # legend.draggable(True) # # anp = ax.annotate('$\hspace{1}$', xy=(x[0], y[0]), xycoords='data', # xytext=None, textcoords=None, # ha="center", va="center", # bbox=bbox_args2, # ) # # ant = ax.annotate('Drag me 1', xy=(0.5, 0.5), xycoords=anp, # xytext=(15,0.5), textcoords=anp,#'offset points', # ha="left", va="center", # bbox=bbox_args, # arrowprops=dict( # #patchB=anp.get_bbox_patch(), # connectionstyle="arc3,rad=0.2", # **arrow_args) # ) # # # anp.draggable() # ant.draggable() # # # plt.show()