Skip to content
Snippets Groups Projects
Verified Commit d6c8d039 authored by Stéphane Adjemian's avatar Stéphane Adjemian
Browse files

Add routine to plot expressions from dseries objects.

Example:

>> ds1 = dseries(randn(100,3), dates('2000Q1'), {'x','y','z'});
>> ds2 = dseries(randn(100,3), dates('2000Q1'), {'x','y','z'});
>> dplot --expression 2*cumsum(x/y(-1)-1) --dseries toto --dseries noddy --range 2001Q1:2024Q1
parent 3f15efe0
No related branches found
No related tags found
No related merge requests found
Pipeline #8435 passed
function initialize_dseries_class()
% Copyright © 2015-2021 Dynare Team
% Copyright © 2015-2023 Dynare Team
%
% This code is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
......@@ -38,7 +38,8 @@ p = {'mdbnomics2dseries'; ...
'utilities/cumulate'; ...
'utilities/struct'; ...
'utilities/misc'; ...
'utilities/x13'};
'utilities/x13'; ...
'utilities/dplot'};
% Add missing routines if dynare is not in the path
if ~exist('isint','file')
......
function dplot(varargin)
% Plot expressions extracting data from different dseries objects.
%
% EXAMPLE
%
% >> ds1 = dseries(randn(100,3), dates('2000Q1'), {'x','y','z'});
% >> ds2 = dseries(randn(100,3), dates('2000Q1'), {'x','y','z'});
% >> dplot --expression 2*cumsum(x/y(-1)-1) --dseries toto --dseries noddy --range 2001Q1:2024Q1
%
% Will produce plots of 2*cumsum(x/y(-1)-1), where x and y are variables in objects toto and noddy,
% in the same figure.
%
% REMARKS
% [1] More than one --expression argument is allowed
% [2] --expression arguments must come first
% [3] For each dseries object we plot all the expressions. We use two nested loops, the outer loop is over
% the dseries objects and the inner loop over the expressions. This determines the ordering of the plotted
% lines.
% [4] All dseries objects must be defined in the calling workspace, if a dseries object is missing the routine
% throws a warning (we only build the plots for the available dseries objects), if all dseries objects are
% missing the routine throws an error.
% [5] An expression involves variables (defined in dseries objects belonging to the caller workspace), parameters
% (defined in the caller workspace) or numbers. dseries methods returning dseries objects can also be used
% in an expression.
% [6] If the --range argument is missing the range is defined by the dates property of the first dseries object. In
% in this case, lags/leads or diff on the variables should not be used.
% Copyright © 2023 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 <https://www.gnu.org/licenses/>.
% Get expressions to be plotted
expressions = getexpressions(varargin);
% Get dseriesnames
names = getdseriesnames(varargin);
% Put all dseries objects in a cell array
data = cell(size(names));
Names = names;
for i=1:length(Names)
try
data{i} = evalin('caller', Names{i});
catch
warning('dplot:: dseries object %s is unknown.', Names{i})
names(i) = [];
end
end
assert(length(names)>0, 'dplot:: None of the dseries objects declared is available.')
data = data(~cellfun(@isempty, data));
% Check that all elements in data are actually dseries objects
for i=1:length(data)
if ~isdseries(data{i})
error('%s is not a dseries object.', names{i})
end
end
range = getrange(varargin);
if isempty(range)
range = data{1}.dates;
end
ts = cell(1, length(data)*length(expressions)); l = 0;
Expressions = cell(size(expressions));
for i=1:length(data)
for j=1:length(expressions)
% Check that brackets are balanced
openbrackets = regexpi(expressions{j}, '(','match');
closebrackets = regexpi(expressions{j}, ')','match');
assert(length(openbrackets)==length(closebrackets), 'dplot:: Brackets are not balanced.')
% Get all tokens
tokens = unique(regexpi(expressions{j}, '\w*','match'));
% Filter out dseries methods
tokens = setdiff(tokens, allowedmethods());
% Filter out numbers
tokens = tokens(cellfun(@(x)isnan(str2double(x)), tokens));
listofvariables = tokens(cellfun(@(x)ismember(x,data{i}.name), tokens));
listofparameters = setdiff(tokens, listofvariables);
% Test if parameters are defined scalars in the caller workspace
for k=1:length(listofparameters)
if ~evalin('caller', sprintf('exist(''%s'', ''var'')', listofparameters{k}))
error('dplot:: %s is not a known object.', listofparameters{k})
end
if ~evalin('caller', sprintf('isscalar(%s) && isnumeric(%s)', listofparameters{k}, listofparameters{k}))
error('dplot:: Parameter %s has to be a numeric scalar.', listofparameters{k})
end
eval(sprintf('%s = evalin(''caller'', sprintf(''%%s'', listofparameters{k}));', listofparameters{k}))
end
Expressions{j} = rewrite(expressions{j}, listofvariables, i);
l = l+1;
ts{l} = eval(Expressions{j});
end
end
%
% Build plots
%
plot(1:length(range), ts{1}(range).data)
if length(ts)>1
hold on
for i=2:length(ts)
plot(1:length(range), ts{i}(range).data)
end
hold off
end
axis tight
%
% Reset x-axis labels (with dates)
%
ax = gca;
ax.XTick = unique(round(ax.XTick)); % Only keep integer labels
id = ax.XTick;
dd = strings(range(id));
ax.XTickLabel = dd;
end
function expr = getexpressions(cellarray)
% Return expressions to be plotted.
%
% INPUTS
% - cellarray [char] 1×n cell array of row char arrays.
%
% OUTPUTS
% - expr [char] 1×p cell array of row char arrays.
[epos, dpos, rpos, zpos] = positions(cellarray);
expr = cell(1, length(rpos));
Epos = [epos, zpos];
for i=1:length(epos)
tmp = cellarray(Epos(i)+1:Epos(i+1)-1);
expr{i} = strcat(tmp{:});
end
end
function names = getdseriesnames(cellarray)
% Return expressions to be plotted.
%
% INPUTS
% - cellarray [char] 1×n cell array of row char arrays.
%
% OUTPUTS
% - names [char] 1×p cell array of row char arrays.
[~, dpos] = positions(cellarray);
names = cellarray(dpos+1);
end
function range = getrange(cellarray)
% Return period range for the plots.
%
% INPUTS
% - cellarray [char] 1×n cell array of row char arrays.
%
% OUTPUTS
% - range [dates]
[~, ~, rpos] = positions(cellarray);
if length(rpos)==0
range = dates();
return
end
str = cellarray{rpos+1};
sepid = strfind(str, ':');
assert(~isempty(sepid), 'dplot:: --range argument is wrong.')
assert(length(sepid)==1, 'dplot:: Only one semicolon is allowed in --range argument.')
assert(isdate(str(1:sepid-1)), 'dplot:: --range argument is wrong (first declared period cannot be interpreted as a dates object).')
assert(isdate(str(sepid+1:end)), 'dplot:: --range argument is wrong (second declared period cannot be interpreted as a dates object).')
range = dates(str(1:sepid-1)):dates(str(sepid+1:end));
end
function [epos, dpos, rpos, zpos] = positions(cellarray)
% Return positions of the arguments.
%
% INPUTS
% - cellarray [char] 1×n cell array of row char arrays.
%
% OUTPUTS
% - epos [integer] 1×pₑ vector, indices for the --expression arguments.
% - dpos [integer] 1×pₛ vector, indices for the --dseries arguments.
% - rpos [integer] scalar, index of the --range argument.
% - zpos [integer] first index of non --expression argument.
% Indices for --expression arguments.
epos = find(strcmp('--expression', cellarray));
if isempty(epos)
error('dplot::positions: --expression argument is mandatory.')
end
% Indices for the --dseries arguments.
dpos = find(strcmp('--dseries', cellarray));
if isempty(dpos)
error('dplot::positions: --dseries argument is mandatory.')
end
% Index for --range argument.
rpos = find(strcmp('--range', cellarray));
assert(length(rpos)==1 || length(rpos)==0, 'dplot::positions: Only one range is allowed.')
% Define index for the first argument name different from --expression
if isempty(rpos)
zpos = dpos(1);
else
zpos = min(dpos(1), rpos);
end
% Check that expressions are coming before other arguments
assert(max(epos)<zpos, 'dplot::positions: --expression must come befor the other arguments.')
end
function m = allowedmethods()
m = {'abs', 'center', 'cumprod', 'cumsum', 'detrend', 'dgrowth', 'diff', 'exp', 'log', 'hdiff', 'hgrowth', 'baxter_king_filter', ...
'hpcycle', 'hptrend', 'onesidedhpcycle', 'onesidedhptrend', 'lag', 'lead', 'mdiff', 'mgrowth', 'qdiff', 'qgrowth', 'mean', 'std', ...
'ydiff', 'ygrowth'};
end
function expr = rewrite(expr, list, index)
for i=1:length(list)
expr = regexprep(expr, sprintf('\\<%s\\>', list{i}), sprintf('data{%u}.%s', index, list{i}));
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment