📚 The CoCalc Library - books, templates and other resources
License: OTHER
"""This file contains code for use with "Think Stats",1by Allen B. Downey, available from greenteapress.com23Copyright 2014 Allen B. Downey4License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html5"""67from __future__ import print_function89import logging10import math11import matplotlib12import matplotlib.pyplot as pyplot13import numpy as np14import pandas1516# customize some matplotlib attributes17#matplotlib.rc('figure', figsize=(4, 3))1819#matplotlib.rc('font', size=14.0)20#matplotlib.rc('axes', labelsize=22.0, titlesize=22.0)21#matplotlib.rc('legend', fontsize=20.0)2223#matplotlib.rc('xtick.major', size=6.0)24#matplotlib.rc('xtick.minor', size=3.0)2526#matplotlib.rc('ytick.major', size=6.0)27#matplotlib.rc('ytick.minor', size=3.0)282930class _Brewer(object):31"""Encapsulates a nice sequence of colors.3233Shades of blue that look good in color and can be distinguished34in grayscale (up to a point).3536Borrowed from http://colorbrewer2.org/37"""38color_iter = None3940colors = ['#081D58',41'#253494',42'#225EA8',43'#1D91C0',44'#41B6C4',45'#7FCDBB',46'#C7E9B4',47'#EDF8B1',48'#FFFFD9']4950# lists that indicate which colors to use depending on how many are used51which_colors = [[],52[1],53[1, 3],54[0, 2, 4],55[0, 2, 4, 6],56[0, 2, 3, 5, 6],57[0, 2, 3, 4, 5, 6],58[0, 1, 2, 3, 4, 5, 6],59]6061@classmethod62def Colors(cls):63"""Returns the list of colors.64"""65return cls.colors6667@classmethod68def ColorGenerator(cls, n):69"""Returns an iterator of color strings.7071n: how many colors will be used72"""73for i in cls.which_colors[n]:74yield cls.colors[i]75raise StopIteration('Ran out of colors in _Brewer.ColorGenerator')7677@classmethod78def InitializeIter(cls, num):79"""Initializes the color iterator with the given number of colors."""80cls.color_iter = cls.ColorGenerator(num)8182@classmethod83def ClearIter(cls):84"""Sets the color iterator to None."""85cls.color_iter = None8687@classmethod88def GetIter(cls):89"""Gets the color iterator."""90if cls.color_iter is None:91cls.InitializeIter(7)9293return cls.color_iter949596def PrePlot(num=None, rows=None, cols=None):97"""Takes hints about what's coming.9899num: number of lines that will be plotted100rows: number of rows of subplots101cols: number of columns of subplots102"""103if num:104_Brewer.InitializeIter(num)105106if rows is None and cols is None:107return108109if rows is not None and cols is None:110cols = 1111112if cols is not None and rows is None:113rows = 1114115# resize the image, depending on the number of rows and cols116size_map = {(1, 1): (8, 6),117(1, 2): (14, 6),118(1, 3): (14, 6),119(2, 2): (10, 10),120(2, 3): (16, 10),121(3, 1): (8, 10),122}123124if (rows, cols) in size_map:125fig = pyplot.gcf()126fig.set_size_inches(*size_map[rows, cols])127128# create the first subplot129if rows > 1 or cols > 1:130pyplot.subplot(rows, cols, 1)131global SUBPLOT_ROWS, SUBPLOT_COLS132SUBPLOT_ROWS = rows133SUBPLOT_COLS = cols134135136def SubPlot(plot_number, rows=None, cols=None):137"""Configures the number of subplots and changes the current plot.138139rows: int140cols: int141plot_number: int142"""143rows = rows or SUBPLOT_ROWS144cols = cols or SUBPLOT_COLS145pyplot.subplot(rows, cols, plot_number)146147148def _Underride(d, **options):149"""Add key-value pairs to d only if key is not in d.150151If d is None, create a new dictionary.152153d: dictionary154options: keyword args to add to d155"""156if d is None:157d = {}158159for key, val in options.items():160d.setdefault(key, val)161162return d163164165def Clf():166"""Clears the figure and any hints that have been set."""167_Brewer.ClearIter()168pyplot.clf()169fig = pyplot.gcf()170fig.set_size_inches(8, 6)171172173def Figure(**options):174"""Sets options for the current figure."""175_Underride(options, figsize=(6, 8))176pyplot.figure(**options)177178179def _UnderrideColor(options):180if 'color' in options:181return options182183color_iter = _Brewer.GetIter()184185if color_iter:186try:187options['color'] = next(color_iter)188except StopIteration:189print('Warning: Brewer ran out of colors.')190_Brewer.ClearIter()191return options192193194def Plot(obj, ys=None, style='', **options):195"""Plots a line.196197Args:198obj: sequence of x values, or Series, or anything with Render()199ys: sequence of y values200style: style string passed along to pyplot.plot201options: keyword args passed to pyplot.plot202"""203options = _UnderrideColor(options)204label = getattr(obj, 'name', '_nolegend_')205options = _Underride(options, linewidth=3, alpha=0.8, label=label)206207xs = obj208if ys is None:209if hasattr(obj, 'Render'):210xs, ys = obj.Render()211if isinstance(obj, pandas.Series):212ys = obj.values213xs = obj.index214215if ys is None:216pyplot.plot(xs, style, **options)217else:218pyplot.plot(xs, ys, style, **options)219220221def FillBetween(xs, y1, y2=None, where=None, **options):222"""Plots a line.223224Args:225xs: sequence of x values226y1: sequence of y values227y2: sequence of y values228where: sequence of boolean229options: keyword args passed to pyplot.fill_between230"""231options = _UnderrideColor(options)232options = _Underride(options, linewidth=0, alpha=0.5)233pyplot.fill_between(xs, y1, y2, where, **options)234235236def Bar(xs, ys, **options):237"""Plots a line.238239Args:240xs: sequence of x values241ys: sequence of y values242options: keyword args passed to pyplot.bar243"""244options = _UnderrideColor(options)245options = _Underride(options, linewidth=0, alpha=0.6)246pyplot.bar(xs, ys, **options)247248249def Scatter(xs, ys=None, **options):250"""Makes a scatter plot.251252xs: x values253ys: y values254options: options passed to pyplot.scatter255"""256options = _Underride(options, color='blue', alpha=0.2,257s=30, edgecolors='none')258259if ys is None and isinstance(xs, pandas.Series):260ys = xs.values261xs = xs.index262263pyplot.scatter(xs, ys, **options)264265266def HexBin(xs, ys, **options):267"""Makes a scatter plot.268269xs: x values270ys: y values271options: options passed to pyplot.scatter272"""273options = _Underride(options, cmap=matplotlib.cm.Blues)274pyplot.hexbin(xs, ys, **options)275276277def Pdf(pdf, **options):278"""Plots a Pdf, Pmf, or Hist as a line.279280Args:281pdf: Pdf, Pmf, or Hist object282options: keyword args passed to pyplot.plot283"""284low, high = options.pop('low', None), options.pop('high', None)285n = options.pop('n', 101)286xs, ps = pdf.Render(low=low, high=high, n=n)287options = _Underride(options, label=pdf.name)288Plot(xs, ps, **options)289290291def Pdfs(pdfs, **options):292"""Plots a sequence of PDFs.293294Options are passed along for all PDFs. If you want different295options for each pdf, make multiple calls to Pdf.296297Args:298pdfs: sequence of PDF objects299options: keyword args passed to pyplot.plot300"""301for pdf in pdfs:302Pdf(pdf, **options)303304305def Hist(hist, **options):306"""Plots a Pmf or Hist with a bar plot.307308The default width of the bars is based on the minimum difference309between values in the Hist. If that's too small, you can override310it by providing a width keyword argument, in the same units311as the values.312313Args:314hist: Hist or Pmf object315options: keyword args passed to pyplot.bar316"""317# find the minimum distance between adjacent values318xs, ys = hist.Render()319320if 'width' not in options:321try:322options['width'] = 0.9 * np.diff(xs).min()323except TypeError:324logging.warning("Hist: Can't compute bar width automatically."325"Check for non-numeric types in Hist."326"Or try providing width option."327)328329options = _Underride(options, label=hist.name)330options = _Underride(options, align='center')331if options['align'] == 'left':332options['align'] = 'edge'333elif options['align'] == 'right':334options['align'] = 'edge'335options['width'] *= -1336337Bar(xs, ys, **options)338339340def Hists(hists, **options):341"""Plots two histograms as interleaved bar plots.342343Options are passed along for all PMFs. If you want different344options for each pmf, make multiple calls to Pmf.345346Args:347hists: list of two Hist or Pmf objects348options: keyword args passed to pyplot.plot349"""350for hist in hists:351Hist(hist, **options)352353354def Pmf(pmf, **options):355"""Plots a Pmf or Hist as a line.356357Args:358pmf: Hist or Pmf object359options: keyword args passed to pyplot.plot360"""361xs, ys = pmf.Render()362low, high = min(xs), max(xs)363364width = options.pop('width', None)365if width is None:366try:367width = np.diff(xs).min()368except TypeError:369logging.warning("Pmf: Can't compute bar width automatically."370"Check for non-numeric types in Pmf."371"Or try providing width option.")372points = []373374lastx = np.nan375lasty = 0376for x, y in zip(xs, ys):377if (x - lastx) > 1e-5:378points.append((lastx, 0))379points.append((x, 0))380381points.append((x, lasty))382points.append((x, y))383points.append((x+width, y))384385lastx = x + width386lasty = y387points.append((lastx, 0))388pxs, pys = zip(*points)389390align = options.pop('align', 'center')391if align == 'center':392pxs = np.array(pxs) - width/2.0393if align == 'right':394pxs = np.array(pxs) - width395396options = _Underride(options, label=pmf.name)397Plot(pxs, pys, **options)398399400def Pmfs(pmfs, **options):401"""Plots a sequence of PMFs.402403Options are passed along for all PMFs. If you want different404options for each pmf, make multiple calls to Pmf.405406Args:407pmfs: sequence of PMF objects408options: keyword args passed to pyplot.plot409"""410for pmf in pmfs:411Pmf(pmf, **options)412413414def Diff(t):415"""Compute the differences between adjacent elements in a sequence.416417Args:418t: sequence of number419420Returns:421sequence of differences (length one less than t)422"""423diffs = [t[i+1] - t[i] for i in range(len(t)-1)]424return diffs425426427def Cdf(cdf, complement=False, transform=None, **options):428"""Plots a CDF as a line.429430Args:431cdf: Cdf object432complement: boolean, whether to plot the complementary CDF433transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel'434options: keyword args passed to pyplot.plot435436Returns:437dictionary with the scale options that should be passed to438Config, Show or Save.439"""440xs, ps = cdf.Render()441xs = np.asarray(xs)442ps = np.asarray(ps)443444scale = dict(xscale='linear', yscale='linear')445446for s in ['xscale', 'yscale']:447if s in options:448scale[s] = options.pop(s)449450if transform == 'exponential':451complement = True452scale['yscale'] = 'log'453454if transform == 'pareto':455complement = True456scale['yscale'] = 'log'457scale['xscale'] = 'log'458459if complement:460ps = [1.0-p for p in ps]461462if transform == 'weibull':463xs = np.delete(xs, -1)464ps = np.delete(ps, -1)465ps = [-math.log(1.0-p) for p in ps]466scale['xscale'] = 'log'467scale['yscale'] = 'log'468469if transform == 'gumbel':470xs = xp.delete(xs, 0)471ps = np.delete(ps, 0)472ps = [-math.log(p) for p in ps]473scale['yscale'] = 'log'474475options = _Underride(options, label=cdf.name)476Plot(xs, ps, **options)477return scale478479480def Cdfs(cdfs, complement=False, transform=None, **options):481"""Plots a sequence of CDFs.482483cdfs: sequence of CDF objects484complement: boolean, whether to plot the complementary CDF485transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel'486options: keyword args passed to pyplot.plot487"""488for cdf in cdfs:489Cdf(cdf, complement, transform, **options)490491492def Contour(obj, pcolor=False, contour=True, imshow=False, **options):493"""Makes a contour plot.494495d: map from (x, y) to z, or object that provides GetDict496pcolor: boolean, whether to make a pseudocolor plot497contour: boolean, whether to make a contour plot498imshow: boolean, whether to use pyplot.imshow499options: keyword args passed to pyplot.pcolor and/or pyplot.contour500"""501try:502d = obj.GetDict()503except AttributeError:504d = obj505506_Underride(options, linewidth=3, cmap=matplotlib.cm.Blues)507508xs, ys = zip(*d.keys())509xs = sorted(set(xs))510ys = sorted(set(ys))511512X, Y = np.meshgrid(xs, ys)513func = lambda x, y: d.get((x, y), 0)514func = np.vectorize(func)515Z = func(X, Y)516517x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)518axes = pyplot.gca()519axes.xaxis.set_major_formatter(x_formatter)520521if pcolor:522pyplot.pcolormesh(X, Y, Z, **options)523if contour:524cs = pyplot.contour(X, Y, Z, **options)525pyplot.clabel(cs, inline=1, fontsize=10)526if imshow:527extent = xs[0], xs[-1], ys[0], ys[-1]528pyplot.imshow(Z, extent=extent, **options)529530531def Pcolor(xs, ys, zs, pcolor=True, contour=False, **options):532"""Makes a pseudocolor plot.533534xs:535ys:536zs:537pcolor: boolean, whether to make a pseudocolor plot538contour: boolean, whether to make a contour plot539options: keyword args passed to pyplot.pcolor and/or pyplot.contour540"""541_Underride(options, linewidth=3, cmap=matplotlib.cm.Blues)542543X, Y = np.meshgrid(xs, ys)544Z = zs545546x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)547axes = pyplot.gca()548axes.xaxis.set_major_formatter(x_formatter)549550if pcolor:551pyplot.pcolormesh(X, Y, Z, **options)552553if contour:554cs = pyplot.contour(X, Y, Z, **options)555pyplot.clabel(cs, inline=1, fontsize=10)556557558def Text(x, y, s, **options):559"""Puts text in a figure.560561x: number562y: number563s: string564options: keyword args passed to pyplot.text565"""566options = _Underride(options, verticalalignment='top',567horizontalalignment='left')568pyplot.text(x, y, s, **options)569570571def Config(**options):572"""Configures the plot.573574Pulls options out of the option dictionary and passes them to575the corresponding pyplot functions.576"""577names = ['title', 'xlabel', 'ylabel', 'xscale', 'yscale',578'xticks', 'yticks', 'axis', 'xlim', 'ylim']579580for name in names:581if name in options:582getattr(pyplot, name)(options[name])583584# looks like this is not necessary: matplotlib understands text loc specs585loc_dict = {'upper right': 1,586'upper left': 2,587'lower left': 3,588'lower right': 4,589'right': 5,590'center left': 6,591'center right': 7,592'lower center': 8,593'upper center': 9,594'center': 10,595}596597loc = options.get('loc', 0)598#loc = loc_dict.get(loc, loc)599600legend = options.get('legend', True)601if legend:602pyplot.legend(loc=loc)603604605def Show(**options):606"""Shows the plot.607608For options, see Config.609610options: keyword args used to invoke various pyplot functions611"""612clf = options.pop('clf', True)613Config(**options)614pyplot.show()615if clf:616Clf()617618619def Plotly(**options):620"""Shows the plot.621622For options, see Config.623624options: keyword args used to invoke various pyplot functions625"""626clf = options.pop('clf', True)627Config(**options)628import plotly.plotly as plotly629url = plotly.plot_mpl(pyplot.gcf())630if clf:631Clf()632return url633634635def Save(root=None, formats=None, **options):636"""Saves the plot in the given formats and clears the figure.637638For options, see Config.639640Args:641root: string filename root642formats: list of string formats643options: keyword args used to invoke various pyplot functions644"""645clf = options.pop('clf', True)646Config(**options)647648if formats is None:649formats = ['pdf', 'eps']650651try:652formats.remove('plotly')653Plotly(clf=False)654except ValueError:655pass656657if root:658for fmt in formats:659SaveFormat(root, fmt)660if clf:661Clf()662663664def SaveFormat(root, fmt='eps'):665"""Writes the current figure to a file in the given format.666667Args:668root: string filename root669fmt: string format670"""671filename = '%s.%s' % (root, fmt)672print('Writing', filename)673pyplot.savefig(filename, format=fmt, dpi=300)674675676# provide aliases for calling functons with lower-case names677preplot = PrePlot678subplot = SubPlot679clf = Clf680figure = Figure681plot = Plot682scatter = Scatter683pmf = Pmf684pmfs = Pmfs685hist = Hist686hists = Hists687diff = Diff688cdf = Cdf689cdfs = Cdfs690contour = Contour691pcolor = Pcolor692config = Config693show = Show694save = Save695696697def main():698color_iter = _Brewer.ColorGenerator(7)699for color in color_iter:700print(color)701702703if __name__ == '__main__':704main()705706707