Source code for twod

from scipy.optimize import curve_fit
from scipy.odr import *
from math import exp
import matplotlib
import string
import os
from matplotlib.patches import Ellipse, Polygon, Circle
from matplotlib.lines import Line2D
from colour import Color
import numpy as np
import platform
import shutil
import time
from copy import copy
from IPython.display import SVG, display, Latex, HTML, display_latex
import subprocess
import sys
import random
import weakref
import re
import __builtins__ as bi
import lyxithea.lyxithea as lyx
from itertools import count
import psgv.psgv as psgv
import pickle
import os.path

__context__ = psgv.psgv('__context__')
__context__.val = 'writeup'
__figures__ = psgv.psgv('__lyxfigures__')
__figures__.val = {}
__force__ = psgv.psgv('__pygforce__')
__force__.val = False

def metal_dim(ratio='golden'):
	if ratio is "golden":
		r = (1. + np.sqrt(5.)) / 2.
	elif ratio is "silver":
		r = 1. + np.sqrt(2.)
	elif ratio is "bronze":
		r = (3. + np.sqrt(13.)) / 2.
	elif ratio is "invgolden":
		r = 2. / (1. + np.sqrt(5.))
	elif ratio is "invsilver":
		r = 1. / (1. + np.sqrt(2.))
	elif ratio is "invbronze":
		r = 2. / (3. + np.sqrt(13.))
	elif ratio is 'square':
		r = 1.0
	return r

def res(w=1080., ratio='golden'):
	h = w * metal_dim(ratio)
	return (h, w)

def context(ctx='writeup'):
	__context__.val = ctx

if "DISPLAY" not in os.environ.keys():
	import matplotlib
	matplotlib.use('Agg')
else:
	import matplotlib
	matplotlib.use('pgf')

import matplotlib.pyplot as plt

plt.close("all")
preamble = ['\usepackage{nicefrac}',
	'\usepackage{gensymb}',
	'\usepackage{xcolor}',
	'\definecolor{grey60}{HTML}{746C66}',
	'\definecolor{grey40}{HTML}{A7A9AC}',
	r'\usepackage{amsmath, amssymb}',
	r'\usepackage{stackrel}',
	'\\providecommand{\unit}[1]{\ensuremath{\\textcolor{grey60}' +
	'{\mathrm{#1}}}}']

def force(val=True):
	__force__.val = val

def load(fname, svg=False):
	_fig = pickle.load(file(os.path.expanduser('~') +
							'/.pyg/%s.pickle' % fname))
	if not svg:
		_fig.fig._cachedRenderer = None
		for ax in [_fig.ax, _fig.ax2]:
			if ax is not None:
				for axi in ax.images:
					axi._imcache = None
				ax._cachedRenderer = None
	_fig.loaded = True
	return _fig

class svg(object):
	def __init__(self, filename):
		from sys import platform
		import os
		if platform == "darwin":
			self.filename = os.path.abspath(filename)
		else:
			self.filename = filename
		self.loaded = False

	@staticmethod
	def get_width(fname):
		from sys import platform
		if platform == "linux" or platform == "linux2":
		    command = 'inkscape'
		elif platform == "darwin":
		    command = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
		elif platform == "win32":
		    pass
		if 'png' in fname:
			cmd = 'identify -format "%%[w]" %s' % fname
		else:
			cmd = '%s --without-gui --query-width %s' % (command, fname)
		p = subprocess.Popen([cmd], stdout=subprocess.PIPE,
							 stderr=subprocess.PIPE, shell=True)
		(out, err) = p.communicate()
		try:
			width = float(out)
		except ValueError:
			print out
			print err
		return width

	@staticmethod
	def get_height(fname):
		from sys import platform
		if platform == "linux" or platform == "linux2":
		    command = 'inkscape'
		elif platform == "darwin":
		    command = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
		elif platform == "win32":
		    pass
		if 'png' in fname:
			cmd = 'identify -format "%%[w]" %s' % fname
		else:
			cmd = '%s --without-gui --query-width %s' % (command, fname)
		p = subprocess.Popen([cmd], stdout=subprocess.PIPE,
							 stderr=subprocess.PIPE, shell=True)
		(out, err) = p.communicate()
		try:
			height = float(out)
		except ValueError:
			print out
			print err
		return height

	def show(self, caption='', label=None, scale=None, width=None,
			 convert=True, need_string=False, bbox=None):
		from sys import platform
		if platform == "linux" or platform == "linux2":
		    command = 'inkscape'
		elif platform == "darwin":
		    command = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
		elif platform == "win32":
		    pass
		if label is not None and not self.loaded:
			pickle.dump(self, file(os.path.expanduser('~') +
									'/.pyg/%s.pickle' % label, 'w'))
		fig = None
		if label is None:
			label = caption
		html_widths = {'1': 400, '2': 600, '4': 800}
		if lyx.run_from_ipython() and not lyx.need_latex():
			__counter__ = random.randint(0, 2e9)
		curr_width = self.get_width(self.filename)
		curr_height = self.get_height(self.filename)
		if width is not None:
			if isinstance(width, int) or isinstance(width, float):
				fig_width = width
			elif isinstance(width, str):
				fig_width = html_widths[width]
		elif scale is not None:
			fig_width = curr_width * scale
		if not lyx.need_latex():
			fig_html = r"""
				<div class='figure' name='%s' style='align: center; margin-left: auto; margin-right: auto;'>
					<img style='margin: auto; max-width:800px; width:%fpx; height: auto;' src='%s?%d' />
					<div style='margin: auto; text-align: center;' class='figurecaption' name="%s"><b>Figure %d:</b> %s</div>
				</div>
			""" % (label, fig_width, self.filename, __counter__, label, bi.__figcount__, caption)
			__figures__.val[label] = bi.__figcount__
			bi.__figcount__ += 1
			return display(HTML(fig_html))
		else:
			if __context__.val == "writeup":
				widths = {"1": 3.25, "2": 6.25, "4": 12.50, "fp": 10.0, "cs": 0.0}
			elif __context__.val == "tufte":
				widths = {"1": 2.00, "2": 4.30, "4": 6.30, "fp": 10.0, "cs": 0.0}
			elif __context__.val == "thesis":
				widths = {"1": 3.0, "2": 6.0, "4": 12.00, "fp": 9.0, "cs": 0.0}
			else:
				widths = {"1": 3.0, "2": 6.0, "4": 12.00, "fp": 9.0, "cs": 0.0}
			if width is not None:
				if isinstance(width, float):
					fig_width = width
				else:
					fig_width = widths[width]
			else:
				fig_width = widths['2']
			if bbox is not None:
				if fig_width * curr_height / curr_width > bbox[1]:
					fig_width = bbox[1] * curr_width / curr_height
			svg_filename = self.filename
			pdf_filename = self.filename.replace('.svg', '.pdf')
			if convert:
				os.system('{command} --without-gui -f {svg_filename} -A {pdf_filename}'.format(command=command, pdf_filename=pdf_filename, svg_filename=svg_filename))
				#os.system('rsvg-convert -f pdf -o {pdf_filename} {svg_filename}'.format(pdf_filename=pdf_filename, svg_filename=svg_filename))
			strlatex = r"""
			\begin{figure}
				\centering
				\includegraphics[width=%.2fin]{%s}
				\caption{%s\label{fig:%s}}
			\end{figure}""" % (fig_width * 1.375, pdf_filename, caption, label)
			__figures__.val[label] = bi.__figcount__
			bi.__figcount__ += 1
			if need_string:
				return strlatex
			return Latex(strlatex)

def svg_show(filename, caption='', label=None, scale=None, width=None,
			 convert=True, need_string=False):
	_svg = svg(filename)
	return _svg.show(caption=caption, label=label, scale=scale, width=width,
					 convert=convert, need_string=need_string)


# make the line graphing class
[docs]class pyg2d(object): """ A ``pyg.pyg2d`` object plots many two-dimensional data types. The ``pyg2d`` class provides an access to ``matplotlib`` charting functions and some hook ins to making these functions easier to use and more repeatable. The constructor itself takes only one optional argument, ``env``. .. todo:: Add more color schemes and the ability to define and hook in color schemes manually. :param str env: The environement option defines where you are going to use the generated plot, with the default option being plot (or printing). If you are using this to generate plots for a gui, define this option as ``gui`` and the class will choose a prettier parameter set for your chart. Default: ``plot``. :param str colors: The ``colors`` option defines the color scheme which will be used in the plotting. The ability to hook in schemes will be added. Default: ``purdue``. :type env: ``plot``, ``gui``, or ``None`` :type colors: ``pu``, ``purdue``, ``salabs``, or ``ah`` :return: the ``pyg2d`` object. :rtype: ``pyg2d`` """ leg_col_one_col = 2 leg_col_two_col = 3 leg_col_full_page = 4 instances = [] marker = {0: '+', 1: '.', 2: '1', 3: '2', 4: '3', 5: '4'} linestyle = {0: '-', 1: '--', 2: '-.', 3: ':'} sizestring = {'1': 'onecolumn', '2': 'twocolumn', 'fp': 'fullpage', 'cs': 'customsize', 'none': ''} _figcount = count(0) def __init__(self, env='plot', polar=False, colors='purdue'): self.__class__.instances.append(weakref.proxy(self)) self.__counter__ = self._figcount.next() self.fig = plt.figure(self.__counter__) self.ax = self.fig.add_subplot(111, polar=polar) self.ax_subp = [] self.leg = False self.loaded = False self.ax2 = None self.polar = polar if not self.polar: self.ax.spines['top'].set_visible(False) self.ax.spines['right'].set_visible(False) self.ax.get_xaxis().tick_bottom() self.ax.get_yaxis().tick_left() self.artists = [] self.landscape = True self.width = 3.25 self.height = self.width / 1.61803398875 self.plotnum = 0 self.regnum = 0 self.lines = {} self.bars = {} self.regs = {} self.reg_string = {} self.allartists = [] self.pdf_filename = None self.html_filename = None self.data = [] if colors is 'purdue' or colors is 'pu': import pyg.colors.pu as color self.colors = color.pu_colors else: import pyg.colors.pu as color self.colors = color.pu_colors if env is 'plot': self.rcparamsarray = { "pgf.texsystem": "lualatex", "pgf.rcfonts": False, "font.family": "sans", "font.size": 8.0, "axes.linewidth": 0.5, "axes.edgecolor": "#746C66", "xtick.major.width": 0.25, "xtick.major.size": 2, "xtick.direction": "in", "xtick.minor.width": 0.125, "xtick.color": "#746C66", "ytick.major.width": 0.25, "ytick.major.size": 2, "ytick.minor.width": 0.125, "ytick.color": "#746C66", "ytick.direction": "in", "text.color": "#746C66", "axes.facecolor": "none", "figure.facecolor": "none", "axes.labelcolor": "#746C66", "xtick.labelsize": "small", "ytick.labelsize": "small", "axes.labelsize": "medium", "legend.fontsize": "small", "legend.frameon": False, "axes.grid": False, "grid.color": "#A7A9AC", # grid color "grid.linestyle": ":", # dotted "grid.linewidth": 0.125, # in points "grid.alpha": 0.5, # transparency, between 0.0 and 1.0 "savefig.transparent": True, "path.simplify": True, "text.usetex": True, "pgf.preamble": preamble, "text.latex.preamble": preamble } elif env is 'gui': self.rcparamsarray = { "pgf.texsystem": "lualatex", "pgf.rcfonts": False, "font.family": "sans", "font.size": 18.0, "axes.linewidth": 0.5, "axes.edgecolor": "#FFFFFF", "xtick.major.width": 0.25, "xtick.major.size": 2, "xtick.direction": "in", "xtick.minor.width": 0.125, "xtick.color": "#FFFFFF", "ytick.major.width": 0.25, "ytick.major.size": 2, "ytick.minor.width": 0.125, "ytick.color": "#FFFFFF", "ytick.direction": "in", "text.color": "#FFFFFF", "axes.facecolor": "black", "figure.facecolor": "black", "axes.labelcolor": "#FFFFFF", "xtick.labelsize": 12.0, "ytick.labelsize": 12.0, "axes.labelsize": 16.0, "legend.fontsize": "small", "legend.frameon": False, "axes.grid": False, "grid.color": "#A7A9AC", # grid color "grid.linestyle": ":", # dotted "grid.linewidth": 0.125, # in points "grid.alpha": 0.5, # transparency, between 0.0 and 1.0 "savefig.transparent": True, "path.simplify": True, "pgf.preamble": preamble, "text.latex.preamble": preamble } matplotlib.rcParams.update(self.rcparamsarray) @staticmethod def change_context(context): __context__.val = context
[docs] def xlabel(self, label, axes=None): r""" ``pyg2d.xlabel`` adds a label to the x-axis. ``pyg2d.xlabel`` adds a label to the x-axis of the current axes (or other axis given by kwarg ``axes``). :param str label: The label added to the x-axis of the defined axis. The label can take LaTeX arguments and the ah style guide asks for labels given as 'Label ($variable$) [$unit$]'. :param axes: If not ``None``, this argument will apply the x-label to the provided axis. :type axes: axes, or ``None`` :return: None """ if axes is None: axes = self.ax xlab = axes.set_xlabel(label) self.artists.append(xlab) self.allartists.append('xlab: ' + label)
[docs] def add_subplot(self, subp=121, polar=False): """ ``pyg2d.add_subplot`` adds a grid in which you can make subplots. ``pyg2d.add_subplot`` follows Matlab's lead and allows you to plot several axes on one plot. The newly created axes is saved as ``pyg2d.ax2`` - this should be expanded for more axes later. .. todo:: Expand subplotting to be able to use more than two axes total. :param int subp: If kwarg ``subp`` is not defined, the default is to add a second plot in a 1x2 array. When ``subp`` is defined, it will follow that system (i.e. ``subp=234`` means you have two rows and three columns and you are plotting in the 4th postition ``(2,1)``). :return: None """ gsstr = str(subp) gs1 = int(gsstr[0]) gs2 = int(gsstr[1]) self.ax2 = self.fig.add_subplot(subp, polar=polar) self.ax.change_geometry(gs1, gs2, 1) self.ax2.change_geometry(gs1, gs2, 2) self.ax_subp.append(self.fig.add_subplot(subp)) if not self.polar: self.ax2.spines['top'].set_visible(False) self.ax2.spines['right'].set_visible(False) self.ax2.get_xaxis().tick_bottom() self.ax2.get_yaxis().tick_left()
[docs] def title(self, title, axes=None): """ ``pyg2d.title`` adds a title to the plot. :param str title: the title to be added to the plot. The title can take LaTeX arguments. :return: None """ if axes is None: axes = self.ax ttl = axes.set_title(title) self.artists.append(ttl) self.allartists.append(ttl)
[docs] def ylabel(self, label, axes=None): """ ``pyg2d.ylabel`` adds a label to the y-axis. ``pyg2d.ylabel`` adds a label to the y-axis of the current axes (or other axis given by kwarg ``axes``). The label can take LaTeX arguments and the ah style guide asks for labels given as 'Label ($variable$) [$unit$]'. :param str label: The label added to the y-axis of the defined axis. The label can take LaTeX arguments and the ah style guide asks for labels given as 'Label ($variable$) [$unit$]'. :param axes: If not ``None``, this argument will apply the x-label to the provided axis. :type axes: axes, or ``None`` :return: None """ if axes is None: axes = self.ax ylab = axes.set_ylabel(label) self.artists.append(ylab) self.allartists.append('ylab: ' + label)
[docs] def xlim(self, minx, maxx, axes=None): """ ``pyg2d.xlim`` limits the view of the x-axis to limits. :param float minx: The minimum value of x that will be shown. :param float maxx: The maximum value of x that will be shown. :param axes: If not ``None``, this argument will apply the x-limit to the provided axis. :type axes: axes, or ``None`` :return: None """ if axes is None: axes = self.ax axes.set_xlim([minx, maxx])
[docs] def ylim(self, miny, maxy, axes=None): """ ``pyg2d.ylim`` limits the view of the y-axis to limits. :param float miny: The minimum value of y that will be shown. :param float maxy: The maximum value of y that will be shown. :param axes: If not ``None``, this argument will apply the y-limit to the provided axis. :type axes: axes, or ``None`` :return: None """ if axes is None: axes = self.ax axes.set_ylim([miny, maxy])
[docs] def legend(self, loc=1, exclude='saljfdaljdfaslkjfd', axes=None): """ ``pyg2d.legend`` shows the legend on the plot. ``pyg2d.legend`` toggles the legend showing on. This is done by getting the included objects and titles from the ``matplotlib`` axis item, and then checking to see if there is the word 'connector' in that title. If there is that word, then the entry is discarded. :param int loc: The location of the legend in counter-clockwise notation. :param str exclude: Partial key string of legend entries to exclude :return: None """ if axes is None: axes = self.ax self.leg = axes.legend(loc=loc) (legobjs, legtitles) = axes.get_legend_handles_labels() inc_objs = [] inc_titles = [] for i in range(0, len(legtitles)): if 'connector' not in legtitles[i] and exclude not in legtitles[i]: inc_objs.append(legobjs[i]) inc_titles.append(legtitles[i]) axes.legend(inc_objs, inc_titles, loc=loc)
[docs] def xticks(self, ticks, labels, axes=None): """ ``pyg2d.xticks`` changes the ticks and labels to provided values. ``pyg2d.xticks`` will move the ticks on the abscissa to the locations given in ``ticks`` and place the labels in list ``labels`` at those locations, repsectively. :param list ticks: The values where the new tick labels will be placed on the x-axis. :param list labels: The string labels for each tick. :param axes axes: An axes to append these ticks to, if not current. :return: None """ if axes is not None: plt.sca(axes) else: axes = self.ax axes.set_xticks(ticks) axes.set_xticklabels(labels)
[docs] def yticks(self, ticks, labels, axes=None): """ ``pyg2d.yticks`` changes the ticks and labels to provided values. ``pyg2d.yticks`` will move the ticks on the ordinate axis to the locations given in ``ticks`` and place the labels in list ``labels`` at those locations, repsectively. :param list ticks: The values where the new tick labels will be placed on the y-axis. :param list labels: The string labels for each tick. :param axes axes: An axes to append these ticks to, if not current. :return: None """ if axes is not None: plt.sca(axes) else: axes = self.ax axes.set_yticks(ticks) axes.set_yticklabels(labels)
[docs] def markers_on(self): """ ``pyg2d.markers_on`` turns on the data markers for all data sets. :return: None """ for key in self.lines: self.lines[key].set_alpha(1.0) self.lines[key].set_markersize(6)
[docs] def markers_off(self): """ ``pyg2d.markers_off`` turns off the data markers for all data sets. :return: None """ for key in self.lines: self.lines[key].set_markersize(0)
[docs] def fit_markers_off(self): """ ``pyg2d.fit_markers_off`` turns off the data markers for any fit lines that are plotted :return: None """ for key in self.lines: if "fit" in key: self.lines[key].set_markersize(0)
[docs] def fit_lines_on(self): """ ``pyg2d.fit_lines_on`` turns on the connector lines for any regression fits that are plotted :return: None """ for key in self.lines: if "fit" in key: self.lines[key].set_linewidth(1.0)
[docs] def lines_on(self): """ ``pyg2d.lines_on`` turns on the connector lines for all data sets. :return: None """ for key in self.lines: self.lines[key].set_linewidth(1.0)
[docs] def lines_off(self): """ ``pyg2d.lines_off`` turns off the connector lines for all data sets. :return: None """ for key in self.lines: self.lines[key].set_linewidth(0.0)
[docs] def add_vline(self, x, ymin=None, ymax=None, ls='solid', lw=1.5, color='black', name=None, axes=None): """ ``pyg2d.add_vline`` draws a vertical line. ``pyg2d.add_vline`` draws a vertical line from either the bottom axis to the top axis if ``ymin`` and ``ymax`` are not provided, otherwise it is drawn from ``ymin`` to ``ymax`` at ``x``. Be careful not to change from linear to log scale AFTER using this function, as .. math:: \log\left(0\\right)=-\infty and this means the line will extend past the extents of the latex page. .. todo:: Fix the latex page extents problem with :math:`-\infty` :param float x: The abscissa coordinate of the line. :param float ymin: The lower extent of the line. :param float ymax: The upper extent of the line. :param string ls: The style of the line, i.e. '-', '--', ':', etc. :param float lw: The width of the line in pt. :param string color: The color of the line. :param axes axes: The axes object the line should be added to, if not current. :return: None """ if axes is None: axes = self.ax if ymin == None: ymin = np.min(axes.get_ylim()) if ymax == None: ymax = np.max(axes.get_ylim()) if name is not None: self.add_to_legend(name=name, color=color, linestyle=ls) return axes.vlines(x, ymin, ymax, linestyles=ls, linewidths=lw, color=color)
[docs] def add_hline(self, y, xmin=None, xmax=None, ls='solid', lw=1.5, color='black', axes=None): """ ``pyg2d.add_hline`` draws a horizontal line. ``pyg2d.add_hline`` draws a horizontal line from either the left axis to the right axis if ``xmin`` and ``xmax`` are not provided, otherwise it is drawn from ``xmin`` to ``xmax`` at ``y``. Be careful not to change from linear to log scale AFTER using this function, as .. math:: \log\left(0\\right)=-\infty and this means the line will extend past the extents of the latex page. :param float y: The ordinate axis coordinate of the line. :param float xmin: The left extent of the line. :param float xmax: The right extent of the line. :param string ls: The style of the line, i.e. '-', '--', ':', etc. :param float lw: The width of the line in pt. :param string color: The color of the line. :param axes axes: The axes object the line should be added to, if not current. :return: None """ if axes is None: axes = self.ax if xmin == None: xmin = np.min(axes.get_xlim()) if xmax == None: xmax = np.max(axes.get_xlim()) return axes.hlines(y, xmin=xmin, xmax=xmax, linestyle=ls, linewidth=lw, color=color)
def add_label(self, x, y, string, color='black'): curve_place = (x, y) ann = self.ax.annotate(string, xy=curve_place, xytext=curve_place, color=color) self.allartists.append(ann) def change_style(self, rcparamsarray): matplotlib.rcParams.update(rcparamsarray) def add_arrow(self, x1, x2, y1, y2, string='', axes=None, fc="0.5", alpha=1.0, ha='center', va='center', arrowprops=None): if arrowprops is None: arrowprops = dict(arrowstyle="-|>", fc=fc, ec=fc, alpha=alpha) if axes is None: axes = self.ax ann = axes.annotate(string, xy=(x2, y2), xytext=(x1, y1), color=fc, alpha=alpha, horizontalalignment=ha, verticalalignment=va, arrowprops=arrowprops) self.allartists.append(ann) def add_text(self, x1, y1, string=None, ha='center', va='center', color="#746C66", rotation=0, axes=None, fontsize=None): if axes is None: axes = self.ax if fontsize is not None: ann = axes.text(x1, y1, string, fontsize=fontsize, ha=ha, va=va, color=color, rotation=rotation) else: ann = axes.text(x1, y1, string, ha=ha, va=va, color=color, rotation=rotation) self.allartists.append(ann) def add_vmeasure(self, x1, y1, y2, string=None, place=None, offset=0.01, axes=None, units='', log=False): if axes is None: axes = self.ax if string is None: string = r"$%.0f\,\mathrm{" % np.sqrt((y2 - y1)**2.0) + units + "}$" if place is None: place = "left" total_width = np.max(axes.get_xlim()) - np.min(axes.get_xlim()) length = 0.05 lw = 0.5 h1 = self.add_hline(y1, x1 - offset * total_width, x1 - offset * total_width - length * total_width, lw=lw, axes=axes) h2 = self.add_hline(y2, x1 - offset * total_width, x1 - offset * total_width - length * total_width, lw=lw, axes=axes) if log: y_mid = np.sqrt(y2 * y1) else: y_mid = (y2 + y1) / 2.0 x_mid = (x1 - offset * total_width + x1 - offset * total_width - length * total_width) / 2.0 h3 = self.add_arrow(x_mid, x_mid, y_mid, y1, string=self.latex_string(string), axes=axes) h4 = self.add_arrow(x_mid, x_mid, y_mid, y2, string=self.latex_string(string), axes=axes) self.allartists.append((h1, h2, h3, h4)) def add_hmeasure(self, x1, x2, y1, string=None, place=None, offset=0.01, axes=None, units='', log=False): if axes is None: axes = self.ax if string is None: string = r"$%.0f\,\mathrm{" % np.sqrt((x2 - x1)**2.0) + units + "}$" if place is None: place = "up" total_width = np.max(axes.get_ylim()) - np.min(axes.get_ylim()) length = 0.05 lw = 0.5 h1 = self.add_vline(x1, y1 + offset * total_width, y1 + offset * total_width + length * total_width, lw=lw, axes=axes) h2 = self.add_vline(x2, y1 + offset * total_width, y1 + offset * total_width + length * total_width, lw=lw, axes=axes) if log: x_mid = np.sqrt(x1 * x2) else: x_mid = (x2 + x1) / 2.0 y_mid = (y1 + offset * total_width + y1 + offset * total_width + length * total_width) / 2.0 h3 = self.add_arrow(x_mid, x1, y_mid, y_mid, string=self.latex_string(string), axes=axes) h4 = self.add_arrow(x_mid, x2, y_mid, y_mid, string=self.latex_string(string), axes=axes) self.allartists.append((h1, h2, h3, h4)) def equal_aspect_ratio(self): self.ax.set_aspect('equal', 'datalim') @staticmethod def latex_string(string): try: power = int(re.search('(e([+-]?[0-9]+))', string).group(2)) string = re.sub('e([+-]?[0-9]+)', (r'\times 10^{%d}' % power).encode('string-escape'), string) except AttributeError: pass return string def add_data_pointer(self, x, curve=None, point=None, string=None, place='up-right', ha='left', axes=None, latex=True, fc='0.3'): if axes is None: axes = self.ax if curve is not None: y = curve.at(x) elif point is not None: y = point else: raise Exception('No point for the arrow given in reference to ' + 'data pointer.') if string is None: string = '$\left( %f,%f \\right)$' % (x, y) if place == 'up-right': curve_place = (4.0 * x / 3.0, 4.0 * y / 3.0) elif place == 'up-left': curve_place = (3.0 * x / 4.0, 4.0 * y / 3.0) elif place == 'down-right': curve_place = (4.0 * x / 3.0, 3.0 * y / 4.0) elif place == 'down-left': curve_place = (2.0 * x / 4.0, 3.0 * y / 4.0) elif type(place) is tuple: curve_place = place if latex: string = self.latex_string(string) ann = axes.annotate(string, xy=(x, y), xytext=curve_place, ha=ha, color=fc, arrowprops=dict(arrowstyle="fancy", fc=fc, ec="none", patchB=Ellipse((2, -1), 0.5, 0.5), connectionstyle= "angle3,angleA=0,angleB=-90") ) self.allartists.append(ann) def add_reg_line(self, x, y, regtype='lin', name='reg', xerr=None, yerr=None, axes=None): if axes is None: axes = self.ax self.regnum = self.regnum + 1 if name is 'reg': name = 'reg%d' % (self.regnum); # set up the error bounds if yerr is None: y_err_up = None y_err_down = None else: y_err_up = yerr[0, :] y_err_down = yerr[1, :] print y_err_up print y # determine the regression if regtype.isdigit(): # determine the coefficients of degree regtype coeffs = np.polyfit(x,y,regtype); print coeffs # determine a fine grid of values x_fit = np.linspace(min(x),max(x),num=1000); y_fit = np.polyval(coeffs,x_fit); self.coeffs = coeffs; name = '$y\left( x \\right) = '; for i in range(0,int(regtype)): if coeffs[i] > 0: name += '+ %f' % (abs(coeffs[i])); if i > 0: name += 'x^{%d}' % (i); elif coeffs[i] < 0: name += '- %f' % (abs(coeffs[i])); if i > 0: name += 'x^{%d}' % (i); name += '$'; print name; elif regtype is 'exp': x_np = np.array(x); x_err_np = np.array(xerr); y_np = np.array(y); y_err_np = np.array(yerr); def exp_func(B,x): return B[0]*np.exp(B[1]*x); exp_model = Model(exp_func); exp_data = RealData(x_np,y_np,sx=x_err_np,sy=y_err_np); odr = ODR(exp_data,exp_model,beta0=[0.,1.]) out = odr.run(); if out.res_var > 1.0 and out.beta[1] < 0.0: x_fit = np.linspace(min(x),max(x),num=1000); y_fit = exp_func(out.beta,x_fit); self.reg_string[name] = '$t_{wait} = e^{%.2f \cdot p} + %.2f$' % (out.beta[1],out.beta[0]); if out.sum_square < 20: y_err_up = exp_func(out.beta+out.sd_beta,x_fit); y_err_down = exp_func(out.beta-out.sd_beta,x_fit); if y_err_up[0] > 120: y_err_up = None; y_err_down = None; else: y_err_up = None; y_err_down = None; print "showing the exponential error will occlude data"; else: y_fit = None; x_fit = None; y_err_up = None; y_err_down = None; print "the exponential does not fit to the data"; elif regtype is 'log': print 'I haven\'t yet completed the log fitting!'; #do something; elif regtype is 'gaussian': def gaus(x,a,x0,sigma): return a*exp(-(x-x0)**2/(2*sigma**2)); pop,pcov = curve_fit(gaus,x,y,p0=[1,np.mean(y),np.std(y)]); x_fit = x_fit = np.linspace(min(x),max(x),num=1000); y_fit = gaus(x_fit); # plot the regression if x_fit is not None and y_fit is not None: self.x_fit = x_fit; self.y_fit = y_fit; lines = axes.plot(x_fit, y_fit, label=name, color='#A7A9AC', ls='--') self.regs[name] = lines[0]; # make sure these are lines lines[0].set_markersize(0); lines[0].set_lw(1.0); if y_err_up is not None and y_err_down is not None: uperrlines = plt.plot(x_fit,y_err_up,color='#D1D3D4',ls='--'); downerrlines = plt.plot(x_fit,y_err_down,color='#D1D3D4',ls='--'); axes.fill_between(x_fit,y_err_up,y_err_down,facecolor='#D1D3D4',alpha=0.5,lw=0.0); # add the regression to the dict of regressions def fill_under_curve(self, curve, scale=0., *args, **kwargs): if curve.data == 'binned': self.fill_between(curve.binned_data_x, scale * np.ones_like(curve.binned_data_y), curve.binned_data_y, **kwargs) else: self.fill_between(curve.x, scale * np.ones_like(curve.y), curve.y, **kwargs) def fill_between(self, x, y1, y2=None, fc='red', name='plot', ec='None', leg=True, axes=None, alpha=0.5, xmin=None, xmax=None, log=False): if axes is None: axes = self.ax self.plotnum = self.plotnum + 1 if name is 'plot': name = 'plot%d' % (self.plotnum) p = axes.fill_between(x, y1, y2, facecolor=fc, alpha=alpha, edgecolor=ec, linewidth=0.001) self.allartists.append(p) if leg: patch = axes.add_patch(Polygon([[0, 0], [0, 0], [0, 0]], facecolor=fc, alpha=alpha, label=name)) self.bars[name] = patch def add_to_legend(self, name=None, line=True, color=None, linestyle=None, alpha=1.0, axes=None): if axes is None: axes = self.ax if not line: patch = axes.add_patch(Polygon([[0, 0], [0, 0], [0, 0]], color=color, alpha=alpha, label=name)) self.bars[name] = patch else: line = axes.add_line(Line2D([0, 0], [0, 0], color=color, alpha=alpha, linestyle=linestyle, label=name)) self.lines[name] = line def fill_betweenx(self, x1, x2, y, fc='red', name='plot', ec='None', leg=True, axes=None, alpha=0.5): if axes is None: axes = self.ax; self.plotnum=self.plotnum+1; if name is 'plot': name = 'plot%d' % (self.plotnum); idx = np.argsort(x1); x1 = np.array(x1); x2 = np.array(x2); y = np.array(y); x1 = x1[idx]; x2 = x2[idx]; y = y[idx]; p = axes.fill_betweenx(y,x1,x2,facecolor=fc,edgecolor=ec,alpha=alpha) self.allartists.append(p) def semi_log_y(self, axes=None): if axes is None: axes = self.ax axes.set_yscale('log', nonposy='mask') self.allartists.append('logy') def semi_log_x(self, axes=None): if axes is None: axes = self.ax axes.set_xscale('log', nonposx='mask') self.allartists.append('logx') def log_log(self): self.semi_log_x() self.semi_log_y() def add_circle(self, x, y, r, fc='red', ec='None', alpha=0.5, axes=None, name='plot'): if axes is None: axes = self.ax self.plotnum = self.plotnum + 1 if name is 'plot': name = 'plot%d' % (self.plotnum) patch = axes.add_patch(Circle((x, y), r, facecolor=fc, edgecolor=ec, alpha=alpha, label=name)) self.bars[name] = patch self.allartists.append(patch) def stackplot(self, x, y, baseline='zero', axes=None, name='stackplot', **kwargs): if axes is None: axes = self.ax self.plotnum = self.plotnum + 1 if name is 'plot': name = 'plot%d' % (self.plotnum) stack = axes.stackplot(x, y, baseline=baseline, **kwargs) def add_line(self, x, y, name='plot', xerr=None, yerr=None, linewidth=0.5, linestyle=None, linecolor='black', legend=True, axes=None, error_fill=False): if axes is None: axes = self.ax self.data.extend([[x, y]]) self.plotnum = self.plotnum + 1 if name == 'plot': name = 'plot%d' % (self.plotnum) if linestyle is None: _ls = '-' else: _ls = linestyle if xerr is None and yerr is None: line = axes.plot(x, y, label=name, color=linecolor, marker=self.marker[self.plotnum % 5], ls=_ls, lw=linewidth, solid_capstyle='butt') for i in range(0, len(line)): self.lines[name + '%d' % (i)] = (line[i]) else: if linecolor == 'black': ecolor = '#A7A9AC' else: col = Color(linecolor) col.saturation = 0.5 col.luminance = 0.75 ecolor = col.hex if not error_fill: line, caplines, barlinecols = axes.errorbar(x, y, label=name, color=linecolor, xerr=xerr, yerr=yerr, marker=self.marker[self.plotnum % 5], ls=_ls, ecolor=ecolor, lw=linewidth, clip_on=True) self.lines[name] = (line) else: self.add_line(x, y, xerr=None, yerr=None, name=name, linewidth=0.5, linestyle=linestyle, linecolor=linecolor, legend=legend, axes=axes) self.fill_between(x, np.array(y) - np.array(yerr), np.array(y) + np.array(yerr), leg=False, fc=linecolor, name=name + 'err') line = self.lines[name + '0'] self.markers_on() self.lines_on() self.allartists.append(line) def add_line_yy(self, x, y, name='plot', xerr=None, yerr=None, linecolor='black', linewidth=0.5, linestyle=None, legend=True, axes=None): # make new axis if axes is None: self.ax2 = self.ax.twinx() else: self.ax2 = axes.twinx() line = self.add_line(x, y, name=name + 'yy', xerr=xerr, yerr=yerr, linewidth=linewidth, linecolor=linecolor, linestyle=linestyle, legend=legend, axes=self.ax2) self.allartists.append(line) if legend: self.add_line([0., 0.], [np.nan, np.nan], name=name, linewidth=linewidth, linecolor=linecolor, linestyle=linestyle, axes=self.ax) def add_line_xx(self, x, y, name='plot', xerr=None, yerr=None, linecolor='black', linewidth=0.5, linestyle=None, legend=True): # make new axis self.ax2 = self.ax.twiny() line = self.add_line(x, y, name=name, xerr=xerr, yerr=yerr, linewidth=linewidth, linecolor=linecolor, linestyle=linestyle, legend=legend, axes=self.ax2) self.allartists.append(line) def add_xx(self,calfunc): self.ax2 = self.ax.twiny() mini = calfunc(np.min(self.ax.get_xlim())) maxi = calfunc(np.max(self.ax.get_xlim())) self.ax2.set_xlim(mini,maxi) self.ax2.get_xaxis().tick_top() self.allartists.append(self.ax2) def add_yy(self,calfunc): self.ax2 = self.ax.twinx(); self.calfunc = calfunc; self.update_yy() self.allartists.append(self.ax2) def update_yy(self): mini = self.calfunc(np.min(self.ax.get_ylim())); maxi = self.calfunc(np.max(self.ax.get_ylim())); self.ax2.set_ylim(mini,maxi); self.ax2.get_yaxis().tick_right(); def add_hist(self, y, bins, facecolor='gray', alpha=0.5, name='plot'): self.plotnum = self.plotnum + 1 if name is 'plot': name = 'plot%d' % (self.plotnum) n, bins, patches = self.ax.hist(y, bins=bins, label=name, facecolor=facecolor, alpha=alpha, normed=False) self.bars[name] = patches self.allartists.append(self.bars[name]) return n, bins def add_bar(self, x, y, hold=True, facecolor='gray', alpha=0.5, name='plot'): self.plotnum = self.plotnum + 1 self.data.extend([[x, y]]) if name is 'plot': name = 'plot%d' % (self.plotnum) delta = [j - i for i, j in zip(x[:-1], x[1:])] delta.append(delta[-1]) # x = [j - (i/2) for i, j in zip(delta, x)]; patches = self.ax.bar(x, y, width=delta, label=name, facecolor=facecolor, alpha=alpha) self.bars[name] = patches self.allartists.append(self.bars[name]) return x, y, delta def add_legend(self, axes=None): if axes is None: axes = self.ax self.leg = True leg = axes.legend() self.artists.append(leg) def det_height(self, ratio="golden"): if ratio is "golden": r = (1. + np.sqrt(5.)) / 2. elif ratio is "silver": r = 1. + np.sqrt(2.) elif ratio is "bronze": r = (3. + np.sqrt(13.)) / 2. elif ratio is "invgolden": r = 2. / (1. + np.sqrt(5.)) elif ratio is "invsilver": r = 1. / (1. + np.sqrt(2.)) elif ratio is "invbronze": r = 2. / (3. + np.sqrt(13.)) else: r = float(ratio) if self.landscape: self.height = self.width / r else: self.height = self.width * r def remove_font_sizes(self,filename): f=open(filename,'r') fstring = "\\centering \n" + f.read() f.close() f=open(filename,'w') fstring=fstring.replace("\\rmfamily\\fontsize{8.328000}{9.993600}\\selectfont","\\scriptsize") fstring=fstring.replace("\\rmfamily\\fontsize{12.000000}{14.400000}\\selectfont","\\normalsize") fstring = filter(lambda x: x in string.printable, fstring); f.write(fstring) f.close() def add_math_jax(self, filename): f = open(filename, 'r') fstring = \ "<script type=\"text/x-mathjax-config\">\n" + \ "MathJax.Hub.Config({\n" + \ " tex2jax: {\n" + \ " inlineMath: [ ['$','$'], ['\\\\(','\\\\)'] ],\n" + \ " },\n" + \ " \"HTML-CSS\": {\n" + \ " linebreaks: {\n" + \ " automatic: true,\n" + \ " width: \"80% container\",\n" + \ " }\n" + \ " },\n" + \ " SVG: {\n" + \ " linebreaks: {\n" + \ " automatic: true,\n" + \ " width: \"80% container\",\n" + \ " }\n" + \ " },\n" + \ " TeX: {\n" + \ " equationNumbers: {\n" + \ " autoNumber: \"all\"\n" + \ " },\n" + \ " },\n" + \ " showMathMenu: false\n" + \ "});\n" + \ "\n" + \ "</script>\n" + \ "\n" + \ "<script type=\"text/javascript\"" + \ " src=\"http://cdn.mathjax.org/mathjax/latest/MathJax.js?" + \ "config=TeX-AMS-MML_HTMLorMML\">" + \ "</script>" + f.read() f.close() f = open(filename, 'w') f.write(fstring) f.close() def long_name(self): self.leg_col_one_col = 1 self.leg_col_two_col = 1 self.leg_col_full_page = 1 def set_size(self, size, sizeofsizes, customsize=None, legloc=None, tight=True, ratio="golden", width=None): #global __context__.val if __context__.val == "writeup": widths = {"1": 3.25, "2": 6.25, "4": 12.50, "fp": 10.0, "cs": 0.0} elif __context__.val == "tufte": widths = {"1": 2.00, "2": 4.30, "4": 6.30, "fp": 10.0, "cs": 0.0} elif __context__.val == "thesis": widths = {"1": 3.0, "2": 6.0, "4": 12.00, "fp": 9.0, "cs": 0.0} else: widths = {"1": 3.0, "2": 6.0, "4": 12.00, "fp": 9.0, "cs": 0.0} if width is None: self.width = widths[size] elif isinstance(width, basestring): self.width = widths[width] else: self.width = width if size is '1': self.det_height(ratio=ratio) elif size is '2': self.det_height(ratio=ratio) self.fig.set_size_inches(self.width, self.height) elif size is '4': self.det_height(ratio=ratio) self.fig.set_size_inches(self.width, self.height) elif size is 'fp': self.width=10; self.det_height(); self.fig.set_size_inches(self.width,self.height); if self.leg: self.ax.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3, ncol=self.leg_col_full_page, mode="expand", borderaxespad=0.); elif size is 'cs': if customsize is not None: self.width=customsize[0]; self.height=customsize[1]; if legloc is not None: self.ax.legend(loc=legloc,ncol=2); self.fig.set_size_inches(self.width, self.height) if tight: plt.tight_layout() def export_fmt(self, filename, size, sizeofsizes, format): if sizeofsizes == 1: size = 'none' if format is 'png': add = '.png' elif format is 'pgf': add = '.pgf' elif format is 'pdf': add = '.pdf' elif format is 'svg': # save as pdf, then pdf2svg if not os.path.isfile(filename + self.sizestring[size] + '.svg') \ or self.force_export or __force__.val: self.fig.savefig(filename + self.sizestring[size] + '.pdf', bbox_extra_artists=self.artists, bbox_inches='tight', transparent=True, dpi=1200) os.system('pdf2svg ' + filename + self.sizestring[size] + '.pdf ' + filename + self.sizestring[size] + '.svg') os.remove(filename + self.sizestring[size] + '.pdf') self.svg_filename = filename + self.sizestring[size] + '.svg' elif format is 'websvg': add = 'web.svg' self.svg_filename = filename + self.sizestring[size] + add if (format is not 'svg') and (format is not 'html'): if not os.path.isfile(filename + self.sizestring[size] + add) \ or self.force_export or __force__.val: self.fig.savefig(filename + self.sizestring[size] + add, bbox_extra_artists=self.artists, bbox_inches='tight', transparent=True) if format is 'html': add = '.html' import mpld3 from mpld3 import plugins import plotly.plotly as py import plotly.tools as tls import plotly.offline as offline from plotly.offline.offline import _plot_html plotly_fig = tls.mpl_to_plotly(self.fig) plot_file = offline.plot(plotly_fig) js_string = '<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG"></script>' self.html_filename = filename + add os.system('cp temp-plot.html ' + filename + add) scriptstring = \ "MathJax.Hub.Config({\n" + \ " tex2jax: {\n" + \ r" inlineMath: [ ['$','$'], ['\\(','\\)'] ]," + "\n" + \ " }\n" + \ "});\n" from bs4 import BeautifulSoup as bs with open(filename + add, 'r') as f: soup = bs(f.read(), "lxml") title = soup.find('meta') script = soup.new_tag('script') script2 = soup.new_tag('script') script['type'] = "text/javascript" script['src'] = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG" script2['type'] = "text/x-mathjax-config" script2.append(scriptstring) # script['src'] = "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG" title.insert_after(script2) script2.insert_after(script) with open(filename + '2' + add, 'wb') as f: f.write(soup.prettify('utf-8')) os.system("cp " + filename + "2" + add + " " + filename + add) if format is 'pgf': self.remove_font_sizes(filename + self.sizestring[size] + add) self.pgf_filename = filename + self.sizestring[size] + add def show(self, caption='', label=None, scale=None, interactive=False, need_string=False): if label is not None and not self.loaded: plt.ioff() pickle.dump(self, file(os.path.expanduser('~') + '/.pyg/%s.pickle' % label, 'w')) fig = None if label is None: label = str([''.join(ch for ch in caption if ch.isalnum())]) self.label = label if scale is None and lyx.run_from_ipython() and not lyx.need_latex(): scale = 2.0 elif scale is None: scale = 1.0 if caption is not None: self.caption = caption if interactive: plt.ion() plt.show(block=True) elif not lyx.need_latex(): __counter__ = random.randint(0, 2e9) fig_width = self.fig.get_figwidth() * self.fig.dpi * scale fig_html = r""" <div class='pygfigure' name='%s' style='text-align: center; max-width: 800px; margin-left: auto; margin-right: auto;'> <img style='margin: auto; max-width:100%%; width:%fpx; height: auto;' src='%s?%d' /> <div style='margin: auto; text-align: center;' class='figurecaption'><b>Figure %d:</b> %s</div> </div> """ % (label, fig_width, self.svg_filename, __counter__, bi.__figcount__, self.caption) __figures__.val[label] = bi.__figcount__ bi.__figcount__ += 1 fig = HTML(fig_html) self.close() if need_string: return fig_html elif lyx.need_latex(): if self.force_pdf: include_line = '\includegraphics{%s}' % self.pdf_filename else: include_line = '\input{%s}' % (self.pgf_filename) if __context__.val == 'tufte' and self.width > 5.0: figfloat = 'figure*' centering = '' elif __context__.val == 'tufte' and self.width < 4: figfloat = 'marginfigure' centering = '' else: figfloat = 'figure' centering = r'\centering' strlatex = r""" \begin{%s} %s %s \caption{%s\label{fig:%s}} \end{%s}""" % (figfloat, centering, include_line, self.caption, self.label, figfloat) __figures__.val[label] = bi.__figcount__ bi.__figcount__ += 1 fig = Latex(strlatex) self.close() if need_string: return strlatex else: if self.pdf_filename is not None: if platform.system() == "Darwin": os.system("open -a Preview " + self.pdf_filename) if platform.system() == "Linux": os.system("evince " + self.pdf_filename + " &") if self.html_filename is not None: os.system("google-chrome " + self.html_filename + " &") display(fig) def export(self, filename, sizes=None, formats=None, customsize=None, legloc=None, tight=True, ratio="golden", width=None, caption='', force_pdf=False, force=False): self.force_pdf = force_pdf self.force_export = force self.caption = caption #global __context__.val if sizes is None: if __context__.val == "writeup": sizes = ['1'] elif __context__.val == "thesis": sizes = ['2'] if lyx.run_from_ipython(): sizes = ['2'] for size in sizes: if formats is None: if lyx.run_from_ipython(): formats = ['svg'] if lyx.need_latex() and not force_pdf: formats = ['pgf'] elif lyx.need_latex() and force_pdf: formats = ['pdf'] else: formats = ['pdf'] for format in formats: if format == 'html': tight = False self.set_size(size, len(sizes), customsize=customsize, legloc=legloc, tight=tight, ratio=ratio, width=width) self.export_fmt(filename, size, len(sizes), format) if format == 'pdf': self.pdf_filename = filename + '.pdf' elif format == 'pgf': self.pgf_filename = filename + '.pgf' elif format == 'png': self.png_filename = filename + '.png' def close(self): plt.close(self.fig) def add_metadata(self, filename): os.system("setfattr -n user.creation_script -v \'%s\' %s" % (__file__, filename)) os.system("setfattr -n user.creation_date -v \'%s\' %s" % (time.strftime("%d/%m/%Y"), filename)) def publish_to(self, directory): if directory not in bi.__exported_files__: bi.__exported_files__[directory] = [] if hasattr(self, 'pgf_filename'): self.add_metadata(self.pgf_filename) shutil.copy(self.pgf_filename, directory) bi.__exported_files__[directory].extend([os.path.basename(self.pgf_filename)]) if hasattr(self, 'pdf_filename'): self.add_metadata(self.pdf_filename) shutil.copy(self.pdf_filename, directory) bi.__exported_files__[directory].extend([os.path.basename(self.pdf_filename)]) if hasattr(self, 'png_filename'): self.add_metadata(self.png_filename) shutil.copy(self.png_filename, directory) bi.__exported_files__[directory].extend([os.path.basename(self.png_filename)])
def commit_publications(message='automated commit'): for key, val in bi.__exported_files__.iteritems(): os.chdir(key) for filename in val: os.system('git add %s' % filename) os.system('git commit -am "%s"' % message)