Commit 1b897b37 authored by Dóra Kocsis's avatar Dóra Kocsis

Add daily frequency, closes Enterprise/dseries#1

parent 686a3840
......@@ -14,7 +14,7 @@ function q = colon(varargin) % --*-- Unitary tests --*--
% 1. p must be greater than o if d>0.
% 2. p and q are dates objects with one element.
% Copyright (C) 2013-2017 Dynare Team
% Copyright (C) 2013-2020 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
......@@ -67,7 +67,7 @@ end
q = dates();
% Compute the number of elements in the returned dates object.
n = (p-o)+1; % The number of elements in q dates object if d==1.
n = (p-o)+1; % The number of elements in q dates object if d==1.
m = n;
if d>1 % Correction of the number of elements (if d is not equal to one).
m = length(1:d:n);
......@@ -81,8 +81,12 @@ if isequal(q.freq, 1)
q.time = NaN(m,2);
q.time(:,1) = o.time(1)+transpose(0:d:n-1);
q.time(:,2) = 1;
elseif isequal(q.freq, 365)
q.time = NaN(n,3);
initperiods = datenum(o.time):datenum(p.time);
q.time = str2num(datestr(initperiods, 'yyyy mm dd'));
else
% Weekly, Monthly, Quaterly
% Weekly, Monthly, Quaterly, Bi-Annual
q.time = NaN(n,2);
initperiods = min(q.freq-o.time(2)+1,n);
q.time(1:initperiods,1) = o.time(1);
......@@ -190,3 +194,20 @@ end
%$ t(2) = dassert(d.freq,e.freq);
%$ T = all(t);
%$ @eof:4
%$ @test:5
%$ % Create an empty dates object for daily data
%$ dd = dates('D');
%$
%$ % Define expected results.
%$ e.freq = 365;
%$ e.time = [1950 1 28; 1950 1 29; 1950 1 30];
%$
%$ % Call the tested routine.
%$ d = dd(1950, 01, 28):dd(1950, 01, 30);
%$
%$ % Check the results.
%$ t(1) = dassert(d.time,e.time);
%$ t(2) = dassert(d.freq,e.freq);
%$ T = all(t);
%$ @eof:5
classdef dates<handle % --*-- Unitary tests --*--
% Copyright (C) 2014-2017 Dynare Team
% Copyright (C) 2014-2020 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
......@@ -41,7 +41,12 @@ methods
else
error('dates:ArgCheck', 'All dates passed as inputs must have the same frequency!')
end
o.time = transpose(reshape([tmp.time], 2, length(tmp)));
if any(cellfun(@(c)(size(c,2)==3), {tmp.time}, 'UniformOutput', true))
o.time = transpose(reshape([tmp.time], 3, length(tmp)));
else
o.time = transpose(reshape([tmp.time], 2, length(tmp)));
end
return
end
if isequal(nargin,1) && isfreq(varargin{1})
......@@ -107,6 +112,32 @@ methods
end
return
end
if isequal(nargin,4) && isfreq(varargin{1}) && isequal(varargin{1},365)
o.time = NaN(0,3);
if ischar(varargin{1})
o.freq = string2freq(varargin{1});
else
o.freq = varargin{1};
end
if (isnumeric(varargin{2}) && isvector(varargin{2}) && all(isint(varargin{2})))
if isnumeric(varargin{3}) && isvector(varargin{3}) && all(isint(varargin{3}))
if isnumeric(varargin{4}) && isvector(varargin{4}) && all(isint(varargin{4}))
if all(varargin{3}>=1) && all(varargin{3}<=12) && all(varargin{4}>=1) && all(varargin{3}<=31)
o.time = [varargin{2}(:), varargin{3}(:), varargin{4}(:)];
else
error('dates:ArgCheck','Third input must contain integers between 1 and %i.', o.freq)
end
else
error('dates:ArgCheck','Fourth input must be a vector of integers.')
end
else
error('dates:ArgCheck','Third input must be a vector of integers.')
end
else
error('dates:ArgCheck','Second input must be a vector of integers.')
end
return
end
error('dates:ArgCheck','The input cannot be interpreted as a date. You should first read the manual!')
end % dates constructor.
% Other methods
......@@ -303,7 +334,7 @@ end % classdef
%@test:10
%$ try
%$ B = dates(4,[1950, 1950], [1, 2]);
%$ B = dates(365,[1956, 1956], [1, 1], [12, 13]);
%$ t = 1;
%$ catch
%$ t = 0;
......
......@@ -13,7 +13,7 @@ function B = subsref(A,S) % --*-- Unitary tests --*--
% 1. The type of the returned argument depends on the content of S.
% 2. See the matlab's documentation about the subsref method.
% Copyright (C) 2011-2017 Dynare Team
% Copyright (C) 2011-2020 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
......@@ -57,10 +57,11 @@ switch S(1).type
case '()'
if isempty(A)
if isempty(A.freq)
% Populate an empty dates object with time member (freq is not specified). Needs two or three inputs. First input is an integer
% Populate an empty dates object with time member (freq is not specified). Needs two, three or four inputs. First input is an integer
% scalar specifying the frequency. Second input is either the time member (a n*2 array of integers) or a column vector with n
% elements (the first column of the time member --> years). If the the second input is a row vector and if A.freq~=1 a third input
% is necessary. The third input is n*1 vector of integers between 1 and A.freq (second column of the time member --> subperiods).
% is necessary. The third input is n*1 vector of integers between 1 and A.freq (or 31 in case of daily data) (second column of the time member --> subperiods).
% The fourth input is only necessary in case of daily data, it is n*1 vector of integers between 1 and 12.
B = dates();
% First input is the frequency.
if isfreq(S(1).subs{1})
......@@ -72,7 +73,7 @@ switch S(1).type
else
error('dates::subsref: First input must be a frequency!')
end
if isequal(length(S(1).subs),2)
if isequal(length(S(1).subs),2) && ~isequal(B.freq,365)
% If two inputs are provided, the second input must be a n*2 array except if frequency is annual.
[n, m] = size(S(1).subs{2});
if m>2
......@@ -94,7 +95,7 @@ switch S(1).type
else
error('dates::subsref: This is a bug!')
end
elseif isequal(length(S(1).subs),3)
elseif isequal(length(S(1).subs),3) && ~isequal(B.freq,365)
% If three inputs are provided, the second and third inputs are column verctors of integers (years and subperiods).
if ~iscolumn(S(1).subs{2}) && ~all(isint(S(1).subs{2}))
error('dates::subsref: Second input argument must be a column vector of integers!')
......@@ -108,14 +109,32 @@ switch S(1).type
error('dates::subsref: Second and third input arguments must have the same number of elements!')
end
B.time = [S(1).subs{2}, S(1).subs{3}];
elseif isequal(length(S(1).subs),4) && isequal(B.freq,365)
% If four inputs are provided, the second, third and fourth inputs are column verctors of integers (years and subperiods (months and days when freq is 365)).
if ~iscolumn(S(1).subs{2}) && ~all(isint(S(1).subs{2}))
error('dates::subsref: Second input argument must be a column vector of integers!')
end
n1 = size(S(1).subs{2},1);
if ~iscolumn(S(1).subs{3}) && ~all(S(1).subs{2}>=1) && ~all(S(1).subs{2}<=12)
error('dates::subsref: Third input argument must be a column vector of subperiods (integers between 1 and 12)!')
end
n2 = size(S(1).subs{3},1);
if ~iscolumn(S(1).subs{4}) && ~all(S(1).subs{2}>=1) && ~all(S(1).subs{2}<=31)
error('dates::subsref: Fourth input argument must be a column vector of subperiods (integers between 1 and 31)!')
end
n3 = size(S(1).subs{4},1);
if ~isequal(n1,n2,n3)
error('dates::subsref: Second, third and fourth input arguments must have the same number of elements!')
end
B.time = [S(1).subs{2}, S(1).subs{3}, S(1).subs{4}];
else
error('dates::subsref: Wrong calling sequence!')
end
else
% Populate an empty dates object with time member (freq is already specified).
% Needs one (time) or two (first and second columns of time for years and subperiods) inputs.
% Needs one (time), two or three (first, second and third columns of time for years and subperiods (months and days when freq is 365)) inputs.
B = copy(A);
if isequal(length(S(1).subs),2)
if isequal(length(S(1).subs),2) && ~isequal(B.freq,365)
if ~iscolumn(S(1).subs{1}) && ~all(isint(S(1).subs{1}))
error('dates::subsref: First argument has to be a column vector of integers!')
end
......@@ -128,7 +147,7 @@ switch S(1).type
error('dates::subsref: First and second argument must have the same number of rows!')
end
B.time = [S(1).subs{1}, S(1).subs{2}];
elseif isequal(length(S(1).subs),1)
elseif isequal(length(S(1).subs),1) && ~isequal(B.freq,365)
[n, m] = size(S(1).subs{1});
if ~isequal(m,2) && ~isequal(B.freq,1)
error('dates::subsref: First argument has to be a n*2 array!')
......@@ -146,6 +165,23 @@ switch S(1).type
else
error('dates::subsref: This is a bug!')
end
elseif isequal(length(S(1).subs),3) && isequal(B.freq,365)
if ~iscolumn(S(1).subs{1}) && ~all(isint(S(1).subs{1}))
error('dates::subsref: First argument has to be a column vector of integers!')
end
n1 = size(S(1).subs{1},1);
if ~iscolumn(S(1).subs{2}) && ~all(S(1).subs{2}>=1) && ~all(S(1).subs{2}<=12)
error('dates::subsref: Second argument has to be a column vector of subperiods (integers between 1 and 12!')
end
n2 = size(S(1).subs{2},1);
if ~iscolumn(S(1).subs{3}) && ~all(S(1).subs{2}>=1) && ~all(S(1).subs{2}<=31)
error('dates::subsref: Third argument has to be a column vector of subperiods (integers between 1 and 31!')
end
n3 = size(S(1).subs{3},1);
if ~isequal(n3,n2,n1)
error('dates::subsref: All arguments must have the same number of rows!')
end
B.time = [S(1).subs{1}, S(1).subs{2}, S(1).subs{3}];
else
error('dates::subsref: Wrong number of inputs!')
end
......@@ -345,3 +381,43 @@ end
%$ end
%$ T = all(t);
%@eof:7
%@test:8
%$ % Define a dates object
%$ B = dates('1950-11-15','1950-11-16','1950-11-17','1950-11-18');
%$
%$ % Try to extract a sub-dates object.
%$ try
%$ d = B([]);
%$ t(1) = true;
%$ catch
%$ t(1) = false;
%$ end
%$
%$ if t(1)
%$ t(2) = dassert(isa(d,'dates'), true);
%$ t(3) = dassert(isempty(d), true);
%$ end
%$ T = all(t);
%@eof:8
%@test:9
%$ % Define a dates object
%$ B = dates('1950-11-15','1950-11-16','1950-11-17','1950-11-18');
%$
%$ % Try to extract a sub-dates object.
%$ d = B(2);
%$
%$ if isa(d,'dates')
%$ t(1) = 1;
%$ else
%$ t(1) = 0;
%$ end
%$
%$ if t(1)
%$ t(2) = dassert(d.freq,B.freq);
%$ t(3) = dassert(d.time,[1950 11 16]);
%$ t(4) = dassert(d.ndat(),1);
%$ end
%$ T = all(t);
%@eof:9
\ No newline at end of file
function o = dgrowth(o) % --*-- Unitary tests --*--
% Computes daily growth rates.
%
% INPUTS
% - o [dseries]
%
% OUTPUTS
% - o [dseries]
% Copyright (C) 2020 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/>.
o = copy(o);
o.dgrowth_();
%@test:1
%$ try
%$ data = (1+.01).^transpose(0:1:50);
%$ ts = dseries(data,'1950-01-01');
%$ ds = ts.dgrowth();
%$ t(1) = 1;
%$ catch
%$ t(1) = 0;
%$ end
%$
%$ if t(1)
%$ DATA = NaN(1,ds.vobs);
%$ DATA = [DATA; .01*ones(ds.nobs-1,ds.vobs)];
%$ t(2) = dassert(ds.data,DATA,1e-15);
%$ t(3) = dassert(ts.data,data,1e-15);
%$ end
%$
%$ T = all(t);
%@eof:1
\ No newline at end of file
function o = dgrowth_(o) % --*-- Unitary tests --*--
% Computes daily growth rates.
%
% INPUTS
% - o [dseries]
%
% OUTPUTS
% - o [dseries]
% Copyright (C) 2020 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/>.
switch frequency(o)
case 1
error('dseries::dgrowth: I cannot compute daily growth rates from yearly data!')
case 2
error('dseries::dgrowth: I cannot compute daily growth rates from bi-annual data!')
case 4
error('dseries::dgrowth: I cannot compute daily growth rates from quaterly data!')
case 12
error('dseries::dgrowth: I cannot compute daily growth rates from monthly data!')
case 52
error('dseries::dgrowth: I cannot compute daily growth rates from weekly data!')
case 365
o.data(2:end,:) = o.data(2:end,:)./o.data(1:end-1,:) - 1;
o.data(1,:) = NaN;
otherwise
error(['dseries::dgrowth: object ' inputname(1) ' has unknown frequency']);
end
for i = 1:vobs(o)
if isempty(o.ops{i})
o.ops(i) = {['dgrowth(' o.name{i} ')']};
else
o.ops(i) = {['dgrowth(' o.ops{i} ')']};
end
end
%@test:1
%$ try
%$ data = (1+.01).^transpose(0:1:50);
%$ ts = dseries(data,'1950Q1');
%$ ts.dgrowth_;
%$ t(1) = 0;
%$ catch
%$ t(1) = 1;
%$ end
%$
%$ T = all(t);
%@eof:1
%@test:2
%$ try
%$ data = (1+.01).^transpose(0:1:80);
%$ ts = dseries(data, '1950-01-01');
%$ ts.dgrowth_;
%$ t(1) = 1;
%$ catch
%$ t(1) = 0;
%$ end
%$
%$ if t(1)
%$ DATA = NaN(1,ts.vobs);
%$ DATA = [DATA; (1.01-1)*ones(ts.nobs-1, ts.vobs)];
%$ t(2) = dassert(ts.data, DATA, 1e-15);
%$ end
%$
%$ T = all(t);
%@eof:2
......@@ -124,7 +124,7 @@ methods
init = dates(1,1);
elseif (isdates(b) && isequal(length(b),1))
init = b;
elseif ischar(b) && isdate(b)% Weekly, Monthly, Quaterly, Bi-annual or Annual data (string).
elseif ischar(b) && isdate(b)% Daily, Weekly, Monthly, Quaterly, Bi-annual or Annual data (string).
init = dates(b);
elseif (isnumeric(b) && isscalar(b) && isint(b)) % Yearly data.
init = dates([num2str(b) 'Y']);
......
......@@ -39,6 +39,8 @@ switch frequency(o)
o.data(1:6,:) = NaN;
case 52
error('dseries::hdiff: I do not know yet how to compute bi-annual differences from weekly data!')
case 365
error('dseries::hdiff: I do not know yet how to compute bi-annual differences from daily data!')
otherwise
error(['dseries::hdiff: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -39,6 +39,8 @@ switch frequency(o)
o.data(1:6,:) = NaN;
case 52
error('dseries::hgrowth: I do not know yet how to compute bi-annual growth rates from weekly data!')
case 365
error('dseries::hgrowth: I do not know yet how to compute bi-annual growth rates from daily data!')
otherwise
error(['dseries::hgrowth: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -35,6 +35,10 @@ switch frequency(o)
case 12
o.data(2:end,:) = o.data(2:end,:)-o.data(1:end-1,:);
o.data(1,:) = NaN;
case 52
error('dseries::mdiff: I do not know yet how to compute monthly differences from weekly data!')
case 365
error('dseries::mdiff: I do not know yet how to compute monthly differences from daily data!')
otherwise
error(['dseries::mdiff: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -35,6 +35,10 @@ switch frequency(o)
case 12
o.data(2:end,:) = o.data(2:end,:)./o.data(1:end-1,:) - 1;
o.data(1,:) = NaN;
case 52
error('dseries::mgrowth: I do not know yet how to compute monthly growth rates from weekly data!')
case 365
error('dseries::mgrowth: I do not know yet how to compute monthly growth rates from daily data!')
otherwise
error(['dseries::mgrowth: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -38,6 +38,8 @@ switch frequency(o)
o.data(1:3,:) = NaN;
case 52
error('dseries::qdiff: I do not know yet how to compute quaterly differences from weekly data!')
case 365
error('dseries::qdiff: I do not know yet how to compute quaterly differences from daily data!')
otherwise
error(['dseries::qdiff: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -38,6 +38,8 @@ switch frequency(o)
o.data(1:3,:) = NaN;
case 52
error('dseries::qgrowth: I do not know yet how to compute quaterly growth rates from weekly data!')
case 365
error('dseries::qgrowth: I do not know yet how to compute quaterly growth rates from daily data!')
otherwise
error(['dseries::qgrowth: object ' inputname(1) ' has unknown frequency']);
end
......
......@@ -53,7 +53,7 @@ switch S(1).type
% Returns a dates object (last date).
r = o.dates(end);
case 'freq'
% Returns an integer characterizing the data frequency (1, 4, 12 or 52)
% Returns an integer characterizing the data frequency (1, 2, 4, 12, 52 or 365)
r = o.dates.freq;
case 'length'
error(['dseries::subsref: we do not support the length operator on ' ...
......@@ -112,6 +112,7 @@ switch S(1).type
'hgrowth','hgrowth_', ...
'qgrowth','qgrowth_', ...
'mgrowth','mgrowth_', ...
'dgrowth','dgrowth_', ...
'ydiff','ydiff_', ...
'hdiff','hdiff_', ...
'qdiff','qdiff_', ...
......@@ -743,4 +744,21 @@ return
end
T = all(t);
%@eof:20
\ No newline at end of file
%@eof:20
%@test:21
try
tseries = dseries(dates('2000-01-01'));
ts = tseries(ones(6,1));
t(1) = true;
catch
t(1) = false;
end
if t(1)
t(2) = ts.dates(1)==dates('2000-01-01');
t(3) = ts.dates(6)==dates('2000-01-06');
end
T = all(t);
%@eof:21
\ No newline at end of file
......@@ -41,6 +41,9 @@ switch frequency(o)
case 52
o.data(53:end,:) = o.data(53:end,:)-o.data(1:end-52,:);
o.data(1:52,:) = NaN;
case 365
o.data(366:end,:) = o.data(366:end,:)-o.data(1:end-365,:);
o.data(1:365,:) = NaN;
otherwise
error(['dseries::ydiff: object ' inputname(1) ' has unknown frequency']);
end
......@@ -155,3 +158,23 @@ end
%$
%$ T = all(t);
%@eof:5
%@test:6
%$ try
%$ data = transpose(1:1095);
%$ ts = dseries(data,'1950-01-01',{'A1'},{'A_1'});
%$ ts.ydiff_;
%$ t(1) = true;
%$ catch
%$ t(1) = false;
%$ end
%$
%$ if t(1)
%$ DATA = NaN(365,ts.vobs);
%$ DATA = [DATA; 365*ones(ts.nobs-365,ts.vobs)];
%$ t(2) = dassert(ts.data,DATA);
%$ t(3) = dassert(ts.ops{1},['ydiff(A1)']);
%$ end
%$
%$ T = all(t);
%@eof:6
\ No newline at end of file
......@@ -41,6 +41,9 @@ switch frequency(o)
case 52
o.data(53:end,:) = o.data(53:end,:)./o.data(1:end-52,:) - 1;
o.data(1:52,:) = NaN;
case 365
o.data(366:end,:) = o.data(366:end,:)./o.data(1:end-365,:) - 1;
o.data(1:365,:) = NaN;
otherwise
error(['dseries::ygrowth: object ' inputname(1) ' has unknown frequency']);
end
......@@ -131,3 +134,23 @@ end
%$
%$ T = all(t);
%@eof:4
%@test:5
%$ try
%$ data = repmat(transpose(1:365),100,1);
%$ ts = dseries(data,'1950-01-01');
%$ ts.ygrowth_();
%$ t(1) = true;
%$ catch
%$ t(1) = false;
%$ end
%$
%$
%$ if t(1)
%$ DATA = NaN(365,ts.vobs);
%$ DATA = [DATA; zeros(ts.nobs-365,ts.vobs)];
%$ t(2) = dassert(ts.data,DATA);
%$ end
%$
%$ T = all(t);
%@eof:5
......@@ -4,8 +4,8 @@ function s = date2string(varargin) % --*-- Unitary tests --*--
%
% INPUTS
% o varargin{1} + dates object with one element, if nargin==1.
% + 1*2 vector of integers (first element is the year, second element is the subperiod), if nargin==2.
% o varargin{2} integer scalar equal to 1, 2, 4, 12 or 52 (frequency).
% + 1*3 vector of integers (first element is the year, second and third elements are the subperiods), if nargin==3.
% o varargin{2} integer scalar equal to 1, 2, 4, 12, 52 or 365 (frequency).
%
% OUTPUTS
% o s string.
......@@ -37,8 +37,8 @@ if isequal(nargin,1)
end
if isequal(nargin,2)
if ~(isvector(varargin{1}) && isequal(length(varargin{1}),2) && all(isint(varargin{1})) && isscalar(varargin{2} && ismember(varargin{2},[1 2 4 12 52])))
error(['dates::format: First input must be a 1*2 vector of integers and second input must be a scalar integer (1, 2, 4, 12 or 52)!'])
if ~(isvector(varargin{1}) && (isequal(length(varargin{1}),2) || isequal(length(varargin{1}),3)) && all(isint(varargin{1})) && isscalar(varargin{2} && ismember(varargin{2},[1 2 4 12 52 365])))
error(['dates::format: First input must be a 1*2 or 1*3 vector of integers and second input must be a scalar integer (1, 2, 4, 12, 52 or 365)!'])
else
if varargin{1}(2)>varargin{2} || varargin{1}(2)<1
error('dates::format: Second element of the first input be between 1 and %s!',num2str(varargin{2}))
......@@ -48,9 +48,13 @@ if isequal(nargin,2)
end
end
s = [num2str(time(1)) freq2string(freq)];
if freq>1
s = strcat(s, num2str(time(2)));
if freq == 365
s = [num2str(time(1),'%02d') '-' num2str(time(2),'%02d') '-' num2str(time(3),'%02d')];
else
s = [num2str(time(1)) freq2string(freq)];
if freq>1
s = strcat(s, num2str(time(2)));
end
end
%@test:1
......@@ -135,3 +139,17 @@ end
%$ T = all(t);
%@eof:6
%@test:7
%$ try
%$ str = date2string([1938; 1; 12], 365);
%$ t(1) = true;
%$ catch
%$ t(1) = false;
%$ end
%$
%$ if t(1)
%$ t(2) = dassert(str, '1938-01-12');
%$ end
%$
%$ T = all(t);
%@eof:7
function s = freq2string(freq) % --*-- Unitary tests --*--