Skip to content

Commit 0ccd44a

Browse files
committed
add format_string method for psyplot Project
This commit adds a new Project.format_string method that can be used to inject the meta data attrs of the arrays, as well as time information etc. into a string.
1 parent d36be31 commit 0ccd44a

2 files changed

Lines changed: 85 additions & 74 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Added
99
see `#20 <https://github.com/psyplot/psyplot/pull/20>`__. Furthermore, the
1010
`check_data` method of the various plotmethods now also accept a `decoder`
1111
parameter, see `#22 <https://github.com/psyplot/psyplot/pull/22>`__
12-
* `psyplot.data.open_dataset` now decodes grid_mappings attributes,
12+
* ``psyplot.data.open_dataset`` now decodes grid_mappings attributes,
1313
see `#17 <https://github.com/psyplot/psyplot/pull/17>`__
1414
* psyplot projects now support the with syntax, e.g. something like::
1515

@@ -19,6 +19,8 @@ see `#17 <https://github.com/psyplot/psyplot/pull/17>`__
1919
sp will be closed automatically (see `#18 <https://github.com/psyplot/psyplot/pull/18>`__)
2020
* the update to variables with other dimensions works now as well
2121
(see `#22 <https://github.com/psyplot/psyplot/pull/22>`__)
22+
* a ``psyplot.project.Project`` now has a new ``format_string`` method to
23+
format a string with the meta attributes of the data in the projects
2224

2325
Changed
2426
-------

‎psyplot/project.py‎

Lines changed: 82 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ def show():
656656
docstrings.keep_params('join_dicts.parameters', 'delimiter')
657657
docstrings.keep_params('join_dicts.parameters', 'keep_all')
658658

659+
@docstrings.get_sectionsf('Project.joined_attrs')
659660
@docstrings.with_indent(8)
660661
def joined_attrs(self, delimiter=', ', enhanced=True, plot_data=False,
661662
keep_all=True):
@@ -693,6 +694,52 @@ def joined_attrs(self, delimiter=', ', enhanced=True, plot_data=False,
693694
return utils.join_dicts(all_attrs, delimiter=delimiter,
694695
keep_all=keep_all)
695696

697+
@docstrings.get_sectionsf('Project.format_string')
698+
@docstrings.with_indent(8)
699+
def format_string(self, s, use_time=False, format_args=None, *args,
700+
**kwargs):
701+
"""Format a string with the attributes in this project
702+
703+
Parameters
704+
----------
705+
s: str
706+
The string that is subject to be formatted
707+
use_time: bool
708+
If True, formatting strings for the
709+
:meth:`datetime.datetime.strftime` are expected to be found in
710+
`output` (e.g. ``'%%m'``, ``'%%Y'``, etc.). If so, other formatting
711+
strings must be escaped by double ``'%%'`` (e.g. ``'%%%i'``
712+
instead of (``'%%i'``))
713+
format_args: tuple
714+
A tuple of arguments that shall be inserted in `s` via
715+
``s %% format_args``. (There will be no error, when this fails!)
716+
%(Project.joined_attrs.parameters)s
717+
718+
Returns
719+
-------
720+
str
721+
The formatted string `s`
722+
"""
723+
attrs = self.joined_attrs(*args, **kwargs)
724+
if use_time:
725+
tnames = self._get_tnames()
726+
tname = next(iter(tnames)) if len(tnames) == 1 else None
727+
728+
time = attrs[tname]
729+
try: # assume a valid datetime.datetime instance
730+
s = pd.to_datetime(time).strftime(s)
731+
except ValueError:
732+
pass
733+
if format_args is not None:
734+
try:
735+
s = safe_modulo(s, format_args, print_warning=False)
736+
except TypeError:
737+
pass
738+
return safe_modulo(s, attrs)
739+
740+
docstrings.keep_params('Project.format_string.parameters', 'use_time')
741+
742+
@docstrings.with_indent(8)
696743
def export(self, output, tight=False, concat=True, close_pdf=None,
697744
use_time=False, **kwargs):
698745
"""Exports the figures of the project to one or more image files
@@ -707,7 +754,7 @@ def export(self, output, tight=False, concat=True, close_pdf=None,
707754
If string (or iterable of strings), attribute names in the
708755
xarray.DataArray.attrs attribute as well as index dimensions
709756
are replaced by the respective value (see examples below).
710-
Furthermore a single format string without key (e.g. %i, %s, %d,
757+
Furthermore a single format string without key (e.g. %%i, %%s, %%d,
711758
etc.) is replaced by a counter.
712759
tight: bool
713760
If True, it is tried to figure out the tight bbox of the figure
@@ -721,12 +768,7 @@ def export(self, output, tight=False, concat=True, close_pdf=None,
721768
If None and `output` is a string, it is the same as
722769
``close_pdf=True``, if None and `output` is neither a string nor an
723770
iterable, it is the same as ``close_pdf=False``
724-
use_time: bool
725-
If True, formatting strings for the
726-
:meth:`datetime.datetime.strftime` are expected to be found in
727-
`output` (e.g. ``'%m'``, ``'%Y'``, etc.). If so, other formatting
728-
strings must be escaped by double ``'%'`` (e.g. ``'%%i'`` instead
729-
of (``'%i'``))
771+
%(Project.format_string.parameters.use_time)s
730772
``**kwargs``
731773
Any valid keyword for the :func:`matplotlib.pyplot.savefig`
732774
function
@@ -747,18 +789,18 @@ def export(self, output, tight=False, concat=True, close_pdf=None,
747789
Save all figures into separate pngs with increasing numbers (e.g.
748790
``'my_plots_1.png'``)::
749791
750-
>>> p.export('my_plots_%i.png')
792+
>>> p.export('my_plots_%%i.png')
751793
752794
Save all figures into separate pngs with the name of the variables
753795
shown in each figure (e.g. ``'my_plots_t2m.png'``)::
754796
755-
>>> p.export('my_plots_%(name)s.png')
797+
>>> p.export('my_plots_%%(name)s.png')
756798
757799
Save all figures into separate pngs with the name of the variables
758800
shown in each figure and with increasing numbers (e.g.
759801
``'my_plots_1_t2m.png'``)::
760802
761-
>>> p.export('my_plots_%i_%(name)s.png')
803+
>>> p.export('my_plots_%%i_%%(name)s.png')
762804
763805
Specify the names for each figure directly via a list::
764806
@@ -768,79 +810,46 @@ def export(self, output, tight=False, concat=True, close_pdf=None,
768810
if tight:
769811
kwargs['bbox_inches'] = 'tight'
770812

771-
if use_time:
772-
def insert_time(s, attrs):
773-
time = attrs[tname]
774-
try: # assume a valid datetime.datetime instance
775-
s = pd.to_datetime(time).strftime(s)
776-
except ValueError:
777-
pass
778-
return s
779-
780-
tnames = self._get_tnames()
781-
tname = next(iter(tnames)) if len(tnames) == 1 else None
782-
else:
783-
def insert_time(s, attrs):
784-
return s
785-
786-
tname = None
813+
not_enough_files_warnings = (
814+
"Not enough output files specified! %i figures are open "
815+
"but only %i filenames have been given! This will cause "
816+
"that some figures may be overwritten after being "
817+
"exported! Use a pdf instead if you want to save all "
818+
"figures or include a '%%i' string in the filename to "
819+
"avoid duplicates.")
787820

788821
if isinstance(output, six.string_types): # a single string
789822
out_fmt = kwargs.pop('format', os.path.splitext(output))[1][1:]
790823
if out_fmt.lower() == 'pdf' and concat:
791-
attrs = self.joined_attrs('-')
792-
if tname is not None and tname in attrs:
793-
output = insert_time(output, attrs)
794-
pdf = PdfPages(safe_modulo(output, attrs))
824+
output = self.format_string(output, use_time, delimiter='-')
825+
pdf = PdfPages(output)
795826

796-
def save(fig):
827+
for fig in self.figs:
797828
pdf.savefig(fig, **kwargs)
798-
799-
def close():
800-
if close_pdf is None or close_pdf:
801-
pdf.close()
802-
return
829+
if close_pdf is None or close_pdf:
830+
pdf.close()
831+
return
832+
else:
803833
return pdf
804834
else:
805-
def save(fig):
806-
attrs = self.figs[fig].joined_attrs('-')
807-
out = output
808-
if tname is not None and tname in attrs:
809-
out = insert_time(out, attrs)
810-
try:
811-
out = safe_modulo(out, i, print_warning=False)
812-
except TypeError:
813-
pass
814-
fig.savefig(safe_modulo(out, attrs), **kwargs)
815-
816-
def close():
817-
pass
818-
elif utils.is_iterable(output): # a list of strings
819-
output = cycle(output)
820-
821-
def save(fig):
822-
attrs = self.figs[fig].joined_attrs('-')
823-
out = next(output)
824-
if tname is not None and tname in attrs:
825-
out = insert_time(out, attrs)
826-
try:
827-
out = safe_modulo(next(output), i, print_warning=False)
828-
except TypeError:
829-
pass
830-
fig.savefig(safe_modulo(out, attrs), **kwargs)
831-
832-
def close():
833-
pass
835+
output = [output] * len(self.figs)
836+
837+
if utils.is_iterable(output): # a list of strings
838+
output = [sp.format_string(out, use_time, i, delimiter='-')
839+
for i, (out, sp) in enumerate(
840+
zip(output, self.figs.values()), 1)]
841+
if len(set(output)) != len(output):
842+
warn(not_enough_files_warnings % (
843+
len(output), len(self.figs)))
844+
output = iter(output)
845+
846+
for fig, out in zip(self.figs, output):
847+
fig.savefig(out, **kwargs)
834848
else: # an instances of matplotlib.backends.backend_pdf.PdfPages
835-
def save(fig):
849+
for fig in self.figs:
836850
output.savefig(fig, **kwargs)
837-
838-
def close():
839-
if close_pdf:
840-
output.close()
841-
for i, fig in enumerate(self.figs, 1):
842-
save(fig)
843-
return close()
851+
if close_pdf:
852+
output.close()
844853

845854
docstrings.keep_params('Plotter.share.parameters', 'keys')
846855
docstrings.delete_params('Plotter.share.parameters', 'keys', 'plotters')

0 commit comments

Comments
 (0)