#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Plot functions
.. topic:: plot.py summary
A module that provides plotting functions for Gnuplot
of Matplorlib.
:Code status: mature
:Documentation status: to be completed
:Authors: Thomas Cokelaer <Thomas.Cokelaer@sophia.inria.fr>,
Samuel Dufour-Kowalski <samuel.dufour@sophia.inria.fr>
:Revision: $Id$
.. .. inheritance-diagram:: openalea.stat_tool.plot
"""
__version__ = "$Id$"
DISABLE_PLOT = False
# !!! Do not plot in nosetests !!!
# buildbot cannot close the windwos popped up by the method/function Plot/plot
# So, we test if the command "python setup.py nosetests" has been used.
# Still, using nosetests executable, windows should pop up.
# if ("nosetests" in sys.argv) or ("pytest" in sys.argv[0]):
# DISABLE_PLOT = True
[docs]
class plotter(object):
"""Abstract base class for all plotter"""
def _init__(self):
pass
[docs]
def plot(self, obj, title, groups=None, *args, **kargs):
"""Plot obj with title"""
raise NotImplementedError()
[docs]
class fakeplot(plotter):
def __init__(self):
plotter.__init__(self)
[docs]
def plot(self, obj, title, groups=None, *args, **kargs):
"""Plot obj with title"""
return
[docs]
class gnuplot(plotter):
"""GNUPlot implementation"""
def __init__(self):
"""Initialize GnuPlot"""
plotter.__init__(self)
import Gnuplot
self.session = Gnuplot.Gnuplot()
[docs]
def plot(self, plotable, title, groups=None, *args, **kargs):
"""
Plot a plotable with title
groups : list of group (int) to plot
"""
import Gnuplot
multiset = plotable
g = self.session
# Title & border
# multiset.border
# nb subplot
_nbx = len(multiset)
# For each subplot
for _i, multiplot in enumerate(multiset):
# Group filter
if groups and multiplot.group not in groups:
continue
g.title(multiplot.title)
# yoffset = i * plotsize
# g('set origin 0.0, %f'%(yoffset))
# g('set size 1.0, %f'%(plotsize))
# Labels
g.xlabel(multiplot.xlabel)
g.ylabel(multiplot.ylabel)
# List of argument for the plot function
plot_list = []
for singleplot in multiplot:
style = singleplot.style
legend = singleplot.legend
_color = singleplot.color
x = []
y = []
for pt in singleplot:
x.append(pt.x)
y.append(pt.y)
p = Gnuplot.Data(x, y)
if style:
# todo: check that this option works.
p.set_option(with_=style)
if legend:
p.set_option(title=legend)
plot_list.append(p)
# Range
_xrange = multiplot.xrange
_yrange = multiplot.yrange
if _xrange.min != _xrange.max:
g("set xrange[%f:%f]" % (_xrange.min, _xrange.max))
if _yrange.min != _yrange.max:
g("set yrange[%f:%f]" % (_yrange.min, _yrange.max))
# Tics
if multiplot.xtics > 0:
g("set xtics 0, %f" % (multiplot.xtics))
if multiplot.ytics > 0:
g("set ytics 0, %f" % (multiplot.ytics))
g.plot(*plot_list)
eval(input("Press Enter to continue"))
[docs]
class mplotlib(plotter):
"""matplotlib implementation of AML Plot
This class defines an interface to matplolib plotting functions
and options.
"""
linestyles = ("-", "--", ":", ".")
pointstyles = ("o", "^", "x", "+", "s", "v", ">", "<")
colors = ("g", "r", "y", "b", "k", "m", "c")
def __init__(self):
"""Initialize matplotlib"""
plotter.__init__(self)
import matplotlib
# matplotlib.use('Qt4Agg')
import pylab
self.pylab = pylab
self.matplotlib = matplotlib
[docs]
def plot(self, plotable, title, groups=None, *args, **kargs):
"""Plot a plotable with title
Parameters
----------
plotable: object of type Plotable
A plotable instance from the ``stat_tool`` object such as :func:`Distribution`.
title: str
Title of the plot
groups: list of group (int)
List of group indices to plot.
Other Parameters
----------------
Show : bool, optional
If True (default), display the figure.
nbcol : int, optional
Number of columns in the figure layout.
legend_size : int, optional
Legend font size (default: 10).
legend_nbcol : int, optional
Number of columns in the legend (default: 2).
legend_loc : str, optional
Legend location (default: ``"best"``).
legend : bool, optional
Whether to display the legend.
y_maxrange_ratio : float, optional
Multiply the maximum y-range by this value (default: 1).
Notes
-----
show=True by default will pop up the figure
"""
show = kargs.get("Show", True)
nbcol = kargs.get("nbcol", 2)
fontsize = kargs.get("fontsize", 10)
options_y_maxrange_ratio = kargs.get("y_maxrange_ratio", 1.1)
line2d = {}
line2d["linewidth"] = kargs.get("linewidth", 1)
legend_size = kargs.get("legend_size", 10)
legend_nbcol = kargs.get("legend_nbcol", 1)
legend_loc = kargs.get("legend_loc", "best")
legend_on = kargs.get("legend", True)
# legend_size = kargs.get("legend_kwds", {})
# Plot(seq1, ViewPoint="Data", nbcol=2, legend_kwds={'prop':{'size':9}})
fig_id = kargs.get("FigureId", 1)
pylab = self.pylab
matplotlib = self.matplotlib
multiset = plotable
if len(multiset) == 1:
nbcol = 1
# Title & border
# if title: pylab.suptitle(title)
# multiset.border
# Configure figure
f1 = pylab.figure(fig_id, figsize=(10, 10))
f1.clf()
f1.set_facecolor("w")
# Count group
group_size = {} # Group size
group_index = {} # group counter
for multiplot in multiset:
g = multiplot.group
try:
group_size[g] += 1
except KeyError:
# init group
group_size[g] = 1
group_index[g] = 0
f = pylab.figure(fig_id + g)
f.set_facecolor("w")
# nb subplot
_nbx = len(multiset)
if title:
pylab.suptitle(title, fontsize=fontsize)
# For each subplot
for i, multiplot in enumerate(multiset):
g = multiplot.group
# Group filter
if groups and g not in groups:
continue
# Select window
pylab.figure(fig_id + g)
# Select Subplot
pylab.subplot(
group_size[g] % nbcol + group_size[g] // nbcol,
nbcol,
group_index[g] + 1,
)
group_index[g] += 1
pylab.title(multiplot.title)
# Labels
pylab.xlabel(multiplot.xlabel, fontsize=fontsize)
pylab.ylabel(multiplot.ylabel, fontsize=fontsize)
lines = []
legends = []
# Impulses do not show up at the boundaries of plot
adjust_xrange_impulse = False
# List of argument for the plot function
for j, singleplot in enumerate(multiplot):
style = singleplot.style
legend = singleplot.legend
color = singleplot.color
label = singleplot.label
if not color:
color = self.colors[j % len(self.colors)]
x = []
y = []
labels = []
for i in range(0, len(singleplot)):
x.append(singleplot.get_label_x(i))
y.append(singleplot.get_label_y(i))
# no data available. check if label is on.
if len(x) == 0:
if label == True:
for i in range(0, singleplot.get_label_size()):
x = singleplot.get_label_x(i)
y = singleplot.get_label_y(i)
labels = singleplot.get_label_text(i)
matplotlib.pyplot.text(x, y, labels)
pylab.hold(True)
# break # nothing else to be done in principle
else:
print("Warning. Empty data.")
for i in range(0, singleplot.get_label_size()):
x = singleplot.get_label_x(i)
y = singleplot.get_label_y(i)
labels = singleplot.get_label_text(i)
matplotlib.pyplot.text(x, y, labels)
pylab.hold(True)
# return
else:
pass
# continue to the normal plots
# Manage style
pointstyle = self.pointstyles[j % len(self.pointstyles)]
if "impulses" in style:
l = pylab.vlines(x, 0, y, **line2d)
adjust_xrange_impulse = True
elif "linespoints" in style:
# l = pylab.plot(x, y, '-', x, y, pointstyle)
l = pylab.plot(x, y, pointstyle + "-", **line2d)
elif "points" in style:
l = pylab.plot(x, y, pointstyle, **line2d)
elif "lines" in style:
l = pylab.plot(x, y, "-", **line2d)
elif "histeps" in style:
l = pylab.plot(x, y, linestyle="steps-mid", **line2d)
else:
l = pylab.plot(x, y, style, **line2d)
if color:
pylab.setp(l, color=color)
if adjust_xrange_impulse:
axes = pylab.gca()
for child in axes.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color("#f5f5f5")
# child.set_color('#ffffff')
lines.append(l)
legends.append(legend)
# Legend
try:
import warnings
with warnings.catch_warnings(record=True) as w:
# Plot(seq1, Viewpoint="Data", nbcol=2,
# legend_kwds={'prop':{'size':9}})
kwds = {}
kwds["prop"] = {"size": legend_size}
kwds["ncol"] = legend_nbcol
kwds["loc"] = legend_loc
if legend_on is True and len(legends) < 15:
lg = pylab.legend(lines, legends, **kwds)
lg.legendPatch.set_alpha(0.1) # transparency
if w:
# it seems that matplotlib.collections.LineCollection appear
# last in legends
new_legends = [] # permutation of legends with LineCollection at the end
index_begin_line_collection = (
0 # where LineCollection objects begin
)
for li in range(len(lines)):
current_legend = legends[li]
if (
str(lines[li].__class__)
== "<class 'matplotlib.collections.LineCollection'>"
):
new_legends += [current_legend]
else:
new_legends.insert(
index_begin_line_collection, current_legend
)
index_begin_line_collection += 1
pylab.legend(new_legends, **kwds)
except Exception as e:
import warnings
warnings.warn("legend failed:" + str(e))
# Grid
pylab.grid(bool(multiplot.grid))
# Range
_xrange = multiplot.xrange
_yrange = multiplot.yrange
a = pylab.gca()
if _xrange.min != _xrange.max:
a.set_xlim([round(_xrange.min, 5), round(_xrange.max, 5)])
if _yrange.min != _yrange.max:
a.set_ylim(
[
round(_yrange.min, 5),
round(_yrange.max * options_y_maxrange_ratio, 5),
]
)
# Tics
xt = round(multiplot.xtics, 5)
if xt > 0:
xmin, xmax = pylab.xlim()
pylab.xticks(pylab.arange(xmin, xmax, xt))
yt = round(multiplot.ytics, 5)
if yt > 0:
ymin, ymax = pylab.ylim()
pylab.yticks(pylab.arange(ymin, ymax, yt))
if show == True:
pylab.show()
return f1
PLOTTER = None
[docs]
def set_plotter(plot):
global PLOTTER
PLOTTER = plot
[docs]
def get_plotter():
"""
Plotter factory
Return a plotter object (matplotlib or gnuplot)
If none is available, raise an ImportError exception
"""
global DISABLE_PLOT
if DISABLE_PLOT:
return fakeplot()
# Try user define PLOTTER
global PLOTTER
if PLOTTER:
return PLOTTER
# Try to import a plotter
else:
try:
_plotter = mplotlib()
except ImportError:
# _plotter = gnuplot()
pass
set_plotter(_plotter)
return _plotter