1
1
from __future__ import division
2
2
3
+ import inspect
3
4
import warnings
4
5
import itertools
5
6
import functools
@@ -176,9 +177,16 @@ def __init__(self, data, col=None, row=None, col_wrap=None,
176
177
self ._col_wrap = col_wrap
177
178
self ._x_var = None
178
179
self ._y_var = None
180
+ self ._cmap_extend = None
179
181
self ._mappables = []
180
182
181
- self .set_titles ()
183
+ @property
184
+ def _left_axes (self ):
185
+ return self .axes [:, 0 ]
186
+
187
+ @property
188
+ def _bottom_axes (self ):
189
+ return self .axes [- 1 , :]
182
190
183
191
def map_dataarray (self , func , x , y , ** kwargs ):
184
192
"""
@@ -201,46 +209,22 @@ def map_dataarray(self, func, x, y, **kwargs):
201
209
self : FacetGrid object
202
210
203
211
"""
204
-
205
212
# These should be consistent with xray.plot._plot2d
206
213
cmap_kwargs = {'plot_data' : self .data .values ,
207
- 'vmin' : None ,
208
- 'vmax' : None ,
209
- 'cmap' : None ,
210
- 'center' : None ,
211
- 'robust' : False ,
212
- 'extend' : None ,
213
214
# MPL default
214
215
'levels' : 7 if 'contour' in func .__name__ else None ,
215
216
'filled' : func .__name__ != 'contour' ,
216
217
}
217
218
218
- # Allow kwargs to override these defaults
219
- # Remove cmap_kwargs from kwargs for now, we will add them back later
220
- for param in list (kwargs ):
221
- if param in cmap_kwargs :
222
- cmap_kwargs [param ] = kwargs .pop (param )
219
+ cmap_args = inspect .getargspec (_determine_cmap_params ).args
220
+ cmap_kwargs .update ((a , kwargs [a ]) for a in cmap_args if a in kwargs )
223
221
224
- # colormap inference has to happen here since all the data in
225
- # self.data is required to make the right choice
226
222
cmap_params = _determine_cmap_params (** cmap_kwargs )
227
223
228
- if 'contour' in func .__name__ :
229
- # extend is a keyword argument only for contour and contourf, but
230
- # passing it to the colorbar is sufficient for imshow and
231
- # pcolormesh
232
- kwargs ['extend' ] = cmap_params ['extend' ]
233
- kwargs ['levels' ] = cmap_params ['levels' ]
234
-
235
- defaults = {
236
- 'add_colorbar' : False ,
237
- 'add_labels' : False ,
238
- 'norm' : cmap_params .pop ('cnorm' ),
239
- }
240
-
241
224
# Order is important
242
- defaults .update (cmap_params )
243
- defaults .update (kwargs )
225
+ func_kwargs = kwargs .copy ()
226
+ func_kwargs .update (cmap_params )
227
+ func_kwargs .update ({'add_colorbar' : False , 'add_labels' : False })
244
228
245
229
# Get x, y labels for the first subplot
246
230
x , y = _infer_xy_labels (darray = self .data .loc [self .name_dicts .flat [0 ]],
@@ -250,40 +234,67 @@ def map_dataarray(self, func, x, y, **kwargs):
250
234
# None is the sentinel value
251
235
if d is not None :
252
236
subset = self .data .loc [d ]
253
- self ._mappables .append (func (subset , x , y , ax = ax , ** defaults ))
237
+ mappable = func (subset , x , y , ax = ax , ** func_kwargs )
238
+ self ._mappables .append (mappable )
254
239
255
- # Left side labels
256
- for ax in self .axes [:, 0 ]:
257
- ax .set_ylabel (y )
240
+ self ._cmap_extend = cmap_params .get ('extend' )
241
+ self ._finalize_grid (x , y )
258
242
259
- # Bottom labels
260
- for ax in self .axes [- 1 , :]:
261
- ax .set_xlabel (x )
243
+ if kwargs .get ('add_colorbar' , True ):
244
+ self .add_colorbar ()
245
+
246
+ return self
262
247
248
+ def _finalize_grid (self , * axlabels ):
249
+ """Finalize the annotations and layout."""
250
+ self .set_axis_labels (* axlabels )
251
+ self .set_titles ()
263
252
self .fig .tight_layout ()
264
253
265
- if self ._single_group :
266
- for d , ax in zip (self .name_dicts .flat , self .axes .flat ):
267
- if d is None :
268
- ax .set_visible (False )
254
+ for ax , namedict in zip (self .axes .flat , self .name_dicts .flat ):
255
+ if namedict is None :
256
+ ax .set_visible (False )
269
257
270
- # colorbar
271
- if kwargs .get ('add_colorbar' , True ):
272
- cbar = self .fig .colorbar (self ._mappables [- 1 ],
273
- ax = list (self .axes .flat ),
274
- extend = cmap_params ['extend' ])
258
+ def add_colorbar (self , ** kwargs ):
259
+ """Draw a colorbar
260
+ """
261
+ kwargs = kwargs .copy ()
262
+ if self ._cmap_extend is not None :
263
+ kwargs .setdefault ('extend' , self ._cmap_extend )
264
+ if getattr (self .data , 'name' , None ) is not None :
265
+ kwargs .setdefault ('label' , self .data .name )
266
+ self .fig .colorbar (self ._mappables [- 1 ], ax = list (self .axes .flat ),
267
+ ** kwargs )
268
+ return self
275
269
276
- if self .data .name :
277
- cbar .set_label (self .data .name , rotation = 90 ,
278
- verticalalignment = 'bottom' )
270
+ def set_axis_labels (self , x_var = None , y_var = None ):
271
+ """Set axis labels on the left column and bottom row of the grid."""
272
+ if x_var is not None :
273
+ self ._x_var = x_var
274
+ self .set_xlabels (x_var )
275
+ if y_var is not None :
276
+ self ._y_var = y_var
277
+ self .set_ylabels (y_var )
278
+ return self
279
279
280
- self ._x_var = x
281
- self ._y_var = y
280
+ def set_xlabels (self , label = None , ** kwargs ):
281
+ """Label the x axis on the bottom row of the grid."""
282
+ if label is None :
283
+ label = self ._x_var
284
+ for ax in self ._bottom_axes :
285
+ ax .set_xlabel (label , ** kwargs )
286
+ return self
282
287
288
+ def set_ylabels (self , label = None , ** kwargs ):
289
+ """Label the y axis on the left column of the grid."""
290
+ if label is None :
291
+ label = self ._y_var
292
+ for ax in self ._left_axes :
293
+ ax .set_ylabel (label , ** kwargs )
283
294
return self
284
295
285
296
def set_titles (self , template = "{coord} = {value}" , maxchar = 30 ,
286
- fontsize = _FONTSIZE , ** kwargs ):
297
+ ** kwargs ):
287
298
"""
288
299
Draw titles either above each facet or on the grid margins.
289
300
@@ -293,8 +304,6 @@ def set_titles(self, template="{coord} = {value}", maxchar=30,
293
304
Template for plot titles containing {coord} and {value}
294
305
maxchar : int
295
306
Truncate titles at maxchar
296
- fontsize : string or int
297
- Passed to matplotlib.text
298
307
kwargs : keyword args
299
308
additional arguments to matplotlib.text
300
309
@@ -303,8 +312,9 @@ def set_titles(self, template="{coord} = {value}", maxchar=30,
303
312
self: FacetGrid object
304
313
305
314
"""
315
+ import matplotlib as mpl
306
316
307
- kwargs ['fontsize' ] = fontsize
317
+ kwargs ["size" ] = kwargs . pop ( "size" , mpl . rcParams [ "axes.labelsize" ])
308
318
309
319
nicetitle = functools .partial (_nicetitle , maxchar = maxchar ,
310
320
template = template )
@@ -394,6 +404,10 @@ def map(self, func, *args, **kwargs):
394
404
data = self .data .loc [namedict ]
395
405
plt .sca (ax )
396
406
innerargs = [data [a ].values for a in args ]
397
- func (* innerargs , ** kwargs )
407
+ # TODO: is it possible to verify that an artist is mappable?
408
+ mappable = func (* innerargs , ** kwargs )
409
+ self ._mappables .append (mappable )
410
+
411
+ self ._finalize_grid (* args [:2 ])
398
412
399
413
return self
0 commit comments