"""Plot scenario evaluation results (implements the script ``diles-plot-evaluation-results``). """ from __future__ import division import colorsys from itertools import izip, product, chain import optparse import os import shutil import subprocess import tempfile import Image import ImageDraw import matplotlib.pyplot as plt from matplotlib.pylab import nan from matplotlib import rc as plotrc plotrc('text', usetex=True) plotrc('font', family='serif') from diles import stats from diles.learn.validator import ids as validatorids from diles.util import gendicts, writefile from diles.scenario import LEARNERS, WRAPPERS, PREPROCESSORS from diles.scenario.util import ResultDB from diles.learn.eval import Metric # ============================================================================= # misc utilities and constants # ============================================================================= LEGENDFONT = dict(size='medium') class colmap(dict): """ Dictionary for mapping color names to `#`-prefixed RGB hex values. When accessing an item, the key may be a tuple of the color name and a lightness scale parameter (a float). >>> cm = colmap(green="#8ae234", blue="#1101d0") >>> cm['green'] '#8ae234' >>> cm['green', 1.2] '#a6e965' >>> cm['green', 0.8] '#6ec31c' >>> cm[('green', 0.8), 0.8] == cm['green', 0.8 * 0.8] True """ @staticmethod def _lscale(rgb, adjust): rgb = rgb.lstrip("#") rgb = [int(rgb[2*i:2*i+2], base=16) / 255 for i in range(3)] h, l, s = colorsys.rgb_to_hls(*rgb) l = min(1.0, l * adjust) rgb = colorsys.hls_to_rgb(h, l, s) rgb = [int(round(x * 255)) for x in rgb] return "#%s" % "".join(["%02x" % x for x in rgb]) def __getitem__(self, col): try: return super(colmap, self).__getitem__(col) except KeyError: if type(col) != tuple: raise KeyError(col) basecol, lightness = col basecolhex = super(colmap, self).__getitem__(basecol) val = colmap._lscale(basecolhex, lightness) self[col] = val return val COLORS = colmap({ 'green': "#8ae234", 'red1': '#cc0000', 'red2': '#5b0000', 'orange': '#f57900', 'blue': '#5a8ec7', 'yellow': '#fce94f', 'purple': '#6d4b73', 'visio-1': "#ea9651", # orange 'visio-2': "#b3c283", # green 'visio-3': "#6b9bc7", # blue-light 'visio-4': "#4e66b2", # blue-dark 'visio-5': "#8976ac", # purple 'black': "#000000", }) def mask2fname(mask, dest, prefix): """Build a file name from `mask`, prefixed with `prefix`, located in the directory `dest`. """ meta = ".".join("%s-%s" % (k.lower(), str(v[0]).lower()) for k, v in sorted(mask.items()) if len(v) == 1) print(" - %s" % meta) return os.path.join(dest, "%s.%s" % (prefix, meta)) def smoothvalues(l, w=5, r=1): """Smooth a list of values using a moving average. Uses a window size of `w` for the average. The window size must be a positive odd number. The smoothing is applied `r` times (i.e. if `r` is 0, then the original list is returned). """ assert w > 0 and w % 2 == 1 x = w // 2 ind = range(len(l)) # remove NaN l = l[:] for i, e in enumerate(l): if e != e: l[i] = l[i-1] if i else 0 # smooth for _ in range(r): # expand list to let the window catch each original position l = [l[0]] * x + l + [l[-1]] * x # create a new list with the moving average l = [sum(l[i:i + w]) / w for i in ind] return l # ============================================================================= # latex to png # ============================================================================= LATEXTEMPLATE = r""" \documentclass{article} \usepackage[active,tightpage]{preview} \renewcommand{\PreviewBorder}{1in} \newcommand{\Newpage}{\end{preview}\begin{preview}} \usepackage{paralist} %% compact lists \pagestyle{empty} \setlength{\parskip}{1.5ex} \setlength{\parindent}{0pt} \begin{document} \begin{preview} \small \sffamily %s \end{preview} \end{document} """ def latexpng(src, fname): """Convert latex `src` to a PNG image in file `fname`.""" tmpdir = tempfile.mkdtemp(prefix="latexpng-") try: fnametex = os.path.join(tmpdir, "x.tex") writefile(fnametex, LATEXTEMPLATE % src) cmd = ["latex", "-output-directory=%s" % tmpdir, fnametex] rc = subprocess.call(cmd, stdout=subprocess.PIPE) if rc != 0: print "warn: latex failed" fnamedvi = os.path.splitext(fnametex)[0] + ".dvi" cmd = ["dvipng", "-T", "tight", "-z", "9", "-o", fname, fnamedvi] rc = subprocess.call(cmd, stdout=subprocess.PIPE) if rc != 0: print "warn: dvipng failed" finally: shutil.rmtree(tmpdir) def uniformsize(fnames): """Ensure all images given by `fnames` have the same size and a nice frame border. Mainly useful for images generated by :func:`latexpng`. """ maxw, maxh = 0, 0 for fname in fnames: img = Image.open(fname) maxw, maxh = max(maxw, img.size[0]), max(maxh, img.size[1]) for fname in fnames: imold = Image.open(fname) imnew = Image.new("RGB", (maxw+20, maxh+20), "#ffffff") draw = ImageDraw.Draw(imnew) draw.rectangle((5,5, maxw+16, maxh+16), outline="#cccccc") imnew.paste(imold, (10,10)) imnew.save(fname) # ============================================================================= # plotting functions # ============================================================================= def plotprogress(results, opts): """Draw a gallery of plots of randomized iterative validations.""" def plotmask(mask, fname, mprop='mean', ival=None, xoff=1, styles=None, legend='upper left', title=None, xlabel=None, ylabel=None, resultsort=None): """Plot a subset of `results` using `mask` as a positive filter. The values in `mask` are lists of "allowed" result parameters. The plot is stored in `fname`. A legend (if there is one) is placed according to `legend` (see :func:`matplotlib.pyplot.legend` for possible values). Setting this to `None` suppresses a legend box. Lines are drawn according to `styles`, a list of dictionaries, each containing style arguments for :func:`matplotlib.pyplot.plot`. This list is used repeatedly in case there are more lines than styles. """ plots, legends = [], [] if styles is None: styles = [] for ls in ('solid', 'dashed'): for color in ('visio-1', 'visio-2', 'visio-3', 'visio-4', 'visio-5'): styles.append({'linestyle': ls, 'color': COLORS[color]}) xmask = mask.copy() metrics = xmask.pop('metric') # get results and calc metrics presults = tuple(results.filter(xmask)) mresults = [(dict(meta, metric=m), tuple(getattr(pf, m) for pf in pfl)) for m in metrics for meta, pfl, _xinfo in presults] if resultsort: mresults.sort(key=lambda r: resultsort(r[0])) # plot lines (one per metric-result) plt.figure(1, figsize=(11,7)) for i, (meta, mlist) in enumerate(mresults): # get x- and y-values xvals = range(xoff, len(mlist) + xoff) yvals = [getattr(m, mprop) for m in mlist] yvals = smoothvalues(yvals, 7, 5) assert all([y >= 0 and y <= 1 or y is nan for y in yvals]) # plot a line style = styles[i % len(styles)] if styles is not None else {} plots.append(plt.plot(xvals, yvals, **style)) # optionally plot intervals if ival: ivals = [ival(m) for m in mlist] if type(ivals[0]) == tuple: # absolute value (e.g. quartiles) yupper = [x[-1] for x in ivals] ylower = [x[0] for x in ivals] else: # symmetric relative difference to `mprop` yupper = [min(c + x, 1) for c, x in zip(yvals, ivals)] ylower = [max(c - x, 0) for c, x in zip(yvals, ivals)] style = style.copy() style['linestyle'] = 'dotted' plt.plot(xvals, yupper, **style) plt.plot(xvals, ylower, **style) # build legend (combine varying meta values) varmv = [meta[k] for k in meta if len(mask.get(k, "xx")) > 1] varmv = "/".join([str(v) for v in varmv]) legends.append(varmv + ("*" if all(m.binary for m in mlist) else "")) if not plots: # no results for current mask return # finish plt.xlim(xmin=xoff) plt.ylim(-0.05, 1.05) plt.axes().yaxis.grid(color='lightgrey', alpha=0.5, zorder=-1, linestyle="solid") plt.axes().set_axisbelow(True) if legend is not None and [l for l in legends if l]: plt.legend(plots, legends, loc=legend, prop=LEGENDFONT) nrand = len(presults[0][1][0]) if title: plt.title("%s (%d random orders)" % (title, nrand)) if xlabel: plt.xlabel(xlabel) if ylabel: plt.ylabel(ylabel) # save for iext in opts.image: plt.savefig("%s.%s" % (fname, iext), bbox_inches='tight') plt.close() def plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname, ylabeltmpl=None, **kwargs): """Make plots for each result set yielded by `masktmpl`. Individual result sets may further be reduced by `maskupdate`. Plot titles are created by calling `format` on `titletmpl` using the corresponding filter mask. The y-label is set similarly. Plot filenames start with `basefname`. """ for mask in gendicts(masktmpl): title = titletmpl.format(**mask).capitalize() ylabel = ylabeltmpl and ylabeltmpl.format(**mask).capitalize() or None mask = dict((k, [v]) for k, v in mask.items()) # need lists mask.update(maskupdate) fname = mask2fname(mask, opts.dest, basefname) plotmask(mask, fname, title=title, xlabel="Size of training set", ylabel=ylabel, **kwargs) print "plotting progress stats of iterative validations" basemask = { 'order': ['randomized'], 'subject': results._km['subject'], # pylint: disable-msg=W0212 } metrics = ('match', 'submatch', 'hsubmatch', 'accuracy', 'haccuracy', 'strain') confids = ('match', 'mismatch', 'posconf', 'negconf', 'confsep', 'dynconfsuppos', 'dynconfpreneg') # ::: compare learner ::::::::::::::::::::::::::::::::::::::::::::::::::::: print "- learner comparisons" masktmpl = basemask.copy() masktmpl.update({ 'preprocessor': PREPROCESSORS, 'wrapper': WRAPPERS, 'metric': metrics, }) maskupdate = {} titletmpl = "Learner comparison for {wrapper} wrapper" basefname = "progress-baselearners" plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname, ylabeltmpl="{metric}") # ::: compare wrapper :::::::::::::::::::::::::::::::::::::::::::::::::::: print "- wrapper comparisons" masktmpl = basemask.copy() masktmpl.update({ 'preprocessor': PREPROCESSORS, 'learner': LEARNERS, 'metric': metrics, }) maskupdate = {} titletmpl = "Wrapper comparison for {learner}" basefname = "progress-wrappers" plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname, ylabeltmpl="{metric}") # # ::: compare validator :::::::::::::::::::::::::::::::::::::::::::::::::: # # print "- validator comparisons" # # thresholds = ('dynconf', 'minconf', 'hierarchy') + validatornames # valids = sum((("%spreneg" % t, "%ssuppos" % t) for t in thresholds), ()) # # masktmpl = basemask.copy() # masktmpl.update({ # 'preprocessor': PREPROCESSORS, # 'learner': LEARNERS, # 'wrapper': WRAPPERS, # }) # maskupdate = {'metric': valids} # titletmpl = "Validator comparison for {wrapper} {learner}" # basefname = "progress-comparison-validators" # # plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname) # ::: compare metrics ::::::::::::::::::::::::::::::::::::::::::::::::::::: print "- metric comparisons" masktmpl = basemask.copy() masktmpl.update({ 'preprocessor': PREPROCESSORS, 'learner': LEARNERS, 'wrapper': WRAPPERS, }) maskupdate = {'metric': metrics} titletmpl = "Metric comparison for {wrapper} {learner}" basefname = "progress-metrics" plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname) # ::: compare confidence :::::::::::::::::::::::::::::::::::::::::::::::::: print "- confidence comparisons" masktmpl = basemask.copy() masktmpl.update({ 'preprocessor': PREPROCESSORS, 'learner': LEARNERS, 'wrapper': WRAPPERS, }) maskupdate = {'metric': confids} titletmpl = "Confidence comparison for {wrapper} {learner}" basefname = "progress-confidence" resultsort = lambda meta: confids.index(meta['metric']) plotmasktmpl(masktmpl, maskupdate, titletmpl, basefname, ival=lambda m: m.ci, resultsort=resultsort) def plotglobal(results, opts): def plotthresholds(mask, title): """Plot threshold performance barplots for results and threshold-metric yielded by `mask`. """ # ::: get values for each bar ::::::::::::::::::::::::::::::::::::::::: valuesperbar = {} for vld in mask['threshold']: # get results xmask = dict(mask, threshold=[vld]) xresults = tuple(results.filter(xmask)) if not xresults: continue (_meta, pfl, _xinfo), = xresults del pfl[:10] nrand = len(pfl[0]) # calc metrics mpreneg = "%spreneg" % xmask['threshold'][0] msuppos = "%ssuppos" % xmask['threshold'][0] preneg = Metric(Metric(m).mean for m in izip(*(getattr(pf, mpreneg) for pf in pfl))) suppos = Metric(Metric(m).mean for m in izip(*(getattr(pf, msuppos) for pf in pfl))) match = Metric(Metric(m).mean for m in izip(*(pf.match for pf in pfl))) valuesperbar[vld] = ( (1.0, nan, 0), # supneg (preneg.mean, preneg.sd, match.mean), # preneg (match.mean, match.sd, 0), # prepos (suppos.mean, suppos.sd, 0), # suppos ) if not valuesperbar: # (e.g. no randomized order results) return valuesperbar = sorted(valuesperbar.items()) # ::: set up plot config :::::::::::::::::::::::::::::::::::::::::::::: barspec = [ # name color hatch errcolor edgecolor ('supneg', 'visio-1', None, ('visio-1', 0.4), ('visio-1', 0.4)), ('preneg', ('visio-1', 1.2), None, ('visio-1', 0.4), ('visio-1', 0.4)), ('prepos', ('visio-2', 1.2), None, ('visio-2', 0.1), ('visio-2', 0.4)), ('suppos', 'visio-2', None, ('visio-2', 0.4), ('visio-2', 0.4)), ] fname = mask2fname(mask, opts.dest, "validators") if mask['order'] == ['randomized']: ylabel = "Ratio (mean and STDV of %d random orders)" % nrand else: ylabel = "Ratio (original order)" # transpose values to horizontal lists (one for each metric) valuespermetric = zip(*[x[1] for x in valuesperbar]) assert len(valuespermetric) == len(barspec) # ::: plot bars ::::::::::::::::::::::::::::::::::::::::::::::::::::::: barnames = [x[0] for x in valuesperbar] nbars = len(barnames) ind = range(nbars) plots = [] legends = [] width = 0.8 plt.figure(1, figsize=(11,5)) for (mname, color, hatch, errcolor, edgecolor), mvalues in zip(barspec, valuespermetric): assert len(mvalues) == nbars means, errs, bottom = zip(*mvalues) bars = plt.bar(ind, means, yerr=errs, bottom=bottom, linewidth=0.7, hatch=hatch, width=width, color=COLORS[color], ecolor=COLORS[errcolor], edgecolor=COLORS[edgecolor]) plots.append(bars[0]) legends.append(mname) plt.xticks([i + width/2 for i in ind], barnames, rotation=90) plt.ylim(0.0, 1.0) plt.ylabel(ylabel) plt.title(title) plt.legend(plots, legends, loc='lower right', title="Stacked bars", ncol=4, prop=LEGENDFONT) # save for iext in opts.image: plt.savefig("%s.%s" % (fname, iext), bbox_inches='tight') plt.close() def plotboxplots(mask): """Plot global metrics for results and metric yielded by `mask`.""" mname = mask['metric'][0] metricsperlearner = {} for (lname, wname, pname) in product(LEARNERS, WRAPPERS, PREPROCESSORS): # get results xmask = dict(mask, order=['original', 'randomized'], learner=[lname], wrapper=[wname], preprocessor=[pname]) xresults = tuple(results.filter(xmask)) if len(xresults) < 2: continue # no randomized order results (_meta, origopfl, _xinfo), (_meta, randopfl, _xinfo) = xresults del origopfl[:10] del randopfl[:10] # calc metrics (get means by transposing per-istep lists to # per-order lists) rmeans = Metric((Metric(m).mean for m in izip(*(getattr(pf, mname) for pf in randopfl)))) omeans = Metric((Metric(m).mean for m in izip(*(getattr(pf, mname) for pf in origopfl)))) assert rmeans.n == len(randopfl[0]) assert omeans.n == len(origopfl[0]) learner = " - ".join((pname.split("-")[-1], wname, lname)) metricsperlearner[learner] = rmeans, omeans if not metricsperlearner: return # nothing to plot # preparations skey = lambda x: (x[1][0].mean, x[1][0].sd) metricsperlearner = sorted(metricsperlearner.iteritems(), key=skey) xnames, xvalues = zip(*metricsperlearner) xrando, xorigo = zip(*xvalues) nrand = len(xrando[0]) ind = range(1, len(xvalues) + 1) fname = mask2fname(mask, opts.dest, "learners") # boxplots plt.figure(1, figsize=(11,5)) bplots = plt.boxplot(xrando, sym="+", notch=0) plt.setp(bplots['boxes'], color=COLORS['visio-3']) plt.setp(bplots['caps'], color=COLORS['visio-3']) plt.setp(bplots['whiskers'], color=COLORS['visio-3'], linestyle="solid") plt.setp(bplots['medians'], color=COLORS['black'], solid_capstyle='butt', marker='') plt.setp(bplots['fliers'], color=COLORS['visio-1']) # add means and more randmeanplot = plt.plot(ind, [x.mean for x in xrando], marker=".", color=COLORS['black'], linestyle='') origmeanplot = plt.plot(ind, [x.mean for x in xorigo], marker=".", color=COLORS['visio-2'], linestyle='') #plt.plot(ind, [(x.mean + x.sd) for x in xvalues], marker="_", color=COLORS['visio-1', 0.4], markeredgewidth=1, linestyle='') #plt.plot(ind, [(x.mean - x.sd) for x in xvalues], marker="_", color=COLORS['visio-1', 0.4], markeredgewidth=1, linestyle='') # finish plt.ylim(0.0, 1.0) plt.xticks(ind, xnames, rotation=90) plt.title("Overall performance for original and %s random situation " "orders" % nrand) mlabel = ('%s ratio' if mname in ('match',) else 'mean %s') % mname plt.ylabel("%s (one value per situation order)" % mlabel.capitalize()) plt.axes().yaxis.grid(color='lightgrey', alpha=0.5, zorder=-1, linestyle="solid") plt.axes().set_axisbelow(True) legend = ( (bplots['boxes'][0], 'IQR and highest/lowest value within 1.5 IQR'), (bplots['medians'][0], 'median of values'), (randmeanplot, 'mean of values'), (origmeanplot, 'value for original situation order'), (bplots['fliers'][0], 'outlier values (outside 1.5 IQR)'), ) legloc = 'upper right' if mname == 'strain' else 'lower right' plt.legend(*zip(*legend), numpoints=1, loc=legloc, prop=LEGENDFONT, handlelength=1) for iext in opts.image: plt.savefig("%s.%s" % (fname, iext), bbox_inches='tight') plt.close() basemask = { 'subject': results._km['subject'], # pylint: disable-msg=W0212 } print "plotting global decision threshold barplots" thresholds = ('dynconf', 'minconf', 'hierarchy') + validatorids masktemplate = dict(basemask, order=['randomized', 'original'], preprocessor=PREPROCESSORS, learner=LEARNERS, wrapper=WRAPPERS, ) for mask in gendicts(masktemplate): title = ("Predictions supported and prevented by validators (%(order)s" " order, %(preprocessor)s/%(wrapper)s/%(learner)s)" % mask) mask = dict((k, [v]) for k, v in mask.items()) # need lists mask['threshold'] = sorted(thresholds) plotthresholds(mask, title) print "plotting global metric boxplots" masktemplate = dict(basemask, metric=['match', 'submatch', 'hsubmatch', 'accuracy', 'haccuracy', 'strain']) for mask in gendicts(masktemplate): mask = dict((k, [v]) for k, v in mask.items()) # need lists plotboxplots(mask) def plotruntime(results, opts): print "plotting runtimes" masktemplate = dict( subject=results._km['subject'], # pylint: disable-msg=W0212 order=['original'], # same runtime information for original and random ) for mask in gendicts(masktemplate): mask = dict((k, [v]) for k, v in mask.items()) runtimesperaction = {} for meta, _pfl, extra in results.filter(mask): #for pp, w, l in sorted(product(PREPROCESSORS, WRAPPERS, LEARNERS)): learner = " - ".join(meta[x] for x in ('preprocessor', 'wrapper', 'learner')) for rt, rtvl in extra['runtimes'].items(): perlearner = runtimesperaction.setdefault(rt, {}) perlearner[learner] = rtvl #print runtimesperaction del mask['order'] # invariant for rt, perlearner in runtimesperaction.items(): # plot file name fmask = dict(mask, action=[rt]) fname = mask2fname(fmask, opts.dest, "runtimes") # values and sorting skey = lambda x: (stats.median(x[1]), stats.mean(x[1])) xnames, xvalues = zip(*sorted(perlearner.items(), key=skey)) ind = range(1, len(xvalues) + 1) maxrt = max(tuple(chain(*xvalues))) if maxrt < 1: xvalues = [[x * 1000 for x in vl] for vl in xvalues] unit = "milliseconds" else: unit = "seconds" # boxplots plt.figure(1, figsize=(11,5)) bplots = plt.boxplot(xvalues, sym="+", notch=0) plt.setp(bplots['boxes'], color=COLORS['visio-3']) plt.setp(bplots['caps'], color=COLORS['visio-3']) plt.setp(bplots['whiskers'], color=COLORS['visio-3'], linestyle="solid") plt.setp(bplots['medians'], color=COLORS['black'], solid_capstyle='butt', marker='') plt.setp(bplots['fliers'], color=COLORS['visio-1']) # add means and more meanplot = plt.plot(ind, [stats.mean(x) for x in xvalues], marker=".", color=COLORS['black'], linestyle='') # finish #plt.ylim(0.0, 1.0) plt.xticks(ind, xnames, rotation=90) plt.title("Runtime: %s" % rt) plt.ylabel("time in %s" % unit) plt.axes().yaxis.grid(color='lightgrey', alpha=0.5, zorder=-1, linestyle="solid") plt.axes().set_axisbelow(True) legend = ( (bplots['boxes'][0], 'IQR and highest/lowest value within 1.5 IQR'), (bplots['medians'][0], 'median of values'), (meanplot, 'mean of values'), (bplots['fliers'][0], 'outlier values (outside 1.5 IQR)'), ) legloc = 'upper left' plt.legend(*zip(*legend), numpoints=1, loc=legloc, prop=LEGENDFONT, handlelength=1) for iext in opts.image: plt.savefig("%s.%s" % (fname, iext), bbox_inches='tight') plt.close() def extrainfo(results, opts): print "extra info" mask = {'order': ['original']} fnames = dict((k, []) for k in ( ('params', 'learner-configuration'), ('stats', 'learner-statistics'), )) for meta, _pfl, extra in results.filter(mask): for (xname, xsec), xfnames in fnames.items(): sec = "%(preprocessor)s - %(wrapper)s - %(learner)s - %(subject)s" % meta tex = [r"\textbf{%s}" % sec, "", r"\begin{compactdesc}"] for k, v in sorted(extra.get(xname, {}).items() or [("None", None)]): if k in ('pfeatures',): # scenario-specific parameters continue k = k.replace('_', '-') v = str(v) v = v.replace('_', '-') if v else r"$\,$" tex.append(r"\item[%s:] \texttt{%s}" % (k, v)) tex.append(r"\end{compactdesc}") fmask = dict((k, [v]) for k, v in meta.items()) del fmask['order'] fname = mask2fname(fmask, opts.dest, xsec) + ".png" try: latexpng("\n".join(tex), fname) except OSError: print("warn: `latex` or `dvipng` not found, cannot render learner configs") break xfnames.append(fname) for fnl in fnames.values(): uniformsize(fnl) # ============================================================================= # command line interface # ============================================================================= def options(): op = optparse.OptionParser("%prog -r FILE -d DIR") op.add_option("-d", "--dest", metavar="DIR", help="destination directory to put plot images into") op.add_option("-r", "--results", metavar="FILE", help="results to plot (generated by evaluation script)") op.add_option("-i", "--image", metavar="EXT", default="png", help="plot image types (comma separated, default: png)") opts, args = op.parse_args() if args: op.error("invalid arguments") if opts.dest is None: op.error("need a destination for plot files") if opts.results is None: op.error("need a file to read results from") opts.image = opts.image.split(",") return opts def main(): opts = options() results = ResultDB(opts.results) extrainfo(results, opts) plotruntime(results, opts) plotglobal(results, opts) plotprogress(results, opts) results.close() if __name__ == '__main__': main() # ============================================================================= # tests # ============================================================================= def __doctests_colmap(): """ >>> colmap._lscale("#8ae234", 1.0) '#8ae234' >>> colmap._lscale("#8ae234", 0.8) '#6ec31c' >>> colmap._lscale("#8ae234", 1.2) '#a6e965' >>> colmap._lscale("#8ae234", 0.0) '#000000' >>> colmap._lscale("#8ae234", 100.0) '#ffffff' >>> c = colmap(a="#8ae234") >>> c['a'] '#8ae234' >>> c['a', 1.2] '#a6e965' >>> c['x-y'] = "#aaccee" >>> c['x-y'] '#aaccee' >>> c['x-y', 0.55] '#2570bb' >>> c['x-z', 0.55] # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): KeyError: 'x-z' >>> c['x-y', "foo"] # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): TypeError: ... """