def iter_boolean_groups_from_time_regions(time_regions, tvar, yield_subset=False, raise_if_incomplete=True): """ :param time_regions: Sequence of nested time region dictionaries. >>> [[{'month':[1,2],'year':[2024]},...],...] :param tvar: A temporal variable object. :type tvar: :class:`ocgis.TemporalVariable` :param bool yield_subset: If ``True``, yield a tuple with the subset of ``tvar``. :param bool raise_if_incomplete: If ``True``, raise an exception if the season is incomplete. :returns: boolean ndarray vector with yld.shape == tvar.shape :raises: IncompleteSeasonError """ for sub_time_regions in time_regions: # incomplete seasons are searched for in the nested loop. this indicates if a time region group should be # considered a season. is_complete = True idx_append = np.array([], dtype=int) for time_region in sub_time_regions: sub, idx = tvar.get_time_region(time_region, return_indices=True) # insert a check to ensure there are months present for each time region months = set([d.month for d in sub.value_datetime]) try: assert (months == set(time_region['month'])) except AssertionError: if raise_if_incomplete: for m in time_region['month']: if m not in months: raise IncompleteSeasonError(time_region, month=m) else: is_complete = False idx_append = np.append(idx_append, idx) # if the season is complete append, otherwise pass to next iteration. if is_complete: dgroup = np.zeros(tvar.shape[0], dtype=bool) dgroup[idx_append] = True else: continue if yield_subset: yld = (dgroup, tvar[dgroup]) else: yld = dgroup yield yld
def get_time_regions(seasons, dates, raise_if_incomplete=True): """ >>> seasons = [[6,7,8],[9,10,11],[12,1,2]] >>> dates = <vector of datetime objects> """ # extract the years from the data vector collapsing them to a unique set then sort in ascending order years = list(set([d.year for d in dates])) years.sort() # determine if any of the seasons are interannual interannual_check = list(map(get_is_interannual, seasons)) # holds the return value time_regions = [] # the interannual cases requires two time region sequences to properly extract if any(interannual_check): # loop over years first to ensure each year is accounted for in the time region output for ii_year, year in enumerate(years): # the interannual flag is used internally for simple optimization for ic, cg in zip(interannual_check, seasons): # if no exception is raised for an incomplete season, this flag indicate whether to append to the output append_to_time_regions = True if ic: # copy and sort in descending order the season because december of the current year should be first. _cg = deepcopy(cg) _cg.sort() _cg.reverse() # look for the interannual break and split the season into the current year and next year. diff = np.abs(np.diff(_cg)) split_base = np.arange(1, len(_cg)) split_indices = split_base[diff > 1] split = np.split(_cg, split_indices) # will hold the sub-element time regions sub_time_region = [] for ii_split, s in enumerate(split): try: to_append_sub = {'year': [years[ii_year + ii_split]], 'month': s.tolist()} sub_time_region.append(to_append_sub) # there may not be another year of data for an interannual season. we DO NOT keep incomplete # seasons. except IndexError: # don't just blow through an incomplete season unless asked to if raise_if_incomplete: raise IncompleteSeasonError(None, None, None) else: append_to_time_regions = False continue to_append = sub_time_region else: to_append = [{'year': [year], 'month': cg}] if append_to_time_regions: time_regions.append(to_append) # without interannual seasons the time regions are unique combos of the years and seasons designations else: for year, season in itertools.product(years, seasons): time_regions.append([{'year': [year], 'month': season}]) # ensure each time region is valid. if it is not, remove it from the returned list td = TemporalVariable(value=dates, dimensions=constants.DimensionName.TEMPORAL) remove = [] for idx, time_region in enumerate(time_regions): try: for sub_time_region in time_region: td.get_time_region(sub_time_region) except EmptySubsetError: remove.append(idx) for xx in remove: time_regions.pop(xx) return time_regions