Source code for calliope.time_masks
"""
Copyright (C) 2013-2017 Calliope contributors listed in AUTHORS.
Licensed under the Apache 2.0 License (see LICENSE file).
time_masks.py
~~~~~~~~~~~~~
Functions to pick timesteps from data given certain criteria.
"""
import pandas as pd
from . import time_funcs
def _get_array(data, var, tech, locations):
arr = data[var]
arr = arr.loc[{'y': tech}]
if locations is not None:
arr = arr.loc[{'x': locations}]
return arr
[docs]def zero(data, tech, var='r', locations=None):
"""
Returns timesteps where ``var`` for the technology ``tech``
across the given list of ``locations`` is zero.
If ``locations`` not given, uses all available locations.
"""
arr = _get_array(data, var, tech, locations)
s = arr.mean(dim='x').to_pandas() # Get a t-indexed Series
return s[s == 0].index
def _concat_indices(indices):
return pd.concat([i.to_series() for i in indices]).sort_index().index
def _get_minmax_timestaps(series, length, n, how='max', padding=None):
# Get the max/min timestamps
group = series.groupby(pd.TimeGrouper(length)).mean()
timesteps = []
for _ in range(n):
if how == 'max':
ts = group.idxmax()
elif how == 'min':
ts = group.idxmin()
timesteps.append(ts)
group = group.drop(ts)
# Get range of timestamps including padding
full_timesteps = []
for ts in timesteps:
ts_end = ts + pd.Timedelta(length)
if padding is not None:
ts -= pd.Timedelta(padding)
ts_end += pd.Timedelta(padding)
ts_range = pd.date_range(ts, ts_end, freq='1H')[:-1]
full_timesteps.append(ts_range)
ts_index = _concat_indices(full_timesteps)
return ts_index
[docs]def extreme(data, tech, var='r', how='max',
length='1D', n=1, groupby_length=None,
locations=None, padding=None):
"""
Returns timesteps for period of ``length`` where ``var`` for the technology
``tech`` across the given list of ``locations`` is either minmal
or maximal.
Parameters
----------
data : xarray.Dataset
tech : str
Technology whose `var` to find extreme for.
var : str, optional
default 'r'
how : str, optional
'max' (default) or 'min'.
length : str, optional
Defaults to '1D'.
n : int, optional
Number of periods of `length` to look for, default is 1.
groupby_length : str, optional
Group time series and return `n` periods of `length`
for each group.
locations : list, optional
List of locations to use, if None, uses all available locations.
padding : int, optional
Pad beginning and end of the unmasked area by the number of
timesteps given.
normalize : bool, optional
If True (default), data is normalized
using :func:`~calliope.time_funcs.normalized_copy`.
"""
arr = _get_array(data, var, tech, locations)
return _extreme(arr, how, length, n, groupby_length, padding)
def extreme_diff(data, tech0, tech1, var='r', how='max',
length='1D', n=1, groupby_length=None,
locations=None, padding=None, normalize=True):
if normalize:
data_n = time_funcs.normalized_copy(data)
else:
data_n = data
arr0 = _get_array(data_n, var, tech0, locations)
arr1 = _get_array(data_n, var, tech1, locations)
arr = arr0 - arr1
return _extreme(arr, how, length, n, groupby_length, padding)
def _extreme(arr, how='max',
length='1D', n=1, groupby_length=None,
padding=None):
full_series = arr.mean(dim='x').to_pandas() # Get a t-indexed Series
if groupby_length:
groupby = pd.TimeGrouper(groupby_length)
group_indices = []
grouping = full_series.groupby(groupby)
for k in grouping.groups.keys():
s = grouping.get_group(k)
group_indices.append(_get_minmax_timestaps(s, length, n, how, padding))
ts_index = _concat_indices(group_indices)
else:
ts_index = _get_minmax_timestaps(full_series, length, n, how, padding)
return ts_index
_WEEK_DAY_FUNCS = {
'extreme': extreme,
'extreme_diff': extreme_diff
}
def week(data, day_func, **day_func_kwargs):
# Get extreme day time index
func = _WEEK_DAY_FUNCS[day_func]
day = func(data, **day_func_kwargs)
# Using day of week, figure out how many days before and after to get
# a complete week
days_before = 6 - day[0].dayofweek
days_after = 6 - days_before
# Turn it into a week
# FIXME: assumes 1H timestep length
start_hour = day[0] - pd.Timedelta('{}D'.format(days_before))
end_hour = day[-1] + pd.Timedelta('{}D'.format(days_after))
before = pd.date_range(start_hour, day[0], freq='1H')[:-1]
after = pd.date_range(day[-1], end_hour, freq='1H')[1:]
result_week = before.append(day).append(after)
return result_week