from statsmodels.tsa.statespace import tools class ExtendedDFM ( sm . tsa . DynamicFactor ): def __init__ ( self , endog , ** kwargs ): # Setup the model as if we had a factor order of 4 super ( ExtendedDFM , self ) . __init__ ( endog , k_factors = 1 , factor_order = 4 , error_order = 2 , ** kwargs ) # Note: `self.parameters` is an ordered dict with the # keys corresponding to parameter types, and the values # the number of parameters of that type. # Add the new parameters self . parameters [ 'new_loadings' ] = 3 # Cache a slice for the location of the 4 factor AR # parameters (a_1, ..., a_4) in the full parameter vector offset = ( self . parameters [ 'factor_loadings' ] + self . parameters [ 'exog' ] + self . parameters [ 'error_cov' ]) self . _params_factor_ar = np . s_ [ offset : offset + 2 ] self . _params_factor_zero = np . s_ [ offset + 2 : offset + 4 ] @property def start_params ( self ): # Add three new loading parameters to the end of the parameter # vector, initialized to zeros (for simplicity; they could # be initialized any way you like) return np . r_ [ super ( ExtendedDFM , self ) . start_params , 0 , 0 , 0 ] @property def param_names ( self ): # Add the corresponding names for the new loading parameters # (the name can be anything you like) return super ( ExtendedDFM , self ) . param_names + [ 'loading.L %d .f1. %s ' % ( i , self . endog_names [ 3 ]) for i in range ( 1 , 4 )] def transform_params ( self , unconstrained ): # Perform the typical DFM transformation (w/o the new parameters) constrained = super ( ExtendedDFM , self ) . transform_params ( unconstrained [: - 3 ]) # Redo the factor AR constraint, since we only want an AR(2), # and the previous constraint was for an AR(4) ar_params = unconstrained [ self . _params_factor_ar ] constrained [ self . _params_factor_ar ] = ( tools . constrain_stationary_univariate ( ar_params )) # Return all the parameters return np . r_ [ constrained , unconstrained [ - 3 :]] def untransform_params ( self , constrained ): # Perform the typical DFM untransformation (w/o the new parameters) unconstrained = super ( ExtendedDFM , self ) . untransform_params ( constrained [: - 3 ]) # Redo the factor AR unconstrained, since we only want an AR(2), # and the previous unconstrained was for an AR(4) ar_params = constrained [ self . _params_factor_ar ] unconstrained [ self . _params_factor_ar ] = ( tools . unconstrain_stationary_univariate ( ar_params )) # Return all the parameters return np . r_ [ unconstrained , constrained [ - 3 :]] def update ( self , params , transformed = True , ** kwargs ): # Peform the transformation, if required if not transformed : params = self . transform_params ( params ) params [ self . _params_factor_zero ] = 0 # Now perform the usual DFM update, but exclude our new parameters super ( ExtendedDFM , self ) . update ( params [: - 3 ], transformed = True , ** kwargs ) # Finally, set our new parameters in the design matrix self . ssm [ 'design' , 3 , 1 : 4 ] = params [ - 3 :]