AMI Models¶
This package contains models for advanced metering infrastructure (AMI) data.
Methodology¶
Concepts and Approach¶
Our modeling approach to AMI data is to start with the basic mixture model, under the assumption that the data can at least be characterized in a piece-wise fashion. These pieces can also be referred to as components (e.g. in a mixture) or states (e.g. in a Markov chain). We use the latter term.
Our approach can be understood in phases, or as a process:
Piece-wise/mixture models.
Regression within the mixture states.
- This means that the behaviour within states could depend on weather and/or location.
Add (relative) time dependency to the occupancy and transitions between states via hidden Markov models (HMM).
Add further, possibly exogenous, dependencies to the transition probabilities.
- For example, time-of-day and/or weather can be made to increase the probability of being in a “lower” state of energy usage.
This state-wise interpretation provides an arguably simple means of gradually building up to more complicated models with more sophisticated considerations. It also provides an avenue for literal/physical interpretations of underlying processes, as well as easily comparable terms for before-and-after comparisons. In the case where states are ordered by magnitude, it becomes possible to speak in terms of “low” and “high” usage scenarios, and to compare these ordered states after assumed interventions/changes in data generating processes. At time it may also be possible to attribute these “low” states to physical considerations, such as an “off-time” or “vacation”. Since the states in these models need not appear at fixed or regular frequencies nor necessitate exogenous variables to (indirectly) indicate their presence, they are able to account for irregular, but not unexpected, events. One can imagine, “sick-days” for a residence, for which energy consumption could be increased relative to the average energy usage when the resident is at the office.
Note
The models we consider are broadly discretized state-space models.
Challenges¶
The challenges faced by our methodology include those standard to mixture modeling and regression. Specifically, over-fitting and model selection, both in the state regressions and the number of mixture components. Our use of regression within a mixture model can introduce more than the standard identification issues inherent to mixture modeling.
For instance, consider data generated by a process with easily distinguished fixed time-of-day changes in usage. The time-of-day terms can be used as factors in a regression for a model with only one state, or estimated as a simple constant means mixture with more states could
We take a global-local shrinkage approach to address some aspects of these problems. This approach is seen in our choice of priors for some terms.
In using Markov Chain Monte Carlo (MCMC) for estimation, these methods can incur a computational cost in comparison to standard point-wise estimation. However, the sample results of MCMC allow a broader picture of our model assumptions and its estimation. We’re able to more easily characterize complicated questions using the sample results, as well. Regardless, we’re not limited to MCMC estimation, since our formulations in terms of PyMC are symbolic and ultimately provide a total log likelihood that is amenable to most forms of point-wise estimation.
Usage¶
The current way to use these objects is to create/obtain a vector of observations,
y
, and a list of design matrices/features, X_matrices
, as Pandas
DataFrames or numpy.ndarrays. The length of the list implies the number
of mixture components/states in the model, and the individual matrices
determine the regression terms estimated in each state. If no exogenous
terms are included, i.e. the matrix is a column of ones, then only a single constant
term is estimated.
Next, one can run an initialization function to obtain initial values for the model parameters, then use one of the model generation functions to produce the PyMC objects. Given PyMC objects, the model can be sampled or used to estimate a MAP value (see the PyMC2 documentation).
Here is an example workflow:
from amimodels.normal_hmm import *
from amimodels.step_methods import *
demo_process = NormalHMMProcess(np.asarray([[0.9], [0.1]]),
200,
np.asarray([1., 0.]),
np.asarray([[0.2], [1.]]),
np.asarray([0.1/8.**2, 0.5/3.**2]),
seed=249298)
# Produce a simulation
states_true, y, X_matrices = demo_process.simulate()
# Construct some intial parameters to an HMM
init_params = gmm_norm_hmm_init_params(y, X_matrices)
# Build the model using our initial params.
norm_hmm = make_normal_hmm(y, X_matrices, init_params)
norm_mcmc = pymc.MCMC(norm_hmm.variables)
# If you want to use our special sampling step methods...
norm_mcmc.use_step_method(HMMStatesStep,
norm_hmm.states)
norm_mcmc.use_step_method(TransProbMatStep,
norm_hmm.trans_mat)
norm_mcmc.sample(1000)
For more examples, see this packages’ tests
and examples
folders.
API¶
amimodels package¶
Submodules¶
amimodels.deterministics module¶
This module provides PyMC Deterministic objects custom built for hidden Markov models.
-
class
amimodels.deterministics.
HMMLinearCombination
(name, X_matrices, betas, states, *args, **kwds)¶ Bases:
sphinx.ext.autodoc.Deterministic
A deterministic that represents
\[\mu_t = x_t^{(S_t)\top} \beta^{(S_t)}\]for a state sequence \(\{S_t\}_{t=1}^T\) with \(S_t \in \{1,\dots,K\}\), rows of design matrices \(x^{(k)}_t\), and covariate vectors \(\beta^{(k)}\).
This deterministic organizes, separates and tracks the aforementioned \(\mu_t\) in pieces designated by the current state sequence, \(S_t\). Specficially, it tracks a set containing the following sets for \(k \in \{1,\dots,K\}\)
\[\left\{ \mu^{(k)} = \tilde{X}^{(k)} \beta^{(k)}, \tilde{X}^{(k)} = X_{\mathcal{T}^{(k)}}, \mathcal{T}^{(k)} = \{t : S_t = k\} \right\}\]
amimodels.eemeter_tools module¶
-
amimodels.eemeter_tools.
read_meter_data
(trace_filename, project_info_filename, project_id=None, weather=True, merge_series=True)¶ Read meter data from a raw XML file source, obtain matching project information from a separate CSV file. Fetches the corresponding weather data, when requested, too.
Parameters: trace_filename: str
Filename of XML meter trace.
project_info_filename: str
Filename of CSV file containing project info.
project_id: str
Manually provide the project ID used in project_info_filename. If None, the first part of trace_filename before a _ is used.
weather: bool
True will obtain weather (temperature) data.
merge_series: bool
True will return a pandas.DataFrame with merged consumption and temperature data.
Returns: A DataCollection object with the following fields:
- project_info: pandas.DataFrame
Contains columns for project properties.
- baseline_end: pandas.Datetime
End date of the baseline period.
- consumption_data: eemeter.consumption.ConsumptionData
Consumption data object.
- consumption_data_freq: pandas.DataFrame
Consumption data with normalized frequency.
If
weather=True
:- weather_source: eemeter.ISDWeatherSource
Weather source object.
- weather_data: pandas.DataFrame
Temperature observations in, degF, with frequency matching consumption_data_freq. Values are averaged if raw temperature observations are lower frequency.
If
merge_series=True
:- cons_weather_data: pandas.DataFrame
Merged consumption and temperature data.
amimodels.hmm_utils module¶
-
amimodels.hmm_utils.
compute_steady_state
(trans_mat)¶ Compute the steady state of a transition probability matrix.
Parameters: trans_mat: numpy.array
A transition probability matrix for K states with shape (K, K-1), i.e. the last column omitted.
Returns: A numpy.array representing the steady state.
-
amimodels.hmm_utils.
compute_trans_freqs
(states, N_states, counts_only=False)¶ Computes empirical state transition frequencies.
Parameters: states: a pymc object or ndarray
Vector sequence of states.
N_states: int
Total number of observable states.
counts_only: boolean
Return only the transition counts for each state.
Returns: Unless counts_only is True, return the empirical state transition
frequencies; otherwise, return the transition counts for each state.
-
amimodels.hmm_utils.
plot_hmm
(mcmc_step, obs_index=None, axes=None, smpl_subset=None, states_true=None, plot_samples=True, range_slice=slice(0, None, None))¶ Plot the observations, estimated observation mean parameter’s statistics and estimated state sequence statistics.
Parameters: mcmc_res: a pymc.MCMC object
The MCMC object after estimation.
obs_index: pandas indices or None
Time series index for observations.
axes: list of matplotlib axes
Axes to use for plotting.
smpl_subset: float, numpy.ndarray of int, or None
If a float, the percent of samples to plot; if a numpy.ndarray, the samples to plot; if None, plot all samples.
states_true: pandas.DataFrame
True states time series.
plot_samples: bool
If True, plot the individual sample values, along with the means.
Returns: A matplotlib plot object.
amimodels.normal_hmm module¶
This module provides classes and functions for producing and simulating hidden Markov models (HMM) with scalar normal/Gaussian-distributed observations in PyMC.
-
class
amimodels.normal_hmm.
NormalHMMInitialParams
(alpha_trans, trans_mat, states, betas, Ws, Vs, p0)¶ Bases:
object
An object that holds initial parameters for a normal-observations HMM.
-
class
amimodels.normal_hmm.
NormalHMMProcess
(trans_mat, N_obs, p0, betas, Vs, exogenous_sim_func=None, formulas=None, start_datetime=None, seed=None)¶ Bases:
object
An object that produces simulations from a normal-observations HMM.
Methods
generate_exogenous
()Generate exogenous terms/covariates and time indices. simulate
()Simulate a series from this normal-emissions HMM. -
generate_exogenous
()¶ Generate exogenous terms/covariates and time indices. Override this if you will, but make sure the dimensions match self.betas.
Returns: index_sim
A pandas.DatetimeIndex.
X_matrices
A sequence of design matrices corresponding to each self.betas.
-
simulate
()¶ Simulate a series from this normal-emissions HMM.
Returns: states: numpy.array of int
Simulated state values.
y: pandas.DataFrame
Time series of simulated usage observations.
X_matrices: list of pandas.DataFrame
List of pandas.DataFrame`s for each `self.betas with designs given by self.formulas.
-
-
amimodels.normal_hmm.
bic_norm_hmm_init_params
(y, X_matrices)¶ Initialize a normal HMM regression mixture with a GMM mixture of a BIC determined number of states. Starting with an initial set of design matrices, this function searches for the best number of additional constant states to add to the model.
Parameters: y: pandas.DataFrame or pandas.Series
Time-indexed vector of observations.
X_matrices: list of pandas.DataFrame
Collection of design matrices for each initial state.
Returns: init_params:
A NormalHMMInitialParams object.
-
amimodels.normal_hmm.
calc_alpha_prior
(obs_states, N_states, trans_freqs=None)¶ A method of producing informed Dirichlet distribution parameters from observed states.
Parameters: obs_states: ndarray of int
Array of state label observations.
N_states: int
Total number of states (max integer label of observations).
trans_freqs: numpy.array of float
Empirical transition probabilities for obs_states sequence.
Returns: numpy.array of Dirichlet parameters initialized/updated by the observed
sequence.
-
amimodels.normal_hmm.
generate_temperatures
(ind, period=24.0, offset=0.0, base_temp=60.0, flux_amt=10.0)¶ Generate very regular temperature oscillations. This is roughly based on observations starting at 4/1/2015 in CA (in UTC!).
Parameters: ind: pandas.DatetimeIndex
Time index over which the temperatures will be computed.
period: float
Period of the sinusoid.
offset: float
Frequency offset.
base_temp: float
Temperature around which the sinusoid will fluctuate.
flux_amt: float
Scaling intensity of sinusoid.
-
amimodels.normal_hmm.
get_stochs_excluding
(stoch, excluding)¶ Get the parents of a stochastic excluding the given list of stochastic and/or parent names.
Parameters: stoch: pymc.Stochastic
Root stochastic/node.
excluding: list of str
Stochastic/node and parent names to exclude.
-
amimodels.normal_hmm.
gmm_norm_hmm_init_params
(y, X_matrices)¶ Generates initial parameters for the univariate normal-emissions HMM with normal mean priors.
Parameters: y: pandas.DataFrame or pandas.Series
Time-indexed vector of observations.
X_matrices: list of pandas.DataFrame
Collection of design matrices for each hidden state’s mean.
Returns: init_params:
A NormalHMMInitialParams object.
-
amimodels.normal_hmm.
make_normal_baseline_hmm
(y_data, X_data, baseline_end, initial_params)¶ Construct a PyMC2 scalar normal-emmisions HMM with a stochastic reporting period start time parameter and baseline, reporting parameters for all other stochastics/estimated terms in the model. The reporting period start time parameter is given a discrete uniform distribution starting from the first observation after the baseline to the end of the series.
Parameters: y_data: pandas.DataFrame
Usage/response observations.
X_data: list of pandas.DataFrame
List of design matrices for each state. Each must span the entire length of observations (i.e. y_data).
baseline_end: pandas.tslib.Timestamp
End of baseline period (inclusive), beginning of reporting period.
initial_params: NormalHMMInitialParams
An object containing the following fields/members:
Returns
=======
A pymc.Model object used for sampling.
-
amimodels.normal_hmm.
make_normal_hmm
(y_data, X_data, initial_params=None, single_obs_var=False, include_ppy=False)¶ Construct a PyMC2 scalar normal-emmisions HMM model of the form
\[\begin{split}y_t &\sim \operatorname{N}^{+}(x_t^{(S_t)\top} \beta^{(S_t)}, V^{(S_t)}) \\ \beta^{(S_t)}_i &\sim \operatorname{N}(m^{(S_t)}, C^{(S_t)}), \quad i \in \{1,\dots, M\} \\ S_t \mid S_{t-1} &\sim \operatorname{Categorical}(\pi^{(S_{t-1})}) \\ \pi^{(S_t-1)} &\sim \operatorname{Dirichlet}(\alpha^{(S_{t-1})})\end{split}\]where \(\operatorname{N}_{+}\) is the positive (truncated below zero) normal distribution, \(S_t \in \{1, \ldots, K\}\), \(C^{(S_t)} = \lambda_i^{(S_t) 2} \tau^{(S_t) 2}\) and
\[\begin{split}\lambda^{k}_i &\sim \operatorname{Cauchy}^{+}(0, 1) \\ \tau^{(k)} &\sim \operatorname{Cauchy}^{+}(0, 1) \\ V^{(k)} &\sim \operatorname{Gamma}(n_0/2, n_0 S_0/2)\end{split}\]for \(k \in \{1, \ldots, K\}\).
for observations \(y_t\) in \(t \in \{0, \dots, T\}\), features \(x_t^{(S_t)} \in \mathbb{R}^M\), regression parameters \(\beta^{(S_t)}\), state sequences \(\{S_t\}^T_{t=1}\) and state transition probabilities \(\pi \in [0, 1]^{K}\). \(\operatorname{Cauchy}^{+}\) is the standard half-Cauchy distribution and \(\operatorname{N}\) is the normal/Gaussian distribution.
The set of random variables, \(\mathcal{S} = \{\{\beta^{(k)}, \lambda^{(k)}, \tau^{(k)}, \tau^{(k)}, \pi^{(k)}\}_{k=1}^K, \{S_t\}^T_{t=1}\}\), are referred to as “stochastics” throughout the code.
Parameters: y_data: pandas.DataFrame
Usage/response observations \(y_t\).
X_data: list of pandas.DataFrame
List of design matrices for each state, i.e. \(x_t^{(S_t)}\). Each must span the entire length of observations (i.e. y_data).
initial_params: NormalHMMInitialParams
The initial parameters, which include \(\pi_0, m^{(k)}, \alpha^{(k)}, V^{(k)}\).
single_obs_var: bool, optional
Determines whether there are multiple observation variances or not. Only used when not given intial parameters.
include_ppy: bool, optional
If True, then include an unobserved observation Stochastic that can be used to produce posterior predicitve samples. The Stochastic will have the name y_pp.
Returns: A
pymc.Model
object used for sampling.
-
amimodels.normal_hmm.
make_poisson_hmm
(y_data, X_data, initial_params)¶ Construct a PyMC2 scalar poisson-emmisions HMM model.
TODO: Update to match normal model design.
The model takes the following form:
\[\begin{split}y_t &\sim \operatorname{Poisson}(\exp(x_t^{(S_t)\top} \beta^{(S_t)})) \\ \beta^{(S_t)}_i &\sim \operatorname{N}(m^{(S_t)}, C^{(S_t)}), \quad i \in \{1,\dots,M\} \\ S_t \mid S_{t-1} &\sim \operatorname{Categorical}(\pi^{(S_{t-1})}) \\ \pi^{(S_t-1)} &\sim \operatorname{Dirichlet}(\alpha^{(S_{t-1})})\end{split}\]where \(C^{(S_t)} = \lambda_i^{(S_t) 2} \tau^{(S_t) 2}\) and
\[\begin{split}\lambda^{(S_t)}_i &\sim \operatorname{Cauchy}^{+}(0, 1) \\ \tau^{(S_t)} &\sim \operatorname{Cauchy}^{+}(0, 1)\end{split}\]for observations \(y_t\) in \(t \in \{0, \dots, T\}\), features \(x_t^{(S_t)} \in \mathbb{R}^M\), regression parameters \(\beta^{(S_t)}\), state sequences \(\{S_t\}^T_{t=1}\) and state transition probabilities \(\pi \in [0, 1]^{K}\). \(\operatorname{Cauchy}^{+}\) is the standard half-Cauchy distribution and \(\operatorname{N}\) is the normal/Gaussian distribution.
The set of random variables, \(\mathcal{S} = \{\{\beta^{(k)}, \lambda^{(k)}, \tau^{(k)}, \tau^{(k)}, \pi^{(k)}\}_{k=1}^K, \{S_t\}^T_{t=1}\}\), are referred to as “stochastics” throughout the code.
Parameters: y_data: pandas.DataFrame
Usage/response observations \(y_t\).
X_data: list of pandas.DataFrame
List of design matrices for each state, i.e. \(x_t^{(S_t)}\). Each must span the entire length of observations (i.e. y_data).
initial_params: NormalHMMInitialParams
The initial parameters, which include \(\pi_0, m^{(k)}, \alpha^{(k)}, V^{(k)}\). Ignores V parameters. FIXME: using the “Normal” initial params objects is only temporary.
Returns: A
pymc.Model
object used for sampling.
-
amimodels.normal_hmm.
trace_sampler
(model, stoch, traces, dbname=None)¶ Creates a PyMC ram database for stochastic given a model and set of trace values for its parent stochastics.
Parameters: model: pymc.Model object
The model object.
stoch: pymc.Stochastic or str
The stochastic, or name, for which we want values under the given samples in traces.
traces: dict of str, numpy.ndarray
A dictionary of stoch‘s parents’ stochastic names and trace values
Returns: A pymc.database.ram.Database.
amimodels.step_methods module¶
amimodels.stochastics module¶
-
class
amimodels.stochastics.
HMMStateSeq
(name, trans_mat, N_obs, p0=None, *args, **kwargs)¶ Bases:
sphinx.ext.autodoc.Stochastic
A stochastic that represents an HMM’s state process \(\{S_t\}_{t=1}^T\). It’s basically the distribution of a sequence of Categorical distributions connected by a discrete Markov transition probability matrix.
Use the step methods made specifically for this distribution; otherwise, the default Metropolis samplers will likely perform too poorly.
Parameters: trans_mat: ndarray
A transition probability matrix for K-many states with shape (K, K-1).
N_obs: int
Number of observations.
p0: ndarray
Initial state probabilities. If None, the steady state is computed and used.
value: ndarray of int
Initial value array of discrete states numbers/indices/labels.
size: int
Not used.
See also
pymc.PyMCObjects
- Stochastic
-
class
amimodels.stochastics.
TransProbMatrix
(name, alpha_trans, *args, **kwargs)¶ Bases:
sphinx.ext.autodoc.Stochastic
A stochastic that represents an HMM’s transition probability matrix with rows given by
\[\pi^{(k)} \sim \operatorname{Dir}(\alpha^{(k)}) \;,\]for \(k \in \{1, \dots, K\}\).
This object technically works with the \(K-1\)-many columns of transition probabilities, and each row is represented a Dirichlet distribution (in the \(K-1\) independent terms.
Parameters: alpha_trans: ndarray
Dirichlet parameters for each row of the transition probability matrix.
value: ndarray of int
Initial value.
See also
pymc.PyMCObjects
- Stochastic
-
amimodels.stochastics.
states_logp
(value, trans_mat, N_obs, p0)¶ Computes log likelihood of states in an HMM.
Parameters: value: ndarray of int
Array of discrete states numbers/indices/labels
trans_mat: ndarray
A transition probability matrix for K states with shape (K, K-1), i.e. the last column omitted.
N_obs: int
Number of observations.
p0: ndarray
Initial state probabilities. If None, the steady state is computed and used.
Returns: float value of the log likelihood
-
amimodels.stochastics.
states_random
(trans_mat, N_obs, p0, size=None)¶ Samples states from an HMM.
Parameters: trans_mat: ndarray
A transition probability matrix for K-many states with shape (K, K-1).
N_obs: int
Number of observations.
p0: ndarray
Initial state probabilities. If None, the steady state is computed and used.
size: int
Not used.
Returns: A ndarray of length N_obs containing sampled state numbers/indices/labels.
-
amimodels.stochastics.
trans_mat_logp
(value, alpha_trans)¶ Computes the log probability of a transition probability matrix for the given Dirichlet parameters.
Parameters: value: ndarray
The observations.
alpha_trans: ndarray
The Dirichlet parameters.
Returns: The log probability.
-
amimodels.stochastics.
trans_mat_random
(alpha_trans)¶ Sample a shape (K, K-1) transition probability matrix with K-many states given Dirichlet parameters for each row.
Parameters: alpha_trans: ndarray
Dirichlet parameters for each row.
Returns: A ndarray of the sampled transition probability matrix (without the
last column).
amimodels.testing module¶
-
amimodels.testing.
assert_hpd
(stochastic, true_value, alpha=0.05, subset=slice(0, None, None), rtol=0)¶ Assert that the given stochastic’s \((1-\alpha)\) HPD interval covers the true value.
Parameters: stochastic: a pymc.Stochastic
The stochastic we want to test. Must have trace values.
true_value: ndarray
The “true” values to check against.
alpha: float, optional
The alpha confidence level.
subset: slice, optional
Slice for the subset of parameters to check.
rtol: array of float, optional
Relative tolerences for matching the edges of the intervals.
edge
Returns: Nothing, just exec’s the assert statements.
-
amimodels.testing.
simple_norm_reg_model
(N_obs=100, X_matrices=None, betas=None, trans_mat_obs=array([[ 0.9], [ 0.1]]), tau_y=100, y_obs=None)¶ A simple normal observations/emissions HMM regression model with fixed transition matrix. Uses HMMStateSeq and HMMLinearCombination to model the HMM states and observation mean, respectively.
This is useful for generating simple test/toy data according to a completely known model and parameters.
Parameters: N_obs: int
Number of observations.
X_matrices: list of ndarray or None
List of design matrices for the regression parameters. If None, design matrices are generated for the given betas (which themselves will be generated if None).
betas: list of ndarray or pymc.Stochastic
List of regression parameters matching X_matrices. If None, these are randomly generated according to the shapes of X_matrices.
trans_mat_obs: ndarray or pymc.Stochastic
Transition probability matrix.
tau_y: float or ndarray
Observation variance.
y_obs: ndarray
A vector of observations. If None, a non-observed pymc.Stochastic is produced.
Returns: A dictionary containing all the variables in the model.
-
amimodels.testing.
simple_state_seq_model
(mu_vals=array([-1, 1]), trans_mat_obs=array([[ 0.9], [ 0.1]]), N_obs=100, tau_y=100, y_obs=None)¶ A simple normal observations/emissions HMM model with fixed transition matrix. Uses HMMStateSeq and an array-indexing deterministic for the observations’ means.
This is useful for generating simple test/toy data according to a completely known model and parameters.
Parameters: mu_vals: list of ndarray or pymc.Stochastic
List of mean values for the observations/emissions of each state.
trans_mat_obs: ndarray or pymc.Stochastic
Transition probability matrix.
N_obs: int
Number of observations.
tau_y: float or ndarray
Observation variance.
y_obs: ndarray
A vector of observations. If None, a non-observed pymc.Stochastic is produced.
Returns: A dictionary containing all the variables in the model.
-
amimodels.testing.
simple_state_trans_model
(mu_vals=array([-1, 1]), alpha_trans=array([[ 1., 10.], [ 10., 1.]]), N_obs=100, tau_y=100, y_obs=None)¶ A simple normal observations/emissions HMM model with a stochastic transition matrix. Uses HMMStateSeq, TransProbMatrix and an array-indexing deterministic for the observations’ means.
This is useful for generating simple test/toy data according to a completely known model and parameters.
Parameters: mu_vals: list of ndarray or pymc.Stochastic
List of mean values for the observations/emissions of each state.
alpha_trans: ndarray
Dirichlet hyper-parameters for the transition probability matrix’s prior.
N_obs: int
Number of observations.
tau_y: float or ndarray
Observation variance.
y_obs: ndarray
A vector of observations. If None, a non-observed pymc.Stochastic is produced.
Returns: A dictionary containing all the variables in the model.