diff --git a/src/utilities/convert/dseries2M.m b/src/utilities/convert/dseries2M.m new file mode 100644 index 0000000000000000000000000000000000000000..9b3768f2b71a2baf38ea31ba992a03000045ecc1 --- /dev/null +++ b/src/utilities/convert/dseries2M.m @@ -0,0 +1,102 @@ +function ts = dseries2M(ds, method) + +% INPUTS +% - ds [dseries] daily frequency object with n elements. +% - method [char] 1×m array, method used for the time aggregation, possible values are: +% - 'arithmetic-average' (for rates), +% - 'geometric-average' (for factors), +% - 'sum' (for flow variables), and +% - 'end-of-period' (for stock variables). +% +% OUTPUTS +% - ts [dseries] monthly frequency object with m<n elements. + +% Copyright © 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 +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare dates submodule 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/>. + + dm = dates2M(ds.dates); + dM = unique(dm); % dM.ndat will be the maximum number of periods in ts + + tdata = NaN(dM.ndat, ds.vobs); + + switch method + case 'arithmetic-average' + for i=1:dM.ndat + idm = find(dm==dM(i)); + tdata(i,:) = mean(ds.data(idm,:)); + if ~isalldays(dM(i), idm) + warning('Not all days are available in month %s.', date2string(dM(i))) + end + end + case 'geometric-average' + for i=1:dM.ndat + idm = find(dm==dM(i)); + tdata(i,:) = prod(ds.data(idm,:), 1).^(1/length(idm)); + if ~isalldays(dM(i), idm) + warning('Not all days are available in month %s.', date2string(dM(i))) + end + end + case 'sum' + for i=1:dM.ndat + idm = find(dm==dM(i)); + tdata(i,:) = sum(ds.data(idm,:), 1); + if ~isalldays(dM(i), idm) + warning('Not all days are available in month %s.', date2string(dM(i))) + end + end + case 'end-of-period' + for i=1:dM.ndat + idm = find(dm==dM(i)); + lastday = datevec(ds.dates.time(idm(end))); + tdata(i,:) = ds.data(idm(end),:); + if lastday(3)<expectedlastday(dM(i)) + warning('The last available day is not the last observed day of %s.', date2string(dM(i))) + end + end + otherwise + error('Unknow method.') + end + + ts = dseries(tdata, dM, ds.name, ds.tex); + +end + +function eld = expectedlastday(m) + if ismember(m.time(2), [4,6,9,11]) + eld = 30; + elseif ismember(m.time(2), [1,3,5,7,8,10,12]) + eld = 31; + else % February + if isleapyear(m.time(1)) + eld = 29; + else + eld = 28; + end + end +end + +function iad = isalldays(m, id) + if ismember(m.time(2), [4,6,9,11]) + iad = length(id)==30; + elseif ismember(m.time(2), [1,3,5,7,8,10,12]) + iad = length(id)==31; + else % February + if isleapyear(m.time(1)) + iad = length(id)==29; + else + iad = length(id)==28; + end + end +end \ No newline at end of file diff --git a/src/utilities/convert/dseries2Q.m b/src/utilities/convert/dseries2Q.m new file mode 100644 index 0000000000000000000000000000000000000000..0e05bacae9a30efd8fba5b4e623ba8d05100d8c6 --- /dev/null +++ b/src/utilities/convert/dseries2Q.m @@ -0,0 +1,127 @@ +function ts = dseries2Q(ds, method) + +% INPUTS +% - ds [dseries] daily or monthly frequency object with n elements. +% - method [char] 1×m array, method used for the time aggregation, possible values are: +% - 'arithmetic-average' (for rates), +% - 'geometric-average' (for factors), +% - 'sum' (for flow variables), and +% - 'end-of-period' (for stock variables). +% +% OUTPUTS +% - ts [dseries] quaterly frequency object with m<n elements. + +% Copyright © 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 +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Dynare dates submodule 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/>. + + dq = dates2Q(ds.dates); + dQ = unique(dq); % dQ.ndat will be the maximum number of periods in ts + + tdata = NaN(dQ.ndat, ds.vobs); + + switch method + case 'arithmetic-average' + for i=1:dQ.ndat + idq = find(dq==dQ(i)); + tdata(i,:) = mean(ds.data(idq,:)); + if ds.dates.freq==365 && ~isalldays(dQ(i), idq) + warning('Not all days are available in %s.', date2string(dQ(i))) + end + if ds.dates.freq==12 && ~isequal(length(idq), 3) + warning('Not all months are available in %s.', date2string(dQ(i))) + end + end + case 'geometric-average' + for i=1:dQ.ndat + idq = find(dq==dQ(i)); + tdata(i,:) = prod(ds.data(idq,:), 1).^(1/length(idq)); + if ds.dates.freq==365 && ~isalldays(dQ(i), idq) + warning('Not all days are available in %s.', date2string(dQ(i))) + end + if ds.dates.freq==12 && ~isequal(length(idq), 3) + warning('Not all months are available in %s.', date2string(dQ(i))) + end + end + case 'sum' + for i=1:dQ.ndat + idq = find(dq==dQ(i)); + tdata(i,:) = sum(ds.data(idq,:), 1); + if ds.dates.freq==365 && ~isalldays(dQ(i), idq) + warning('Not all days are available in %s.', date2string(dQ(i))) + end + if ds.dates.freq==12 && ~isequal(length(idq), 3) + warning('Not all months are available in %s.', date2string(dQ(i))) + end + end + case 'end-of-period' + for i=1:dQ.ndat + idq = find(dq==dQ(i)); + tdata(i,:) = ds.data(idq(end),:); + if ds.dates.freq==365 + lastday = datevec(ds.dates.time(idq(end))); + if lastday(3)<expectedlastday(dQ(i)) + warning('The last available day is not the last observed day of %s.', date2string(dQ(i))) + end + end + if ds.dates.freq==12 + lastmonth = ds.dates.time(idq(end), 2); + if lastmonth<expectedlastmonth(dQ(i)) + warning('The last available day is not the last observed day of %s.', date2string(dQ(i))) + end + end + end + otherwise + error('Unknow method.') + end + + ts = dseries(tdata, dQ, ds.name, ds.tex); + +end + +function eld = expectedlastday(q) + if ismember(q.time(2), [1,4]) + eld = 31; % last day of March or December + else + eld = 30; % last day of June or September + end +end + +function elm = expectedlastmonth(q) + switch q.time(2) + case 1 + elm = 3; + case 2 + elm = 6; + case 3 + elm = 9; + case 4 + elm = 12; + otherwise + end +end + +function iad = isalldays(m, id) + if ismember(m.time(2), [3, 4]) + iad = length(id)==92; + elseif m.time(2)==2 + iad = length(id)==91; + else % contains February + if isleapyear(m.time(1)) + iad = length(id)==91; + else + iad = length(id)==90; + end + end +end \ No newline at end of file