From 6d05e5277c1a8af5aa6fb449832452824b5556c1 Mon Sep 17 00:00:00 2001 From: Jan Benda Date: Fri, 20 Dec 2019 23:08:51 +0100 Subject: [PATCH] added global plotstyle.py providing common formatting and colors --- chapter.mk | 4 +- plotstyle.py | 86 +++++++++++++++++++++++++++++++ regression/lecture/cubicerrors.py | 42 +++++---------- regression/lecture/cubicfunc.py | 16 ++---- regression/lecture/cubicmse.py | 25 +++------ 5 files changed, 113 insertions(+), 60 deletions(-) create mode 100644 plotstyle.py diff --git a/chapter.mk b/chapter.mk index ec6fe68..50d1708 100644 --- a/chapter.mk +++ b/chapter.mk @@ -8,8 +8,8 @@ PYPDFFILES=$(PYFILES:.py=.pdf) pythonplots : $(PYPDFFILES) -$(PYPDFFILES) : %.pdf: %.py - python3 $< +$(PYPDFFILES) : %.pdf: %.py ../../plotstyle.py + PYTHONPATH="../../" python3 $< cleanpythonplots : rm -f $(PYPDFFILES) diff --git a/plotstyle.py b/plotstyle.py new file mode 100644 index 0000000..730f0fe --- /dev/null +++ b/plotstyle.py @@ -0,0 +1,86 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt + +# colors: +colors = { + 'red': '#CC0000', + 'orange': '#FF9900', + 'lightorange': '#FFCC00', + 'yellow': '#FFFF66', + 'green': '#99FF00', + 'blue': '#0000FF' +} + + +def show_spines(ax, spines): + """ Show and hide spines. + + Parameters + ---------- + ax: matplotlib figure, matplotlib axis, or list of matplotlib axes + Axis whose spines and ticks are manipulated. + If figure, then apply manipulations on all axes of the figure. + If list of axes, apply manipulations on each of the given axes. + spines: string + Specify which spines and ticks should be shown. All other ones or hidden. + 'l' is the left spine, 'r' the right spine, 't' the top one and 'b' the bottom one. + E.g. 'lb' shows the left and bottom spine, and hides the top and and right spines, + as well as their tick marks and labels. + '' shows no spines at all. + 'lrtb' shows all spines and tick marks. + """ + # collect spine visibility: + xspines = [] + if 't' in spines: + xspines.append('top') + if 'b' in spines: + xspines.append('bottom') + yspines = [] + if 'l' in spines: + yspines.append('left') + if 'r' in spines: + yspines.append('right') + # collect axes: + if isinstance(ax, (list, tuple)): + axs = ax + else: + axs = ax.get_axes() + if not isinstance(axs, (list, tuple)): + axs = [axs] + for ax in axs: + # hide spines: + if not 'top' in xspines: + ax.spines['top'].set_visible(False) + if not 'bottom' in xspines: + ax.spines['bottom'].set_visible(False) + if not 'left' in yspines: + ax.spines['left'].set_visible(False) + if not 'right' in yspines: + ax.spines['right'].set_visible(False) + # ticks: + if len(xspines) == 0: + ax.xaxis.set_ticks_position('none') + ax.set_xticks([]) + elif len(xspines) == 1: + ax.xaxis.set_ticks_position(xspines[0]) + else: + ax.xaxis.set_ticks_position('both') + if len(yspines) == 0: + ax.yaxis.set_ticks_position('none') + ax.set_yticks([]) + elif len(yspines) == 1: + ax.yaxis.set_ticks_position(yspines[0]) + else: + ax.yaxis.set_ticks_position('both') + + +# initialization: +plt.xkcd() +mpl.rcParams['figure.facecolor'] = 'white' +mpl.rcParams['xtick.direction'] = 'out' +mpl.rcParams['ytick.direction'] = 'out' +mpl.rcParams['xtick.major.size'] = 6 +mpl.rcParams['ytick.major.size'] = 6 +mpl.rcParams['xtick.major.width'] = 1.25 +mpl.rcParams['ytick.major.width'] = 1.25 + diff --git a/regression/lecture/cubicerrors.py b/regression/lecture/cubicerrors.py index 60a7bf8..7904121 100644 --- a/regression/lecture/cubicerrors.py +++ b/regression/lecture/cubicerrors.py @@ -1,5 +1,6 @@ import matplotlib.pyplot as plt import numpy as np +from plotstyle import colors, show_spines def create_data(): # wikipedia: @@ -15,18 +16,13 @@ def create_data(): def plot_data(ax, x, y, c): - ax.scatter(x, y, marker='o', color='b', s=40, zorder=10) + ax.scatter(x, y, marker='o', color=colors['blue'], s=40, zorder=10) xx = np.linspace(2.1, 3.9, 100) - ax.plot(xx, c*xx**3.0, color='#CC0000', lw=2, zorder=5) + ax.plot(xx, c*xx**3.0, color=colors['red'], lw=2, zorder=5) for cc in [0.25*c, 0.5*c, 2.0*c, 4.0*c]: - ax.plot(xx, cc*xx**3.0, color='#FF9900', lw=1.5, zorder=5) - - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + ax.plot(xx, cc*xx**3.0, color=colors['orange'], lw=1.5, zorder=5) + + show_spines(ax, 'lb') ax.set_xlabel('Size x / m') ax.set_ylabel('Weight y / kg') ax.set_xlim(2, 4) @@ -36,12 +32,7 @@ def plot_data(ax, x, y, c): def plot_data_errors(ax, x, y, c): - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + show_spines(ax, 'lb') ax.set_xlabel('Size x / m') #ax.set_ylabel('Weight y / kg') ax.set_xlim(2, 4) @@ -54,23 +45,18 @@ def plot_data_errors(ax, x, y, c): xytext=(3.4, 70), textcoords='data', ha='left', arrowprops=dict(arrowstyle="->", relpos=(0.9,1.0), connectionstyle="angle3,angleA=50,angleB=-30") ) - ax.scatter(x[:40], y[:40], color='b', s=10, zorder=0) + ax.scatter(x[:40], y[:40], color=colors['blue'], s=10, zorder=0) inxs = [3, 10, 11, 17, 18, 21, 28, 30, 33] - ax.scatter(x[inxs], y[inxs], color='b', s=40, zorder=10) + ax.scatter(x[inxs], y[inxs], color=colors['blue'], s=40, zorder=10) xx = np.linspace(2.1, 3.9, 100) - ax.plot(xx, c*xx**3.0, color='#CC0000', lw=2) + ax.plot(xx, c*xx**3.0, color=colors['red'], lw=2) for i in inxs : xx = [x[i], x[i]] yy = [c*x[i]**3.0, y[i]] - ax.plot(xx, yy, color='#FF9900', lw=2, zorder=5) + ax.plot(xx, yy, color=colors['orange'], lw=2, zorder=5) def plot_error_hist(ax, x, y, c): - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + show_spines(ax, 'lb') ax.set_xlabel('Squared error') ax.set_ylabel('Frequency') bins = np.arange(0.0, 1250.0, 100) @@ -85,18 +71,16 @@ def plot_error_hist(ax, x, y, c): xytext=(800, 3), textcoords='data', ha='left', arrowprops=dict(arrowstyle="->", relpos=(0.0,0.2), connectionstyle="angle3,angleA=10,angleB=90") ) - ax.hist(errors, bins, color='#FF9900') + ax.hist(errors, bins, color=colors['orange']) if __name__ == "__main__": x, y, c = create_data() - plt.xkcd() fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7., 2.6)) plot_data(ax1, x, y, c) plot_data_errors(ax2, x, y, c) #plot_error_hist(ax2, x, y, c) - fig.set_facecolor("white") fig.tight_layout() fig.savefig("cubicerrors.pdf") plt.close() diff --git a/regression/lecture/cubicfunc.py b/regression/lecture/cubicfunc.py index 8ccc39e..d258723 100644 --- a/regression/lecture/cubicfunc.py +++ b/regression/lecture/cubicfunc.py @@ -1,5 +1,6 @@ import matplotlib.pyplot as plt import numpy as np +from plotstyle import colors, show_spines if __name__ == "__main__": # wikipedia: @@ -12,21 +13,15 @@ if __name__ == "__main__": noise = rng.randn(len(x))*50 y += noise - plt.xkcd() fig, ax = plt.subplots(figsize=(7., 3.6)) - ax.scatter(x, y, marker='o', color='b', s=40, zorder=10) + ax.scatter(x, y, marker='o', color=colors['blue'], s=40, zorder=10) xx = np.linspace(2.1, 3.9, 100) - ax.plot(xx, c*xx**3.0, color='#CC0000', lw=3, zorder=5) + ax.plot(xx, c*xx**3.0, color=colors['red'], lw=3, zorder=5) for cc in [0.25*c, 0.5*c, 2.0*c, 4.0*c]: - ax.plot(xx, cc*xx**3.0, color='#FF9900', lw=2, zorder=5) + ax.plot(xx, cc*xx**3.0, color=colors['orange'], lw=2, zorder=5) - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + show_spines(ax, 'lb') ax.set_xlabel('Size x / m') ax.set_ylabel('Weight y / kg') ax.set_xlim(2, 4) @@ -34,7 +29,6 @@ if __name__ == "__main__": ax.set_xticks(np.arange(2.0, 4.1, 0.5)) ax.set_yticks(np.arange(0, 401, 100)) - fig.set_facecolor("white") fig.subplots_adjust(0.11, 0.16, 0.98, 0.97) fig.savefig("cubicfunc.pdf") plt.close() diff --git a/regression/lecture/cubicmse.py b/regression/lecture/cubicmse.py index f1d3e5b..dd96e98 100644 --- a/regression/lecture/cubicmse.py +++ b/regression/lecture/cubicmse.py @@ -1,5 +1,6 @@ import matplotlib.pyplot as plt import numpy as np +from plotstyle import colors, show_spines def create_data(): # wikipedia: @@ -38,9 +39,9 @@ def plot_mse(ax, x, y, c, cs): for i, cc in enumerate(ccs): mses[i] = np.mean((y-(cc*x**3.0))**2.0) - ax.plot(ccs, mses, 'b', lw=2, zorder=10) - ax.scatter(cs, ms, color='#cc0000', s=40, zorder=20) - ax.scatter(cs[-1], ms[-1], color='#FF9900', s=60, zorder=30) + ax.plot(ccs, mses, colors['blue'], lw=2, zorder=10) + ax.scatter(cs, ms, color=colors['red'], s=40, zorder=20) + ax.scatter(cs[-1], ms[-1], color=colors['orange'], s=60, zorder=30) for i in range(4): ax.annotate('', xy=(cs[i+1]+0.2, ms[i+1]), xycoords='data', @@ -49,12 +50,7 @@ def plot_mse(ax, x, y, c, cs): connectionstyle="angle3,angleA=10,angleB=70") ) - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + show_spines(ax, 'lb') ax.set_xlabel('c') ax.set_ylabel('mean squared error') ax.set_xlim(0, 10) @@ -63,14 +59,9 @@ def plot_mse(ax, x, y, c, cs): ax.set_yticks(np.arange(0, 30001, 10000)) def plot_descent(ax, cs, mses): - ax.plot(np.arange(len(mses))+1, mses, '-o', c='#cc0000', mew=0, ms=8) + ax.plot(np.arange(len(mses))+1, mses, '-o', c=colors['red'], mew=0, ms=8) - ax.spines["right"].set_visible(False) - ax.spines["top"].set_visible(False) - ax.yaxis.set_ticks_position('left') - ax.xaxis.set_ticks_position('bottom') - ax.tick_params(direction="out", width=1.25) - ax.tick_params(direction="out", width=1.25) + show_spines(ax, 'lb') ax.set_xlabel('iteration') #ax.set_ylabel('mean squared error') ax.set_xlim(0, 10.5) @@ -83,11 +74,9 @@ def plot_descent(ax, cs, mses): if __name__ == "__main__": x, y, c = create_data() cs, mses = gradient_descent(x, y) - plt.xkcd() fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7., 2.6)) plot_mse(ax1, x, y, c, cs) plot_descent(ax2, cs, mses) - fig.set_facecolor("white") fig.tight_layout() fig.savefig("cubicmse.pdf") plt.close()