@@ -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