class Plot: def __init__(self): self._made = False self._data = [] self._boxes = [] self._enlarge = (0.02, 0.02, 0.02, 0.02) self._grid = True self._xscale = 'linear' self._yscale = 'linear' self._leg = False self._xaxis = None self._yaxis = None self._fitcount = 0 def xaxis(self, q): self._made = False if not isinstance(q, Quantity): raise TypeError("xaxis argument must be Quantity!") self._xaxis = Quantity(q.sprefunit()) self._xaxis.label = q.label self._xaxis.latex = q.latex self._xaxis.symbol = q.symbol return self def yaxis(self, q): self._made = False if not isinstance(q, Quantity): raise TypeError("yaxis argument must be Quantity!") self._yaxis = Quantity(q.sprefunit()) self._yaxis.label = q.label self._yaxis.latex = q.latex self._yaxis.symbol = q.symbol return self def makex(self): if self._xaxis is None: return '' lab = [] if self._xaxis.label: lab.append(self._xaxis.label) sl = self._xaxis.latex or self._xaxis.symbol if sl: lab.append("${}$".format(sl)) punit = self._xaxis.sprefunit(latex=True) if punit: lab.append('in ${}$'.format(punit)) return ' '.join(lab) def makey(self): if self._yaxis is None: return '' lab = [] if self._yaxis.label: lab.append(self._yaxis.label) sl = self._yaxis.latex or self._yaxis.symbol if sl: lab.append("${}$".format(sl)) punit = self._yaxis.sprefunit(latex=True) if punit: lab.append('in ${}$'.format(punit)) return ' '.join(lab) """ def xunit(self, s): self._made = False if self._xaxis is None: self._xaxis = Quantity() q = Quantity(s) self._xaxis.preferredUnit = q.preferredUnit self._xaxis.value = q.value self._xaxis.uvec = q.uvec return self def yunit(self, s): self._made = False if self._yaxis is None: self._yaxis = Quantity() q = Quantity(s) self._yaxis.preferredUnit = q.preferredUnit self._yaxis.value = q.value self._yaxis.uvec = q.uvec return self""" def xlabel(self, s, symbol=None, latex=None): self._made = False if self._xaxis is None: self._xaxis = Quantity() self._xaxis.label = s if symbol is not None: self._xaxis.symbol = symbol if latex is not None: self._yaxis.latex = latex return self def ylabel(self, s): self._made = False if self._yaxis is None: self._yaxis = Quantity() self._yaxis.label = s if symbol is not None: self._xaxis.symbol = symbol if latex is not None: self._yaxis.latex = latex return self def legend(self, on=True): self._made = False self._leg = on return self def data(self, x, y, fmt, errorbar, label=True, **kwds): self._made = False if label == True: kwds['label'] = y.label elif label is not None: kwds['label'] = label self._data.append((x, y, fmt, errorbar, kwds)) if isinstance(x, Quantity): if self._xaxis is None: self.xaxis(x) if isinstance(y, Quantity): if self._yaxis is None: self.yaxis(y) return self def error(self, x, y, fmt='.k', markersize=5, capsize=0, label=True, ecolor='0.3', **kwds): kwds['capsize'] = capsize self._made = False self.data(x, y, fmt=fmt, markersize=markersize, label=label, ecolor=ecolor, errorbar=True, **kwds) return self # cascade def points(self, x, y, fmt='.k', markersize=3, label=True, **kwds): self._made = False self.data(x, y, fmt=fmt, markersize=markersize, label=label, errorbar=False, **kwds) return self # cascade def line(self, x, y, fmt='-b', linewidth=1, label=True, **kwds): self._made = False self.data(x, y, fmt=fmt, linewidth=linewidth, label=label, errorbar=False, **kwds) return self # cascade def histoline(self, x, y, *args, **kwds): l = len(x) doubledX = np.zeros(2 * l) doubledY = np.zeros(2 * l) for i in range(0, l): doubledY[2 * i] = y.value[i] doubledY[2 * i + 1] = y.value[i] deltaX = x.value[1] - x.value[0] doubledX[0] = x.value[0] - deltaX / 2 for i in range(0, l - 1): doubledX[2 * i + 1] = (x.value[i] + x.value[i + 1]) / 2 doubledX[2 * i + 2] = (x.value[i] + x.value[i + 1]) / 2 deltaX = x.value[-1] - x.value[-2] doubledX[-1] = x.value[-1] + deltaX / 2 doubledX = [doubledX[0]] + list(doubledX) + [doubledX[-1]] doubledY = [0] + list(doubledY) + [0] nx = Quantity(doubledX, 0, x.uvec) nx.name(x.label, x.symbol, x.latex) nx.preferredUnit = x.preferredUnit ny = Quantity(doubledY, 0, y.uvec) ny.name(y.label, y.symbol, y.latex) ny.preferredUnit = y.preferredUnit return self.line(nx, ny, *args, **kwds) fitcolors = 'brgcmy' def fit(self, mf, box=True, bpos=None, xmin=None, xmax=None, chi2=True): if not isinstance(mf, ModelFit): raise TypeError( "Argument of Plot.fit must be a ModelFit, but {} given.". format(type(mf))) self._fitcount += 1 text = 'Fit: ${}$\n\n'.format(mf.eq()) if mf.sr >= 3: text += 'fit failed ({})!\n'.format(mf.sr) for p in mf.parameters: text += ' ${}$\n'.format(p.tex()) if chi2: text += ' $\chi^2/\mathrm{ndf} = ' + ('{:.2f} / {}$'.format( *mf.chi)) xmin = xmin or min(mf.xo.value) xmax = xmax or max(mf.xo.value) if box: self.box((self._fitcount, text, mf.xo, mf.yo, bpos)) x = Quantity(np.linspace(xmin, xmax, 200), unit=mf.xo.uvec) p0 = [Quantity(p.value, 0, p.uvec) for p in mf.parameters] self.line(x, mf.func(x, *p0), fmt='-' + Plot.fitcolors[(self._fitcount - 1) % 6]) return self def box(self, text): self._made = False self._boxes.append(text) return self def xlog(self, log=True): self._made = False self._xscale = 'log' if log else 'linear' return self def ylog(self, log=True): self._made = False self._yscale = 'log' if log else 'linear' return self def save(self, filename, **kwds): if not self._made: self.make() # dpi not set here anymore # do this in a matlibplotrc file, e.g. # figure.dpi : 300 plt.tight_layout() plt.savefig(filename, **kwds) return self def show(self): if not self._made: self.make() plt.show() return self def make(self, clf=True, fig=None, axes=None): if clf: plt.clf() if fig is None and axes is None: fig = plt.figure() if axes is None: axes = fig.add_subplot(111) xlim = None ylim = None for x, y, fmt, errorbar, d in self._data: if isinstance(x, Quantity): f = Quantity(unit=x.uvec) / self._xaxis if not f.unitless(): warnings.warn( 'The ratio of x axis unit ({}) and real unit ({}) must be unit less.' .format(self._xaxis.siunit(), y.siunit())) f = float(f) sx = x.stddev() * f x = x.value * f else: #f = float(1 / Quantity(self._xunit)) #x = x * f sx = 0 if isinstance(y, Quantity): f = Quantity(unit=y.uvec) / self._yaxis if not f.unitless(): warnings.warn( 'The ratio of preferred unit ({}) and real unit ({}) must be unit less.' .format(self._yaxis.siunit(), y.siunit())) f = float(f) sy = y.stddev() * f y = y.value * f else: #f = float(1 / Quantity(self.ypreferredUnit)) #y = y * f sy = 0 xmin = np.nanmin(x - sx) xmax = np.nanmax(x + sx) ymin = np.nanmin(y - sy) ymax = np.nanmax(y + sy) if xlim is None: xlim = [xmin, xmax] if ylim is None: ylim = [ymin, ymax] xlim[0] = min(xlim[0], xmin) xlim[1] = max(xlim[1], xmax) ylim[0] = min(ylim[0], ymin) ylim[1] = max(ylim[1], ymax) xlimraw = xlim ylimraw = ylim xerr = sx yerr = sy if errorbar: axes.errorbar(x, y, sy, sx, fmt=fmt, **d) else: axes.plot(x, y, fmt, **d) xdif = xlim[1] - xlim[0] ydif = ylim[1] - ylim[0] xlim[0] -= xdif * self._enlarge[0] xlim[1] += xdif * self._enlarge[1] ylim[0] -= ydif * self._enlarge[2] ylim[1] += ydif * self._enlarge[3] i = 0 taken = [[0] * 2, [0] * 2, [0] * 2] for b in self._boxes: if isinstance(b, tuple): fitnum, b, x, y, bpos = b if isinstance(bpos, tuple): x = bpos[0] / 3 y = bpos[1] / 2 else: penalty = [] for x in range(3): for y in range(2): points = 0 total = 0 for dx, dy, fmt, errorbar, d in self._data: fx = float(Quantity(unit=dx.uvec) / self._xaxis) fy = float(Quantity(unit=dy.uvec) / self._yaxis) idx1 = (dx.value * fx > (xlim[0] + xdif * (x / 3 - 0.1))) idx2 = (dx.value * fx < (xlim[0] + xdif * (x + 1.3) / 3)) idx3 = (dy.value * fy > (ylim[0] + ydif * (y / 2 - 0.1))) idx4 = (dy.value * fy < (ylim[0] + ydif * (y + 1.2) / 2)) idx = idx1 * idx2 * idx3 * idx4 points += len(dx.value[idx]) / len(dx.value) total = len(dx.value) points += total * taken[x][y] penalty.append((x, y, points)) x, y, points = min(penalty, key=lambda y: y[2]) taken[x][y] += 1 if y == 0: yalign = 'bottom' y = ylimraw[0] + ydif * 0.05 elif y == 1: yalign = 'top' y = ylimraw[1] - ydif * 0.05 if x == 0: xalign = 'left' x = xlimraw[0] + xdif * 0.05 elif x == 1: xalign = 'center' x = xlimraw[0] + 0.5 * xdif elif x == 2: xalign = 'right' x = xlimraw[1] - xdif * 0.05 if self._fitcount > 1 and False: b = '({}) {}'.format(fitnum, b) fx = Quantity(unit=x.uvec) / self._xaxis fy = Quantity(unit=y.uvec) / self._yaxis if not fx.unitless(): warnings.warn( 'The ratio of preferred unit ({}) and real unit ({}) must be unit less.' .format(self._xaxis.siunit(), x.siunit())) if not fy.unitless(): warnings.warn( 'The ratio of preferred unit ({}) and real unit ({}) must be unit less.' .format(self._yaxis.siunit(), y.siunit())) fx = float(fx) fy = float(fy) tx = x.value * fx ty = y.value * fy tx = (min(tx) + max(tx)) / 2 ty = max(ty) ty += 0.05 * ydif axes.text(tx, ty, '({})'.format(fitnum), color=Plot.fitcolors[(fitnum - 1) % 6], horizontalalignment='center', verticalalignment='bottom') #plt.figtext(*xy, s=b,bbox=dict(facecolor='w', edgecolor='black', pad=10), multialignment='left', **align) axes.annotate(b, xy=(x, y), bbox=dict(facecolor='w', edgecolor='k', pad=10), multialignment='left', horizontalalignment=xalign, verticalalignment=yalign) if self._grid: pylab.grid() axes.set_xscale(self._xscale) axes.set_yscale(self._yscale) if self._leg: pylab.legend() if self.makex(): axes.set_xlabel(self.makex()) if self.makey(): axes.set_ylabel(self.makey()) self._made = True axes.set_xlim(*xlimraw) axes.set_ylim(*ylimraw) return self # cascade
class Plot: def __init__(self): self._made = False self._data = [] self._boxes = [] self._enlarge = (0.02,0.02,0.02,0.02) self._grid = True self._xscale = 'linear' self._yscale = 'linear' self._leg = False self._xaxis = None self._yaxis = None self._fitcount = 0 def xaxis(self, q): self._made = False if not isinstance(q, Quantity): raise TypeError("xaxis argument must be Quantity!") self._xaxis = Quantity(q.sprefunit()) self._xaxis.label = q.label self._xaxis.latex = q.latex self._xaxis.symbol = q.symbol return self def yaxis(self, q): self._made = False if not isinstance(q, Quantity): raise TypeError("yaxis argument must be Quantity!") self._yaxis = Quantity(q.sprefunit()) self._yaxis.label = q.label self._yaxis.latex = q.latex self._yaxis.symbol = q.symbol return self def makex(self): if self._xaxis is None: return '' lab = [] if self._xaxis.label: lab.append(self._xaxis.label) sl = self._xaxis.latex or self._xaxis.symbol if sl: lab.append("${}$".format(sl)) punit = self._xaxis.sprefunit(latex=True) if punit: lab.append('in ${}$'.format(punit)) return ' '.join(lab) def makey(self): if self._yaxis is None: return '' lab = [] if self._yaxis.label: lab.append(self._yaxis.label) sl = self._yaxis.latex or self._yaxis.symbol if sl: lab.append("${}$".format(sl)) punit = self._yaxis.sprefunit(latex=True) if punit: lab.append('in ${}$'.format(punit)) return ' '.join(lab) """ def xunit(self, s): self._made = False if self._xaxis is None: self._xaxis = Quantity() q = Quantity(s) self._xaxis.preferredUnit = q.preferredUnit self._xaxis.value = q.value self._xaxis.uvec = q.uvec return self def yunit(self, s): self._made = False if self._yaxis is None: self._yaxis = Quantity() q = Quantity(s) self._yaxis.preferredUnit = q.preferredUnit self._yaxis.value = q.value self._yaxis.uvec = q.uvec return self""" def xlabel(self, s, symbol=None, latex=None): self._made = False if self._xaxis is None: self._xaxis = Quantity() self._xaxis.label = s if symbol is not None: self._xaxis.symbol = symbol if latex is not None: self._yaxis.latex = latex return self def ylabel(self, s): self._made = False if self._yaxis is None: self._yaxis = Quantity() self._yaxis.label = s if symbol is not None: self._xaxis.symbol = symbol if latex is not None: self._yaxis.latex = latex return self def legend(self, on=True): self._made = False self._leg = on return self def data(self, x, y, fmt, errorbar, label=True, **kwds): self._made = False if label == True: kwds['label']=y.label elif label is not None: kwds['label']=label self._data.append( (x, y, fmt, errorbar, kwds) ) if isinstance(x, Quantity): if self._xaxis is None: self.xaxis(x) if isinstance(y, Quantity): if self._yaxis is None: self.yaxis(y) return self def error(self, x, y, fmt='.k', markersize=5, capsize=0, label=True, ecolor='0.3', **kwds): kwds['capsize'] = capsize self._made = False self.data(x, y, fmt=fmt, markersize=markersize, label=label, ecolor=ecolor, errorbar=True, **kwds) return self # cascade def points(self, x, y, fmt='.k', markersize=3, label=True, **kwds): self._made = False self.data(x, y, fmt=fmt, markersize=markersize, label=label, errorbar=False, **kwds) return self # cascade def line(self, x, y, fmt='-b', linewidth=1, label=True, **kwds): self._made = False self.data(x, y, fmt=fmt, linewidth=linewidth, label=label, errorbar=False, **kwds) return self # cascade def histoline(self, x, y, *args, **kwds): l = len(x) doubledX = np.zeros(2*l) doubledY = np.zeros(2*l) for i in range(0, l): doubledY[2*i] = y.value[i] doubledY[2*i+1] = y.value[i] deltaX = x.value[1] - x.value[0] doubledX[0] = x.value[0] - deltaX / 2 for i in range(0, l-1): doubledX[2*i+1] = (x.value[i] + x.value[i+1]) / 2 doubledX[2*i+2] = (x.value[i] + x.value[i+1]) / 2 deltaX = x.value[-1] - x.value[-2] doubledX[-1] = x.value[-1] + deltaX / 2 doubledX = [doubledX[0]] + list(doubledX) + [doubledX[-1]] doubledY = [0] + list(doubledY) + [0] nx = Quantity(doubledX, 0, x.uvec) nx.name(x.label, x.symbol, x.latex) nx.preferredUnit = x.preferredUnit ny = Quantity(doubledY, 0, y.uvec) ny.name(y.label,y.symbol,y.latex) ny.preferredUnit = y.preferredUnit return self.line(nx, ny, *args, **kwds) fitcolors = 'brgcmy' def fit(self, mf, box=True, bpos=None, xmin=None, xmax=None, chi2=True): if not isinstance(mf, ModelFit): raise TypeError("Argument of Plot.fit must be a ModelFit, but {} given.".format(type(mf))) self._fitcount += 1 text = 'Fit: ${}$\n\n'.format(mf.eq()) if mf.sr >= 3: text += 'fit failed ({})!\n'.format(mf.sr) for p in mf.parameters: text += ' ${}$\n'.format(p.tex()) if chi2: text += ' $\chi^2/\mathrm{ndf} = ' +('{:.2f} / {}$'.format(*mf.chi)) xmin = xmin or min(mf.xo.value) xmax = xmax or max(mf.xo.value) if box: self.box( (self._fitcount, text, mf.xo, mf.yo, bpos) ) x = Quantity(np.linspace(xmin, xmax, 200), unit=mf.xo.uvec) p0 = [Quantity(p.value, 0, p.uvec) for p in mf.parameters] self.line(x, mf.func(x, *p0), fmt='-'+Plot.fitcolors[(self._fitcount-1)%6]) return self def box(self, text): self._made = False self._boxes.append(text) return self def xlog(self, log=True): self._made = False self._xscale = 'log' if log else 'linear' return self def ylog(self, log=True): self._made = False self._yscale = 'log' if log else 'linear' return self def save(self, filename, **kwds): if not self._made: self.make() # dpi not set here anymore # do this in a matlibplotrc file, e.g. # figure.dpi : 300 plt.tight_layout() plt.savefig(filename, **kwds) return self def show(self): if not self._made: self.make() plt.show() return self def make(self, clf=True, fig=None, axes=None): if clf: plt.clf() if fig is None and axes is None: fig = plt.figure() if axes is None: axes = fig.add_subplot(111) xlim = None ylim = None for x, y, fmt, errorbar, d in self._data: if isinstance(x, Quantity): f = Quantity(unit=x.uvec) / self._xaxis if not f.unitless(): warnings.warn('The ratio of x axis unit ({}) and real unit ({}) must be unit less.'.format(self._xaxis.siunit(), y.siunit())) f = float(f) sx = x.stddev() * f x = x.value * f else: #f = float(1 / Quantity(self._xunit)) #x = x * f sx = 0 if isinstance(y, Quantity): f = Quantity(unit=y.uvec) / self._yaxis if not f.unitless(): warnings.warn('The ratio of preferred unit ({}) and real unit ({}) must be unit less.'.format(self._yaxis.siunit(), y.siunit())) f = float(f) sy = y.stddev() * f y = y.value * f else: #f = float(1 / Quantity(self.ypreferredUnit)) #y = y * f sy = 0 xmin = np.nanmin(x - sx) xmax = np.nanmax(x + sx) ymin = np.nanmin(y - sy) ymax = np.nanmax(y + sy) if xlim is None: xlim = [xmin, xmax] if ylim is None: ylim = [ymin, ymax] xlim[0] = min(xlim[0], xmin) xlim[1] = max(xlim[1], xmax) ylim[0] = min(ylim[0], ymin) ylim[1] = max(ylim[1], ymax) xlimraw = xlim ylimraw = ylim xerr = sx yerr = sy if errorbar: axes.errorbar(x, y, sy, sx, fmt=fmt, **d) else: axes.plot(x, y, fmt, **d) xdif = xlim[1] - xlim[0] ydif = ylim[1] - ylim[0] xlim[0] -= xdif * self._enlarge[0] xlim[1] += xdif * self._enlarge[1] ylim[0] -= ydif * self._enlarge[2] ylim[1] += ydif * self._enlarge[3] i = 0 taken = [ [0]*2, [0]*2, [0]*2 ] for b in self._boxes: if isinstance(b, tuple): fitnum, b, x, y, bpos = b if isinstance(bpos, tuple): x = bpos[0] / 3 y = bpos[1] / 2 else: penalty = [] for x in range(3): for y in range(2): points = 0 total = 0 for dx, dy, fmt, errorbar, d in self._data: fx = float(Quantity(unit=dx.uvec) / self._xaxis) fy = float(Quantity(unit=dy.uvec) / self._yaxis) idx1 = (dx.value * fx > (xlim[0] + xdif * (x/3 - 0.1))) idx2 = (dx.value * fx < (xlim[0] + xdif * (x+1.3) / 3)) idx3 = (dy.value * fy > (ylim[0] + ydif * (y/2 - 0.1))) idx4 = (dy.value * fy < (ylim[0] + ydif * (y+1.2) / 2)) idx = idx1 * idx2 * idx3 * idx4 points += len(dx.value[idx]) / len(dx.value) total = len(dx.value) points += total * taken[x][y] penalty.append( (x, y, points) ) x, y, points = min(penalty, key=lambda y: y[2]) taken[x][y] += 1 if y == 0: yalign = 'bottom' y = ylimraw[0] + ydif * 0.05 elif y == 1: yalign = 'top' y = ylimraw[1] - ydif * 0.05 if x == 0: xalign = 'left' x = xlimraw[0] + xdif * 0.05 elif x == 1: xalign = 'center' x = xlimraw[0] + 0.5 * xdif elif x == 2: xalign = 'right' x = xlimraw[1] - xdif * 0.05 if self._fitcount > 1: b = '({}) {}'.format(fitnum, b) fx = Quantity(unit=x.uvec) / self._xaxis fy = Quantity(unit=y.uvec) / self._yaxis if not fx.unitless(): warnings.warn('The ratio of preferred unit ({}) and real unit ({}) must be unit less.'.format(self._xaxis.siunit(), x.siunit())) if not fy.unitless(): warnings.warn('The ratio of preferred unit ({}) and real unit ({}) must be unit less.'.format(self._yaxis.siunit(), y.siunit())) fx = float(fx) fy = float(fy) tx = x.value * fx ty = y.value * fy tx = (min(tx)+max(tx))/2 ty = max(ty) ty += 0.05 * ydif axes.text(tx, ty, '({})'.format(fitnum), color=Plot.fitcolors[(fitnum-1)%6], horizontalalignment='center', verticalalignment='bottom') #plt.figtext(*xy, s=b,bbox=dict(facecolor='w', edgecolor='black', pad=10), multialignment='left', **align) axes.annotate(b, xy=(x, y), bbox=dict(facecolor='w', edgecolor='k', pad=10), multialignment='left', horizontalalignment=xalign, verticalalignment=yalign) if self._grid: pylab.grid() axes.set_xscale(self._xscale) axes.set_yscale(self._yscale) if self._leg: pylab.legend() if self.makex(): axes.set_xlabel(self.makex()) if self.makey(): axes.set_ylabel(self.makey()) self._made = True axes.set_xlim(*xlimraw) axes.set_ylim(*ylimraw) return self # cascade