113 lines
3.5 KiB
Python
113 lines
3.5 KiB
Python
|
'''Paneldata model with fixed effect (constants) and AR(1) errors
|
||
|
|
||
|
checking fast evaluation of groupar1filter
|
||
|
quickly written to try out grouparfilter without python loops
|
||
|
|
||
|
maybe the example has MA(1) not AR(1) errors, I'm not sure and changed this.
|
||
|
|
||
|
results look good, I'm also differencing the dummy variable (constants) ???
|
||
|
e.g. nobs = 35
|
||
|
true 0.6, 10, 20, 30 (alpha, mean_0, mean_1, mean_2)
|
||
|
estimate 0.369453125 [ 10.14646929 19.87135086 30.12706505]
|
||
|
|
||
|
Currently minimizes ssr but could switch to minimize llf, i.e. conditional MLE.
|
||
|
This should correspond to iterative FGLS, where data are AR(1) transformed
|
||
|
similar to GLSAR ?
|
||
|
Result statistic from GLS return by OLS on transformed data should be
|
||
|
asymptotically correct (check)
|
||
|
|
||
|
Could be extended to AR(p) errors, but then requires panel with larger T
|
||
|
|
||
|
'''
|
||
|
|
||
|
|
||
|
import numpy as np
|
||
|
from scipy import optimize
|
||
|
|
||
|
from statsmodels.regression.linear_model import OLS
|
||
|
|
||
|
|
||
|
class PanelAR1:
|
||
|
def __init__(self, endog, exog=None, groups=None):
|
||
|
#take this from a super class, no checking is done here
|
||
|
nobs = endog.shape[0]
|
||
|
self.endog = endog
|
||
|
if exog is not None:
|
||
|
self.exog = exog
|
||
|
|
||
|
self.groups_start = (np.diff(groups)!=0)
|
||
|
self.groups_valid = ~self.groups_start
|
||
|
|
||
|
def ar1filter(self, xy, alpha):
|
||
|
#print(alpha,)
|
||
|
return (xy[1:] - alpha * xy[:-1])[self.groups_valid]
|
||
|
|
||
|
def fit_conditional(self, alpha):
|
||
|
y = self.ar1filter(self.endog, alpha)
|
||
|
x = self.ar1filter(self.exog, alpha)
|
||
|
res = OLS(y, x).fit()
|
||
|
return res.ssr #res.llf
|
||
|
|
||
|
|
||
|
def fit(self):
|
||
|
alpha0 = 0.1 #startvalue
|
||
|
func = self.fit_conditional
|
||
|
fitres = optimize.fmin(func, alpha0)
|
||
|
|
||
|
# fit_conditional only returns ssr for now
|
||
|
alpha = fitres[0]
|
||
|
y = self.ar1filter(self.endog, alpha)
|
||
|
x = self.ar1filter(self.exog, alpha)
|
||
|
reso = OLS(y, x).fit()
|
||
|
|
||
|
return fitres, reso
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
|
||
|
#------------ development code for groupar1filter and example
|
||
|
groups = np.array([0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,
|
||
|
2,2,2,2,2,2,2,2])
|
||
|
nobs = len(groups)
|
||
|
data0 = np.arange(nobs)
|
||
|
|
||
|
data = np.arange(1,nobs+1) - 0.5*np.arange(nobs) + 0.1*np.random.randn(nobs)
|
||
|
|
||
|
y00 = 0.5*np.random.randn(nobs+1)
|
||
|
|
||
|
# I do not think a trend is handled yet
|
||
|
data = np.arange(nobs) + y00[1:] + 0.2*y00[:-1] + 0.1*np.random.randn(nobs)
|
||
|
#Are these AR(1) or MA(1) errors ???
|
||
|
data = y00[1:] + 0.6*y00[:-1] #+ 0.1*np.random.randn(nobs)
|
||
|
|
||
|
group_codes = np.unique(groups)
|
||
|
group_dummy = (groups[:,None] == group_codes).astype(int)
|
||
|
|
||
|
groups_start = (np.diff(groups)!=0)
|
||
|
groups_valid = (np.diff(groups)==0) #this applies to y with length for AR(1)
|
||
|
#could use np.nonzero for index instead
|
||
|
|
||
|
y = data + np.dot(group_dummy, np.array([10, 20, 30]))
|
||
|
y0 = data0 + np.dot(group_dummy, np.array([10, 20, 30]))
|
||
|
|
||
|
print(groups_valid)
|
||
|
print(np.diff(y)[groups_valid])
|
||
|
|
||
|
alpha = 1 #test with 1
|
||
|
print((y0[1:] - alpha*y0[:-1])[groups_valid])
|
||
|
alpha = 0.2 #test with 1
|
||
|
print((y0[1:] - alpha*y0[:-1] + 0.001)[groups_valid])
|
||
|
#this is now AR(1) for each group separately
|
||
|
|
||
|
|
||
|
#------------
|
||
|
|
||
|
#fitting the example
|
||
|
|
||
|
exog = np.ones(nobs)
|
||
|
exog = group_dummy
|
||
|
mod = PanelAR1(y, exog, groups=groups)
|
||
|
#mod = PanelAR1(data, exog, groups=groups) #data does not contain different means
|
||
|
#print(mod.ar1filter(mod.endog, 1))
|
||
|
resa, reso = mod.fit()
|
||
|
print(resa[0], reso.params)
|