Skip to content

Commit

Permalink
more flexible stackplot options
Browse files Browse the repository at this point in the history
and docstring and pytests
  • Loading branch information
CommonClimate committed Jul 2, 2024
1 parent c33b9f8 commit 52750e3
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 52 deletions.
99 changes: 48 additions & 51 deletions pyleoclim/core/multipleseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,7 +1607,8 @@ def stackplot(self, figsize=None, savefig_settings=None, time_unit = None,
xlim=None, fill_between_alpha=0.2, colors=None, cmap='tab10',
norm=None, labels='auto', ylabel_fontsize = 8, spine_lw=1.5,
grid_lw=0.5, label_x_loc=-0.15, v_shift_factor=3/4, linewidth=1.5,
yaxis_style = 'minimal', plot_kwargs=None):
yticks_minor = False, xticks_minor = False, ylims ='auto',
plot_kwargs=None):
''' Stack plot of multiple series
Time units are harmonized prior to plotting.
Expand Down Expand Up @@ -1694,11 +1695,16 @@ def stackplot(self, figsize=None, savefig_settings=None, time_unit = None,
Size for ylabel font. Default is 8, to avoid crowding.
yaxis_style: str
Style of the y-axis. If 'minimal', only the extreme values are shown, bracketed by 2 ticks.
If 'detailed', shows major and minor ticks.
Other strings result in default Matplotlib behavior (depends on the assigned style)
yticks_minor : bool
Whether the y axes should contain minor ticks (use sparingly!). Default: False
xticks_minor : bool
Whether the x axis should contain minor ticks. Default: False
ylims : str {'spacious', 'auto'}
Method for determining the limits of the y axes.
Default is 'spacious', which is mean +/- 4 x std
'auto' activates the Matplotlib default
plot_kwargs: dict or list of dict
Expand Down Expand Up @@ -1758,13 +1764,13 @@ def stackplot(self, figsize=None, savefig_settings=None, time_unit = None,
fig, ax = ms.stackplot(labels=None, plot_kwargs=[{'marker':'o'},{'marker':'^'}])
By default, the y axes are kept very minimal to allow stacking many records. In some instances, however,
one may want more detailed axes, iwht major and minor ticks. This option can be actived via `yaxis_style`.
We also show how to enlarge the ylabels for improved readability:
one may want more detailed axes, with major and minor ticks. We also show how to enlarge the ylabels and
adjust vertical spacing for improved readability:
.. jupyter-execute::
fig, ax = ms.stackplot(labels=None, ylabel_fontsize = 12,
yaxis_style = 'detailed')
fig, ax = ms.stackplot(labels=None, ylabel_fontsize = 12, v_shift_factor = 0.9,
yticks_minor=True, xticks_minor=True, ylims='auto')
This approach makes sense with small stacks, but quickly becomes unwieldy with large ones. Use at your own risk!
Expand Down Expand Up @@ -1840,25 +1846,30 @@ def stackplot(self, figsize=None, savefig_settings=None, time_unit = None,

mu = np.nanmean(ts.value)
std = np.nanstd(ts.value)
ylim = [mu-4*std, mu+4*std]
ax[idx].fill_between(ts.time, ts.value, y2=mu, alpha=fill_between_alpha, color=clr)
trans = transforms.blended_transform_factory(ax[idx].transAxes, ax[idx].transData)

if labels == 'auto':
if ts.label is not None:
ax[idx].text(label_x_loc, mu, ts.label, horizontalalignment='right', transform=trans, color=clr, weight='bold')
elif type(labels) ==list:
ax[idx].text(label_x_loc, mu, labels[idx], horizontalalignment='right', transform=trans, color=clr, weight='bold')
elif labels==None:
pass
if yaxis_style == 'minimal':
ax[idx].set_ylim(ylim)
ax[idx].set_yticks(ylim)
ax[idx].yaxis.set_major_formatter(FormatStrFormatter('%.1f'))
elif yaxis_style == 'detailed':

ylim = [mu-4*std, mu+4*std]

if ylims == 'spacious':
ax[idx].set_ylim(ylim)

if yticks_minor is True:
ax[idx].yaxis.set_minor_locator(AutoMinorLocator())
ax[idx].yaxis.set_major_formatter(FormatStrFormatter('%.1f'))
ax[idx].tick_params(which='major', length=7, width=2)
ax[idx].tick_params(which='minor', length=4, width=1, color=clr)
ax[idx].tick_params(which='major', length=7, width=1.5)
ax[idx].tick_params(which='minor', length=3, width=1, color=clr)
else:
ax[idx].set_yticks(ylim)
ax[idx].yaxis.set_major_formatter(FormatStrFormatter('%.1f'))



if idx % 2 == 0:
Expand Down Expand Up @@ -1889,44 +1900,30 @@ def stackplot(self, figsize=None, savefig_settings=None, time_unit = None,
ax[idx].axvline(x=x, color='lightgray', linewidth=grid_lw, ls='-', zorder=-1)
ax[idx].axhline(y=mu, color='lightgray', linewidth=grid_lw, ls='-', zorder=-1)

# subplots_height = 1-height*(1-v_shift_factor)
# ax['subplots_canvas'] = fig.add_axes([left, bottom, width, subplots_height],
# **{'zorder':-1})
# ax['subplots_canvas'].spines['left'].set_visible(False)
# ax['subplots_canvas'].spines['right'].set_visible(False)
# ax['subplots_canvas'].spines['bottom'].set_visible(False)
# ax['subplots_canvas'].set_yticks([])
# ax['subplots_canvas'].set_xlim(xlim)
# ax['subplots_canvas'].tick_params(axis='x', which='both', length=0)
#
# ax['subplots_canvas'].set_xlabel('')
# ax['subplots_canvas'].set_ylabel('')
# ax['subplots_canvas'].set_xticklabels([])
# ax['subplots_canvas'].set_yticklabels([])
# ax['subplots_canvas'].grid(False)

bottom -= height*(1-v_shift_factor)
# other subplots are set inside the subplot that controls the time axis
# trying to make that time axis subplot the whole size of the figure

x_axis_key = 'x_axis'
# x_axis_key = n_ts

ax[x_axis_key] = fig.add_axes([left, bottom, width, height])
ax[x_axis_key].set_xlabel(time_label)
ax[x_axis_key].spines['left'].set_visible(False)
ax[x_axis_key].spines['right'].set_visible(False)
ax[x_axis_key].spines['bottom'].set_visible(True)
ax[x_axis_key].spines['bottom'].set_linewidth(spine_lw)
ax[x_axis_key].set_yticks([])
ax[x_axis_key].patch.set_alpha(0)
ax[x_axis_key].set_xlim(xlim)
ax[x_axis_key].grid(False)
ax[x_axis_key].tick_params(axis='x', which='both', length=3.5)
xt = ax[x_axis_key].get_xticks()[1:-1]
ax['x_axis'] = fig.add_axes([left, bottom, width, height])
ax['x_axis'].set_xlabel(time_label)
ax['x_axis'].spines['left'].set_visible(False)
ax['x_axis'].spines['right'].set_visible(False)
ax['x_axis'].spines['bottom'].set_visible(True)
ax['x_axis'].spines['bottom'].set_linewidth(spine_lw)
ax['x_axis'].set_yticks([])
ax['x_axis'].patch.set_alpha(0)
ax['x_axis'].set_xlim(xlim)
ax['x_axis'].grid(False)
ax['x_axis'].tick_params(axis='x', which='both', length=3.5)
xt = ax['x_axis'].get_xticks()[1:-1]
for x in xt:
ax[x_axis_key].axvline(x=x, color='lightgray', linewidth=grid_lw,
ax['x_axis'].axvline(x=x, color='lightgray', linewidth=grid_lw,
ls='-', zorder=-1)
if xticks_minor is True:
ax['x_axis'].xaxis.set_minor_locator(AutoMinorLocator())
ax['x_axis'].tick_params(which='major', length=7, width=1.5)
ax['x_axis'].tick_params(which='minor', length=3, width=1)


if 'fig' in locals():
if 'path' in savefig_settings:
Expand Down
19 changes: 18 additions & 1 deletion pyleoclim/tests/test_core_MultipleSeries.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,28 @@ def test_StackPlot_t0(self, labels):

@pytest.mark.parametrize('plot_kwargs', [{'marker':'o'},[{'marker':'o'},{'marker':'^'}]])
def test_StackPlot_t1(self, plot_kwargs):

ms = load_data()
fig, ax = ms.stackplot(plot_kwargs=plot_kwargs)
pyleo.closefig(fig)

@pytest.mark.parametrize('ylims', ['spacious', 'auto'])
def test_StackPlot_t2(self, ylims):
ms = load_data()
fig, ax = ms.stackplot(ylims=ylims)
pyleo.closefig(fig)

@pytest.mark.parametrize('yticks_minor', [True, False])
def test_StackPlot_t3(self, yticks_minor):
ms = load_data()
fig, ax = ms.stackplot(yticks_minor=yticks_minor)
pyleo.closefig(fig)

@pytest.mark.parametrize('xticks_minor', [True, False])
def test_StackPlot_t4(self, xticks_minor):
ms = load_data()
fig, ax = ms.stackplot(xticks_minor=xticks_minor)
pyleo.closefig(fig)

class TestMultipleSeriesSpectral():
''' Test for MultipleSeries.spectral
'''
Expand Down

0 comments on commit 52750e3

Please sign in to comment.