@@ -271,20 +271,33 @@ def _validate_seasons(seasons):
271271 return
272272
273273
274- def _month_year_adjusts (seasons ):
275- """Compute the year adjustments required for each month.
274+ def _month_year_adjusts (seasons , use_year_at_season_start = False ):
275+ """
276+ Compute the year adjustments required for each month.
276277
277- These determine whether the month belongs to a season in the same
278- year or is in the start of a season that counts towards the next
279- year.
278+ These adjustments ensure that no season spans two years by assigning months
279+ to the **next** year (use_year_at_season_start is False) or the
280+ **previous** year (use_year_at_season_start is True). E.g. Winter - djf:
281+ either assign Dec to the next year, or Jan and Feb to the previous year.
280282
281283 """
282- month_year_adjusts = [None , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ]
284+ # 1 'slot' for each month, with an extra leading 'slot' because months
285+ # are 1-indexed - January is 1, therefore corresponding to the 2nd
286+ # array index.
287+ month_year_adjusts = np .zeros (13 , dtype = int )
288+
283289 for season in seasons :
284- months = _months_in_season (season )
285- for month in months :
286- if month > months [- 1 ]:
287- month_year_adjusts [month ] = 1
290+ months = np .array (_months_in_season (season ))
291+ if use_year_at_season_start :
292+ months_to_shift = months < months [0 ]
293+ year_shift = - 1
294+ else :
295+ # Sending forwards.
296+ months_to_shift = months > months [- 1 ]
297+ year_shift = 1
298+ indices_to_shift = months [np .flatnonzero (months_to_shift )]
299+ month_year_adjusts [indices_to_shift ] = year_shift
300+
288301 return month_year_adjusts
289302
290303
@@ -383,34 +396,40 @@ def _season_number(coord, value):
383396
384397
385398def add_season_year (
386- cube , coord , name = "season_year" , seasons = ("djf" , "mam" , "jja" , "son" )
399+ cube ,
400+ coord ,
401+ name = "season_year" ,
402+ seasons = ("djf" , "mam" , "jja" , "son" ),
403+ use_year_at_season_start = False ,
387404):
388405 """
389- Add a categorical year-of-season coordinate, with user specified
390- seasons.
391-
392- Args:
393-
394- * cube (:class:`iris.cube.Cube`):
395- The cube containing 'coord'. The new coord will be added into
396- it.
397- * coord (:class:`iris.coords.Coord` or string):
398- Coordinate in 'cube', or its name, representing time.
399-
400- Kwargs:
401-
402- * name (string):
403- Name of the created coordinate. Defaults to "season_year".
404- * seasons (:class:`list` of strings):
406+ Add a categorical year-of-season coordinate, with user specified seasons.
407+
408+ Parameters
409+ ----------
410+ cube : :class:`iris.cube.Cube`
411+ The cube containing `coord`. The new coord will be added into it.
412+ coord : :class:`iris.coords.Coord` or str
413+ Coordinate in `cube`, or its name, representing time.
414+ name : str, default="season_year"
415+ Name of the created coordinate.
416+ seasons : tuple of str, default=("djf", "mam", "jja", "son")
405417 List of seasons defined by month abbreviations. Each month must
406418 appear once and only once. Defaults to standard meteorological
407- seasons ('djf', 'mam', 'jja', 'son').
419+ seasons (``djf``, ``mam``, ``jja``, ``son``).
420+ use_year_at_season_start: bool, default=False
421+ Seasons spanning the year boundary (e.g. Winter ``djf``) will belong
422+ fully to the following year by default (e.g. the year of Jan and Feb).
423+ Set to ``True`` for spanning seasons to belong to the preceding
424+ year (e.g. the year of Dec) instead.
408425
409426 """
410427 # Check that the seasons are valid.
411428 _validate_seasons (seasons )
412429 # Define the adjustments to be made to the year.
413- month_year_adjusts = _month_year_adjusts (seasons )
430+ month_year_adjusts = _month_year_adjusts (
431+ seasons , use_year_at_season_start = use_year_at_season_start
432+ )
414433
415434 # Define a categorisation function.
416435 def _season_year (coord , value ):
0 commit comments