update blog post

parent 5b0f01a5
......@@ -8,8 +8,9 @@ afv2013table1/*.mat
afv2013table1/*.json
afv2013table1/afv2013table1/*
sw2007/Smets_Wouters_2007/*
sw2007/Smets_Wouters_2007*.m
sw2007/Smets_Wouters_2007*.mat
sw2007/Smets_Wouters_2007*.log
sw2007/Smets_Wouters_2007*.json
content/sw2007/Smets_Wouters_2007/*
content/sw2007/+Smets_Wouters_2007/*
content/sw2007/*.log
content/sw2007/*.png
content/sw2007/*.dat
content/sw2007/*.mat
[submodule "dynare"]
path = dynare
url = url = ../../Dynare/dynare.git
url = https://git.dynare.org/Dynare/dynare.git
[submodule "theme"]
path = theme
url = https://git.nomics.world/macro/theme-bootstrap3.git
......
......@@ -11,6 +11,6 @@ To run the code described in the model, do the following:
- Download the latest [snapshot of dynare](http://www.dynare.org/snapshot/) for your system. The code below will work with any version of Dynare newer than the version pointed to by the submodule in this repository.
- Open Matlab or Octave and type:
- `addpath <<path to dynare/matlab folder>>` where `dynare` points to the snapshot you downloaded
- `addpath <<path to obsmacro-dynare-json/ols>>`
- `addpath <<path to obsmacro-dynare-json/content/ols>>`
- `cd <<path to obsmacro-dynare-json/afv2013table1/>>`
- `dynare afv2013table1.mod`
This diff is collapsed.
function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, overlapping_dates, param_names)
%function [Y, lhssub, X, startdates, enddates, residnames] = common_parsing(ds, ast, overlapping_dates, param_names)
%
% Code common to sur.m and pooled_ols.m
%
% INPUTS
% ds [dseries] dataset
% ast [cell array] JSON representation of abstract syntax tree
% overlapping_dates [boolean] if true, dates are same across equations
%
% OUTPUTS
% Y [cell array] dependent variables
% lhssub [cell array] RHS subtracted from LHS
% X [cell array] regressors
% startdates [cell array] first observed period for each
% equation
% enddates [cell array] last observed period for each
% equation
% startidxs [vector] rows corresponding to each
% equation's observations
% residnames [cell array] name of residual in each equation
% param_names [cellstr or cell] list of parameters to estimate (if
% empty, estimate all)
%
% SPECIAL REQUIREMENTS
% none
% Copyright (C) 2019 Dynare Team
%
% This file is part of Dynare.
%
% Dynare is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% Dynare is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
global M_
%% Initialize variables
neqs = length(ast);
Y = cell(neqs, 1);
lhssub = cell(neqs, 1);
X = cell(neqs, 1);
startdates = cell(neqs, 1);
enddates = cell(neqs, 1);
residnames = cell(neqs, 1);
%% Loop over equations
for i = 1:neqs
[Y{i}, lhssub{i}, X{i}, residnames{i}, startdates{i}, enddates{i}] = ...
parse_ols_style_equation(ds, ast{i});
end
if overlapping_dates
maxfp = max([startdates{:}]);
minlp = min([enddates{:}]);
for i = 1:neqs
Y{i} = Y{i}(maxfp:minlp);
if ~isempty(X{i})
X{i} = X{i}(maxfp:minlp);
end
if ~isempty(lhssub{i})
lhssub{i} = lhssub{i}(maxfp:minlp);
end
startdates{i} = maxfp;
enddates{i} = minlp;
end
end
if ~isempty(param_names)
if iscell(param_names) && ~iscellstr(param_names)
assert(length(param_names) == neqs, 'error w param_names arg');
else
pn = param_names;
pn_found_cellstr = false(length(param_names), 1);
end
for i = 1:neqs
if iscell(param_names) && ~iscellstr(param_names)
pn = param_names{i};
pn_found_cellstr_i = false(length(pn), 1);
end
names = X{i}.name;
newlhssub = dseries();
for j = 1:length(names)
idx = find(strcmp(names{j}, pn));
if isempty(idx)
pval = M_.params(strcmp(names{j}, M_.param_names));
if isnan(pval) || isinf(pval)
error(['Could not find param init value for ' names{j}])
end
newlhssub = newlhssub + pval * X{i}.(names{j});
X{i} = X{i}.remove(names{j});
else
if iscell(param_names) && ~iscellstr(param_names)
pn_found_cellstr_i = true;
else
pn_found_cellstr(idx) = true;
end
end
end
Y{i} = Y{i} - newlhssub;
lhssub{i} = lhssub{i} + newlhssub;
if iscell(param_names) && ~iscellstr(param_names)
if ~all(pn_found_cellstr_i)
error(['parameters specified in param_names (' ...
strjoin(pn(~pn_found_cellstr_i), ', ') ...
') were not found in eq ' num2str(i) ' to be estimated'])
end
end
end
if ~(iscell(param_names) && ~iscellstr(param_names))
if ~all(pn_found_cellstr)
error(['parameters specified in param_names (' ...
strjoin(pn(~pn_found_cellstr), ', ') ...
') were not found in the equation(s) to be estimated'])
end
end
end
end
function ds = dyn_ols(ds, fitted_names_dict, eqtags)
% function ds = dyn_ols(ds, fitted_names_dict, eqtags)
function ds = dyn_ols(ds, fitted_names_dict, eqtags, model_name, param_names, ds_range)
% function varargout = dyn_ols(ds, fitted_names_dict, eqtags, model_name, param_names, ds_range)
% Run OLS on chosen model equations; unlike olseqs, allow for time t
% endogenous variables on LHS
%
% INPUTS
% ds [dseries] data
% fitted_names_dict [cell] Nx2 or Nx3 cell array to be used in naming fitted
% values; first column is the equation tag,
% second column is the name of the
% associated fitted value, third column
% (if it exists) is the function name of
% the transformation to perform on the
% fitted value.
% eqtags [cellstr] names of equation tags to estimate. If empty,
% estimate all equations
% ds [dseries] data
% fitted_names_dict [cell] Nx2 or Nx3 cell array to be used in naming fitted
% values; first column is the equation tag,
% second column is the name of the
% associated fitted value, third column
% (if it exists) is the function name of
% the transformation to perform on the
% fitted value.
% eqtags [cellstr] names of equation tags to estimate. If empty,
% estimate all equations
% model_name [celltsr] name to use in oo_ and inc file (must be
% same size as eqtags)
% param_names [cell of cellstr] list of parameters to estimate by eqtag
% (if empty, estimate all)
% ds_range [dates] range of dates to use in estimation
%
% OUTPUTS
% ds [dseries] data updated with fitted values
%
% SPECIAL REQUIREMENTS
% none
% dynare must have been run with the option: json=compute
% Copyright (C) 2017-2018 Dynare Team
% Copyright (C) 2017-2019 Dynare Team
%
% This file is part of Dynare.
%
......@@ -40,164 +45,112 @@ function ds = dyn_ols(ds, fitted_names_dict, eqtags)
global M_ oo_ options_
assert(nargin >= 1 && nargin <= 3, 'dyn_ols: takes between 1 and 3 arguments');
assert(isdseries(ds), 'dyn_ols: the first argument must be a dseries');
if nargin < 1 || nargin > 6
error('dyn_ols() takes between 1 and 6 arguments')
end
jsonfile = [M_.fname '_original.json'];
if exist(jsonfile, 'file') ~= 2
error('Could not find %s! Please use the json=compute option (See the Dynare invocation section in the reference manual).', jsonfile);
if isempty(ds) || ~isdseries(ds)
error('dyn_ols: the first argument must be a dseries')
end
%% Get Equation(s)
jsonmodel = loadjson(jsonfile);
jsonmodel = jsonmodel.model;
if nargin < 6
ds_range = ds.dates;
else
if isempty(ds_range)
ds_range = ds.dates;
else
if ds_range(1) < ds.firstdate || ds_range(end) > lastdate(ds)
error('There is a problem with the 6th argument: the date range does not correspond to that of the dseries')
end
end
end
if nargin == 1
if nargin < 5
param_names = {};
else
if ~isempty(param_names)
if ~iscell(param_names) || (~isempty(eqtags) && length(param_names) ~= length(eqtags))
error('The 5th argument, if provided, must be a cell of the same length as the eqtags argument')
end
for i = 1:length(param_names)
if ~iscellstr(param_names{i})
error('every entry of param_names must be a cellstr')
end
end
end
end
if nargin < 3
eqtags = {};
end
if nargin < 2
fitted_names_dict = {};
elseif nargin == 2
else
assert(isempty(fitted_names_dict) || ...
(iscell(fitted_names_dict) && ...
(size(fitted_names_dict, 2) == 2 || size(fitted_names_dict, 2) == 3)), ...
'dyn_ols: the second argument must be an Nx2 or Nx3 cell array');
elseif nargin == 3
jsonmodel = getEquationsByTags(jsonmodel, 'name', eqtags);
end
%% Estimation
M_endo_exo_names_trim = [M_.endo_names; M_.exo_names];
[junk, idxs] = sort(cellfun(@length, M_endo_exo_names_trim), 'descend');
regex = strjoin(M_endo_exo_names_trim(idxs), '|');
mathops = '[\+\*\^\-\/\(\)]';
for i = 1:length(jsonmodel)
%% Construct regression matrices
rhs_ = strsplit(jsonmodel{i}.rhs, {'+','-','*','/','^','log(','diff(','exp(','(',')'});
rhs_(cellfun(@(x) all(isstrprop(x, 'digit')), rhs_)) = [];
vnames = setdiff(rhs_, M_.param_names);
if ~isempty(regexp(jsonmodel{i}.rhs, ...
['(' strjoin(vnames, '\\(\\d+\\)|') '\\(\\d+\\))'], ...
'once'))
error(['dyn_ols: you cannot have leads in equation on line ' ...
jsonmodel{i}.line ': ' jsonmodel{i}.lhs ' = ' jsonmodel{i}.rhs]);
end
pnames = intersect(rhs_, M_.param_names);
vnames = cell(1, length(pnames));
splitstrings = cell(length(pnames), 1);
X = dseries();
for j = 1:length(pnames)
createdvar = false;
pregex = [...
mathops pnames{j} mathops ...
'|^' pnames{j} mathops ...
'|' mathops pnames{j} '$' ...
];
[startidx, endidx] = regexp(jsonmodel{i}.rhs, pregex, 'start', 'end');
assert(length(startidx) == 1);
if jsonmodel{i}.rhs(startidx) == '*' && jsonmodel{i}.rhs(endidx) == '*'
vnamesl = getStrMoveLeft(jsonmodel{i}.rhs(1:startidx-1));
vnamesr = getStrMoveRight(jsonmodel{i}.rhs(endidx+1:end));
vnames{j} = [vnamesl '*' vnamesr];
splitstrings{j} = [vnamesl '*' pnames{j} '*' vnamesr];
elseif jsonmodel{i}.rhs(startidx) == '*'
vnames{j} = getStrMoveLeft(jsonmodel{i}.rhs(1:startidx-1));
splitstrings{j} = [vnames{j} '*' pnames{j}];
elseif jsonmodel{i}.rhs(endidx) == '*'
vnames{j} = getStrMoveRight(jsonmodel{i}.rhs(endidx+1:end));
splitstrings{j} = [pnames{j} '*' vnames{j}];
if jsonmodel{i}.rhs(startidx) == '-'
vnames{j} = ['-' vnames{j}];
splitstrings{j} = ['-' splitstrings{j}];
end
elseif jsonmodel{i}.rhs(startidx) == '+' ...
|| jsonmodel{i}.rhs(startidx) == '-' ...
|| jsonmodel{i}.rhs(endidx) == '+' ...
|| jsonmodel{i}.rhs(endidx) == '-'
% intercept
createdvar = true;
if any(strcmp(M_endo_exo_names_trim, 'intercept'))
[~, vnames{j}] = fileparts(tempname);
vnames{j} = ['intercept_' vnames{j}];
assert(~any(strcmp(M_endo_exo_names_trim, vnames{j})));
else
vnames{j} = 'intercept';
end
splitstrings{j} = vnames{j};
else
error('dyn_ols: Shouldn''t arrive here');
%% Get Equation(s)
ast = get_ast(eqtags);
%% Set model_name
if nargin < 4
model_name = cell(length(ast), 1);
else
if isempty(model_name)
model_name = repmat({''}, length(ast), 1);
else
if ~iscellstr(model_name) || length(model_name) ~= length(ast)
error('The length of the 4th argument must be a cellstr with length equal to the number of equations estimated')
end
if createdvar
if jsonmodel{i}.rhs(startidx) == '-'
Xtmp = dseries(-ones(ds.nobs, 1), ds.firstdate, vnames{j});
else
Xtmp = dseries(ones(ds.nobs, 1), ds.firstdate, vnames{j});
for i = 1:length(model_name)
if ~isvarname(model_name{i})
error('Every entry in the 4th argument must be a valid string');
end
else
Xtmp = eval(regexprep(vnames{j}, regex, 'ds.$&'));
Xtmp.rename_(vnames{j});
end
X = [X Xtmp];
end
end
lhssub = getRhsToSubFromLhs(ds, jsonmodel{i}.rhs, regex, splitstrings, pnames);
residuals = setdiff(intersect(rhs_, M_.exo_names), ds.name);
assert(~isempty(residuals), ['No residuals in equation ' num2str(i)]);
assert(length(residuals) == 1, ['More than one residual in equation ' num2str(i)]);
%% Parse equations
[Y, lhssub, X, fp, lp] = common_parsing(ds(ds_range), ast, true, param_names);
Y = eval(regexprep(jsonmodel{i}.lhs, regex, 'ds.$&'));
if ~isempty(lhssub)
Y = Y - lhssub;
end
%% Loop over equations
for i = 1:length(Y)
pnames = X{i}.name;
[nobs, nvars] = size(X{i}.data);
fp = max(Y.firstobservedperiod, X.firstobservedperiod);
lp = min(Y.lastobservedperiod, X.lastobservedperiod);
if isfield(jsonmodel{i}, 'tags') ...
&& isfield(jsonmodel{i}.tags, 'sample') ...
&& ~isempty(jsonmodel{i}.tags.sample)
colon_idx = strfind(jsonmodel{i}.tags.sample, ':');
fsd = dates(jsonmodel{i}.tags.sample(1:colon_idx-1));
lsd = dates(jsonmodel{i}.tags.sample(colon_idx+1:end));
if fp > fsd
warning(['The sample over which you want to estimate contains NaNs. '...
'Adjusting estimation range to begin on: ' fp.char])
else
fp = fsd;
end
if lp < lsd
warning(['The sample over which you want to estimate contains NaNs. '...
'Adjusting estimation range to end on: ' lp.char])
if ~isempty(model_name{i})
tag = model_name{i};
else
if isfield(ast{i}, 'tags') && isfield(ast{i}.tags, 'name')
tag = ast{i}.tags.('name');
else
lp = lsd;
tag = ['eq_line_no_' num2str(ast{i}.line)];
end
end
Y = Y(fp:lp);
X = X(fp:lp).data;
if ~isempty(lhssub)
lhssub = lhssub(fp:lp);
end
if isfield(jsonmodel{i}, 'tags') && ...
isfield(jsonmodel{i}.tags, 'name')
tag = jsonmodel{i}.tags.('name');
else
tag = ['eq_line_no_' num2str(jsonmodel{i}.line)];
end
%% Estimation
% From LeSage, James P. "Applied Econometrics using MATLAB"
[nobs, nvars] = size(X);
oo_.ols.(tag).dof = nobs - nvars;
% Estimated Parameters
[q, r] = qr(X, 0);
[q, r] = qr(X{i}.data, 0);
xpxi = (r'*r)\eye(nvars);
oo_.ols.(tag).beta = r\(q'*Y.data);
oo_.ols.(tag).beta = r\(q'*Y{i}.data);
oo_.ols.(tag).param_idxs = zeros(length(pnames), 1);
for j = 1:length(pnames)
oo_.ols.(tag).param_idxs(j) = find(strcmp(M_.param_names, pnames{j}));
M_.params(oo_.ols.(tag).param_idxs(j)) = oo_.ols.(tag).beta(j);
if ~strcmp(pnames{j}, 'intercept')
oo_.ols.(tag).param_idxs(j) = find(strcmp(M_.param_names, pnames{j}));
M_.params(oo_.ols.(tag).param_idxs(j)) = oo_.ols.(tag).beta(j);
end
end
% Write .inc file
write_param_init_inc_file('ols', tag, oo_.ols.(tag).param_idxs, oo_.ols.(tag).beta);
% Yhat
idx = 0;
yhatname = [tag '_FIT'];
......@@ -207,17 +160,17 @@ for i = 1:length(jsonmodel)
yhatname = fitted_names_dict{idx, 2};
end
end
oo_.ols.(tag).Yhat = dseries(X*oo_.ols.(tag).beta, fp, yhatname);
oo_.ols.(tag).Yhat = dseries(X{i}.data*oo_.ols.(tag).beta, fp{i}, yhatname);
oo_.ols.(tag).YhatOrig = oo_.ols.(tag).Yhat;
oo_.ols.(tag).Yobs = Y{i};
% Residuals
oo_.ols.(tag).resid = Y - oo_.ols.(tag).Yhat;
oo_.ols.(tag).resid = Y{i} - oo_.ols.(tag).Yhat;
% Correct Yhat reported back to user
if ~isempty(lhssub)
Y = Y + lhssub;
oo_.ols.(tag).Yhat = oo_.ols.(tag).Yhat + lhssub;
end
Y{i} = Y{i} + lhssub{i};
oo_.ols.(tag).Yhat = oo_.ols.(tag).Yhat + lhssub{i};
% Apply correcting function for Yhat if it was passed
if any(idx) ...
&& length(fitted_names_dict(idx, :)) == 3 ...
......@@ -225,7 +178,7 @@ for i = 1:length(jsonmodel)
oo_.ols.(tag).Yhat = ...
feval(fitted_names_dict{idx, 3}, oo_.ols.(tag).Yhat);
end
ds = [ds oo_.ols.(tag).Yhat];
ds.(oo_.ols.(tag).Yhat.name) = oo_.ols.(tag).Yhat;
%% Calculate statistics
% Estimate for sigma^2
......@@ -233,7 +186,7 @@ for i = 1:length(jsonmodel)
oo_.ols.(tag).s2 = SS_res/oo_.ols.(tag).dof;
% R^2
ym = Y.data - mean(Y);
ym = Y{i}.data - mean(Y{i});
SS_tot = ym'*ym;
oo_.ols.(tag).R2 = 1 - SS_res/SS_tot;
......@@ -258,17 +211,17 @@ for i = 1:length(jsonmodel)
title = ['OLS Estimation of equation ''' tag ''''];
end
preamble = {sprintf('Dependent Variable: %s', jsonmodel{i}.lhs), ...
preamble = {['Dependent Variable: ' Y{i}.name{:}], ...
sprintf('No. Independent Variables: %d', nvars), ...
sprintf('Observations: %d from %s to %s\n', nobs, fp.char, lp.char)};
sprintf('Observations: %d from %s to %s\n', nobs, fp{i}.char, lp{i}.char)};
afterward = {sprintf('R^2: %f', oo_.ols.(tag).R2), ...
sprintf('R^2 Adjusted: %f', oo_.ols.(tag).adjR2), ...
sprintf('s^2: %f', oo_.ols.(tag).s2), ...
sprintf('Durbin-Watson: %f', oo_.ols.(tag).dw)};
dyn_table(title, preamble, afterward, vnames, ...
{'Coefficients','t-statistic','Std. Error'}, 4, ...
dyn_table(title, preamble, afterward, pnames, ...
{'Estimates','t-statistic','Std. Error'}, 4, ...
[oo_.ols.(tag).beta oo_.ols.(tag).tstat oo_.ols.(tag).stderr]);
end
end
......
......@@ -17,7 +17,7 @@ function dyn_table(title, preamble, afterward, rows, cols, indent, data)
% SPECIAL REQUIREMENTS
% none
% Copyright (C) 2017 Dynare Team
% Copyright (C) 2017-2019 Dynare Team
%
% This file is part of Dynare.
%
......@@ -53,20 +53,20 @@ colrow = sprintf('%s', colbegin);
colrow2 = colrow;
format = [' %-' num2str(maxrowstrlen) 's'];
for i = 1:length(cols)
precision = 12;
if colstrlens(i) < precision
colrow = [colrow repmat(' ', 1, floor((precision-mod(colstrlens(i), precision))/2)) cols{i} repmat(' ', 1, ceil((precision-mod(colstrlens(i), precision))/2))];
colrow2 = [colrow2 repmat('_', 1, precision)];
field_width = 16;
if colstrlens(i) < field_width
colrow = [colrow repmat(' ', 1, floor((field_width-mod(colstrlens(i), field_width))/2)) cols{i} repmat(' ', 1, ceil((field_width-mod(colstrlens(i), field_width))/2))];
colrow2 = [colrow2 repmat('_', 1, field_width)];
else
colrow = [colrow cols{i}];
colrow2 = [colrow2 repmat('_', 1, colstrlens(i))];
precision = colstrlens(i);
field_width = colstrlens(i);
end
if i ~= length(cols)
colrow = [colrow repmat(' ', 1, indent)];
colrow2 = [colrow2 repmat(' ', 1, indent)];
end
format = [format repmat(' ', 1, indent) '%' sprintf('%d.5f', precision)];
format = [format repmat(' ', 1, indent) '%' sprintf('%d.5g', field_width)];
end
% Center title
......
function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue)
%function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue)
% Return the jsonmodel structure with the matching tags
function [ast] = getEquationsByTags(ast, tagname, tagvalue)
%function [ast] = getEquationsByTags(ast, tagname, tagvalue)
% Return the ast structure with the matching tags
%
% INPUTS
% jsonmodel [cell array] JSON representation of model block
% tagname [string] The name of the tag whos values are to
% be selected
% tagvalue [string] The values to be selected for the
% provided tagname
% ast [cell array] JSON representation of model block
% tagname [string] The name of the tag whos values are to
% be selected
% tagvalue [string] The values to be selected for the
% provided tagname
%
% OUTPUTS
% jsonmodel [cell array] JSON representation of model block,
% with equations removed that don't match
% eqtags
% ast [cell array] JSON representation of model block,
% with equations removed that don't match
% eqtags
%
% SPECIAL REQUIREMENTS
% none
% Copyright (C) 2017-2018 Dynare Team
% Copyright (C) 2017-2019 Dynare Team
%
% This file is part of Dynare.
%
......@@ -34,24 +34,34 @@ function [jsonmodel] = getEquationsByTags(jsonmodel, tagname, tagvalue)
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
assert(nargin == 3, 'Incorrect number of arguments passed to getEquationsByTags');
assert(iscell(jsonmodel) && ~isempty(jsonmodel), ...
'the first argument must be a cell array of structs');
assert(ischar(tagname), 'Tag name must be a string');
assert(ischar(tagvalue) || iscell(tagvalue), 'Tag value must be a string or a cell string array');
if nargin ~= 3