I am trying to build a Bayesian hierarchical time series model to understand sales data of four stores using PyMC 5.4 in Python. All the stores have a seasonal component, that I am trying to model using a Fourier series. However, for the sake of simplicity, I decided to start with the linear model below and later add the Fourier series (see commented PyMC code below).
The variables I'm using in the linear model are:
- stores: array([2, 5, 6, 7]). These are the stores names.
- coords: dictionary = {'fourier_features': array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]), 'stores': array([2, 5, 6, 7])}
- t: array, with shape (4, 59). Time variable. Four stores, each with 59 monthly time observations
- y_sales: array, with shape (4, 59). Sales variable. Four stores, each with 59 monthly sales observations
The linear model is as follows:
with pm.Model(check_bounds=False, coords=coords) as partial_pooled_linear:
# Hyperpriors
mu_alpha = pm.Normal("mu_alpha", mu=0, sigma=0.5)
mu_beta = pm.Normal("mu_beta", mu=0, sigma=0.5)
mu_sigma = pm.HalfNormal("mu_sigma", sigma=0.1)
#β_fourier = pm.Normal("β_fourier", mu=0, sigma=0.01, dims="fourier_features")
#seasonality = pm.Deterministic("seasonality", pm.math.dot(β_fourier,
# fourier_features.to_numpy().T))
# Store-level priors
alpha = pm.Normal("alpha", mu=mu_alpha, sigma=1, dims=("stores",))
beta = pm.Normal("beta", mu=mu_beta, sigma=1, dims=("stores",))
sigma = pm.HalfNormal("sigma", sigma=mu_sigma, dims=("stores",))
# Define trend for each store
trend = pm.Deterministic("trend", alpha + (beta * t).T)
#mu = pm.Deterministic("mu", alpha[stores] + trend, dims=("stores"))
# Likelihood for each store
y = pm.Normal("likelihood", mu=trend, observed=y_sales)
I am getting the following error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/elemwise.py:441, in Elemwise.get_output_info(self, dim_shuffle, *inputs)
439 try:
440 out_shapes = [
--> 441 [
442 get_most_specialized_shape(shape)
443 for shape in zip(*[inp.type.shape for inp in inputs])
444 ]
445 ] * shadow.nout
446 except ValueError:
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/elemwise.py:442, in (.0)
439 try:
440 out_shapes = [
441 [
--> 442 get_most_specialized_shape(shape)
443 for shape in zip(*[inp.type.shape for inp in inputs])
444 ]
445 ] * shadow.nout
446 except ValueError:
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/elemwise.py:434, in Elemwise.get_output_info..get_most_specialized_shape(shapes)
433 if len(shapes) > 1:
--> 434 raise ValueError
435 return tuple(shapes)[0]
ValueError:
During handling of the above exception, another exception occurred:
ValueError Traceback (most recent call last)
Cell In[66], line 17
14 sigma = pm.HalfNormal("sigma", sigma=mu_sigma, dims=("stores",))
16 # Define trend for each store
---> 17 trend = pm.Deterministic("trend", alpha + (beta * t).T)
18 #mu = pm.Deterministic("mu", alpha[stores] + trend, dims=("stores"))
19
20 # Likelihood for each store
21 y = pm.Normal("likelihood", mu=trend, observed=y_sales)
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/var.py:133, in _tensor_py_operators.__mul__(self, other)
129 def __mul__(self, other):
130 # See explanation in __add__ for the error caught
131 # and the return value in that case
132 try:
--> 133 return at.math.mul(self, other)
134 except (NotImplementedError, TypeError):
135 return NotImplemented
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/graph/op.py:295, in Op.__call__(self, *inputs, **kwargs)
253 r"""Construct an `Apply` node using :meth:`Op.make_node` and return its outputs.
254
255 This method is just a wrapper around :meth:`Op.make_node`.
(...)
292
293 """
294 return_list = kwargs.pop("return_list", False)
--> 295 node = self.make_node(*inputs, **kwargs)
297 if config.compute_test_value != "off":
298 compute_test_value(node)
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/elemwise.py:485, in Elemwise.make_node(self, *inputs)
479 """
480 If the inputs have different number of dimensions, their shape
481 is left-completed to the greatest number of dimensions with 1s
482 using DimShuffle.
483 """
484 inputs = [as_tensor_variable(i) for i in inputs]
--> 485 out_dtypes, out_shapes, inputs = self.get_output_info(DimShuffle, *inputs)
486 outputs = [
487 TensorType(dtype=dtype, shape=shape)()
488 for dtype, shape in zip(out_dtypes, out_shapes)
489 ]
490 return Apply(self, inputs, outputs)
File ~/opt/anaconda3/envs/pymc_env/lib/python3.11/site-packages/pytensor/tensor/elemwise.py:447, in Elemwise.get_output_info(self, dim_shuffle, *inputs)
440 out_shapes = [
441 [
442 get_most_specialized_shape(shape)
443 for shape in zip(*[inp.type.shape for inp in inputs])
444 ]
445 ] * shadow.nout
446 except ValueError:
--> 447 raise ValueError(
448 f"Incompatible Elemwise input shapes {[inp.type.shape for inp in inputs]}"
449 )
451 # inplace_pattern maps output idx -> input idx
452 inplace_pattern = self.inplace_pattern
ValueError: Incompatible Elemwise input shapes [(1, 4), (4, 59)]