Commit 14478ca1 authored by Stéphane Adjemian's avatar Stéphane Adjemian
Browse files

Added the ability to tag variables in dseries objects.

 - New class member tags

 - Member tags is a structure, initialized to empty.

 - Each user-defined field of this structure must be a vobs(o)*1 cell
   array (each element is associated to a variable).

 - To add a new tag name:

      >> o.tag('type')

 - To associate a tag value to a variable:

      >> o.tag('type', 'Consumption', 'Flow')

   the first argument is the tag name, the second argument is the name
   of the variable, the last argument is the value of the tag.
parent 03b223fa
...@@ -29,6 +29,7 @@ p.name = o.name; ...@@ -29,6 +29,7 @@ p.name = o.name;
p.tex = o.tex; p.tex = o.tex;
p.dates = o.dates; p.dates = o.dates;
p.ops = o.ops; p.ops = o.ops;
p.tags = o.tags;
%@test:1 %@test:1
%$ % Define a dates object %$ % Define a dates object
......
...@@ -23,6 +23,7 @@ properties ...@@ -23,6 +23,7 @@ properties
tex = {}; % TeX names of the variables. tex = {}; % TeX names of the variables.
dates = dates(); % Dates associated to the observations. dates = dates(); % Dates associated to the observations.
ops = {}; % History of operations on the variables. ops = {}; % History of operations on the variables.
tags = struct(); % User defined tags on the variables.
end end
methods methods
...@@ -54,6 +55,7 @@ methods ...@@ -54,6 +55,7 @@ methods
o.tex = {}; o.tex = {};
o.dates = dates(); o.dates = dates();
o.ops = {}; o.ops = {};
o.tags = struct();
return return
case 1 case 1
if isdates(varargin{1}) if isdates(varargin{1})
...@@ -67,17 +69,19 @@ methods ...@@ -67,17 +69,19 @@ methods
o.tex = {}; o.tex = {};
o.dates = varargin{1}; o.dates = varargin{1};
o.ops = {}; o.ops = {};
o.tags = struct();
otherwise otherwise
error('dseries:WrongInputArguments', 'Input (identified as a dates object) must have a unique element!'); error('dseries:WrongInputArguments', 'Input (identified as a dates object) must have a unique element!');
end end
return return
elseif ischar(varargin{1}) elseif ischar(varargin{1})
[init, data, varlist, tex, ops] = load_data(varargin{1}); [init, data, varlist, tex, ops, tags] = load_data(varargin{1});
o.data = data; o.data = data;
o.name = varlist; o.name = varlist;
o.dates = init:init+(nobs(o)-1); o.dates = init:init+(nobs(o)-1);
o.tex = tex; o.tex = tex;
o.ops = ops; o.ops = ops;
o.tags = tags;
elseif ~isoctave() && istable(varargin{1}) elseif ~isoctave() && istable(varargin{1})
% It is assumed that the dates are in the first column. % It is assumed that the dates are in the first column.
thistable = varargin{1}; thistable = varargin{1};
...@@ -86,36 +90,40 @@ methods ...@@ -86,36 +90,40 @@ methods
o.data = varargin{1}{:,2:end}; o.data = varargin{1}{:,2:end};
o.dates = dates(varargin{1}{1,1}{1})+(0:size(varargin{1}, 1)-1); o.dates = dates(varargin{1}{1,1}{1})+(0:size(varargin{1}, 1)-1);
o.ops = cell(length(o.name), 1); o.ops = cell(length(o.name), 1);
o.tags = struct();
elseif isnumeric(varargin{1}) && isequal(ndims(varargin{1}),2) elseif isnumeric(varargin{1}) && isequal(ndims(varargin{1}),2)
o.data = varargin{1}; o.data = varargin{1};
o.name = default_name(vobs(o)); o.name = default_name(vobs(o));
o.tex = name2tex(o.name); o.tex = name2tex(o.name);
o.dates = dates(1,1):dates(1,1)+(nobs(o)-1); o.dates = dates(1,1):dates(1,1)+(nobs(o)-1);
o.ops = cell(length(o.name), 1); o.ops = cell(length(o.name), 1);
o.tags = struct();
end end
case {2,3,4} case {2,3,4}
if isequal(nargin,2) && ischar(varargin{1}) && isdates(varargin{2}) if isequal(nargin,2) && ischar(varargin{1}) && isdates(varargin{2})
% Instantiate dseries object with a data file and force the initial date to % Instantiate dseries object with a data file and force the initial date to
% be as given by the second input argument (initial period represented % be as given by the second input argument (initial period represented
% with a dates object). % with a dates object).
[init, data, varlist, tex, ops] = load_data(varargin{1}); [init, data, varlist, tex, ops, tags] = load_data(varargin{1});
o.data = data; o.data = data;
o.name = varlist; o.name = varlist;
o.dates = varargin{2}:varargin{2}+(nobs(o)-1); o.dates = varargin{2}:varargin{2}+(nobs(o)-1);
o.tex = tex; o.tex = tex;
o.ops = ops; o.ops = ops;
o.tags = tags;
return return
end end
if isequal(nargin,2) && ischar(varargin{1}) && ischar(varargin{2}) && isdate(varargin{2}) if isequal(nargin,2) && ischar(varargin{1}) && ischar(varargin{2}) && isdate(varargin{2})
% Instantiate dseries object with a data file and force the initial date to % Instantiate dseries object with a data file and force the initial date to
% be as given by the second input argument (initial period represented with a % be as given by the second input argument (initial period represented with a
% string). % string).
[init, data, varlist, tex, ops] = load_data(varargin{1}); [init, data, varlist, tex, ops, tags] = load_data(varargin{1});
o.data = data; o.data = data;
o.name = varlist; o.name = varlist;
o.dates = dates(varargin{2}):dates(varargin{2})+(nobs(o)-1); o.dates = dates(varargin{2}):dates(varargin{2})+(nobs(o)-1);
o.tex = tex; o.tex = tex;
o.ops = ops; o.ops = ops;
o.tags = tags;
return return
end end
a = varargin{1}; a = varargin{1};
...@@ -177,6 +185,7 @@ methods ...@@ -177,6 +185,7 @@ methods
o.name = default_name(vobs(o)); o.name = default_name(vobs(o));
end end
o.ops = cell(length(o.name), 1); o.ops = cell(length(o.name), 1);
o.tags = struct();
if ~isempty(d) if ~isempty(d)
if vobs(o)==length(d) if vobs(o)==length(d)
for i=1:vobs(o) for i=1:vobs(o)
......
...@@ -78,6 +78,25 @@ end ...@@ -78,6 +78,25 @@ end
a.ops = vertcat(b.ops,c.ops); a.ops = vertcat(b.ops,c.ops);
a.name = vertcat(b.name,c.name); a.name = vertcat(b.name,c.name);
a.tex = vertcat(b.tex,c.tex); a.tex = vertcat(b.tex,c.tex);
btagnames = fieldnames(b.tags);
ctagnames = fieldnames(c.tags);
atagnames = union(btagnames, ctagnames);
if isempty(atagnames)
a.tags = struct();
else
for i=1:length(atagnames)
if ismember(atagnames{i}, btagnames) && ismember(atagnames{i}, ctagnames)
a.tags.(atagnames{i}) = vertcat(b.tags.(atagnames{i}), b.tags.(atagnames{i}));
elseif ismember(atagnames{i}, btagnames)
a.tags.(atagnames{i}) = vertcat(b.tags.(atagnames{i}), cell(vobs(c), 1));
elseif ismember(atagnames{i}, ctagnames)
a.tags.(atagnames{i}) = vertcat(cell(vobs(b), 1), c.tags.(atagnames{i}));
else
error('dseries::horzcat: This is a bug!')
end
end
end
if ~( d_nobs_flag(1) || d_init_flag(1) ) if ~( d_nobs_flag(1) || d_init_flag(1) )
a.data = [b.data,c.data]; a.data = [b.data,c.data];
a.dates = b.dates; a.dates = b.dates;
...@@ -331,3 +350,48 @@ end ...@@ -331,3 +350,48 @@ end
%$ %$
%$ T = t; %$ T = t;
%@eof:7 %@eof:7
%@test:8
%$ % Define a data set.
%$ A = [transpose(1:10),2*transpose(1:10)];
%$ B = [transpose(1:10),2*transpose(1:10)];
%$
%$ % Define names
%$ A_name = {'A1';'A2'};
%$ B_name = {'B1';'B2'};
%$
%$ % Define expected results.
%$ e.init = dates(1,1);
%$ e.freq = 1;
%$ e.name = {'A1';'A2';'B1';'B2'};
%$ e.data = [A,B];
%$
%$ % Instantiate two time series objects.
%$ ts1 = dseries(A,[],A_name,[]);
%$ ts2 = dseries(B,[],B_name,[]);
%$ ts1.tag('t1');
%$ ts1.tag('t1', 'A1', 'Stock');
%$ ts1.tag('t1', 'A2', 'Flow');
%$ ts2.tag('t2');
%$ ts2.tag('t2', 'B1', 0);
%$ ts2.tag('t2', 'B2', 1);
%$
%$ % Call the tested method.
%$ try
%$ ts3 = [ts1,ts2];
%$ t(1) = true;
%$ catch
%$ t(1) = false;
%$ end
%$
%$ % Check the results.
%$ if t(1)
%$ t(2) = dassert(ts3.init,e.init);
%$ t(3) = dassert(ts3.freq,e.freq);
%$ t(4) = dassert(ts3.data,e.data);
%$ t(5) = dassert(ts3.name,e.name);
%$ t(6) = dassert(ts3.tags.t1,{'Stock';'Flow';[];[]});
%$ t(7) = dassert(ts3.tags.t2,{[];[];0;1});
%$ end
%$ T = all(t);
%@eof:8
...@@ -48,23 +48,64 @@ end ...@@ -48,23 +48,64 @@ end
% Keep the second input argument constant. % Keep the second input argument constant.
p = copy(p); p = copy(p);
% Add NaNs if necessary.
[o, p] = align(o, p); [o, p] = align(o, p);
n = length(id); n = length(id);
% Get tag names in p
ptagnames = fieldnames(p.tags);
if n>1 if n>1
[id, jd] = sort(id); [id, jd] = sort(id);
p.data = p.data(:,jd); p.data = p.data(:,jd);
p.name = p.name(jd); p.name = p.name(jd);
p.tex = p.tex(jd); p.tex = p.tex(jd);
p.ops = p.ops(jd); p.ops = p.ops(jd);
if ~isempty(ptagnames)
for i = 1:length(ptagnames)
p.tags.(ptagnames{i}) = p.tags.(ptagnames{i})(jd);
end
end
end
% Get tag names in o
otagnames = fieldnames(o.tags);
% Merge tag names
if isempty(otagnames) && isempty(ptagnames)
notags = true;
else
notags = false;
dtagnames_o = setdiff(ptagnames, otagnames);
dtagnames_p = setdiff(otagnames, ptagnames);
if ~isempty(dtagnames_o)
% If p has tags that are not in o...
for i=1:length(dtagnames_o)
o.tags.(dtagnames_o{i}) = cell(vobs(o), 1);
end
end
if ~isempty(dtagnames_p)
% If o has tags that are not in p...
for i=1:length(dtagnames_p)
p.tags.(dtagnames_p{i}) = cell(vobs(p), 1);
end
end
end end
% Update list of tag names in o.
otagnames = fieldnames(o.tags);
for i=1:n for i=1:n
o.data = insert_column_vector_in_a_matrix(o.data, p.data(:,i),id(i)); o.data = insert_column_vector_in_a_matrix(o.data, p.data(:,i),id(i));
o.name = insert_object_in_a_one_dimensional_cell_array(o.name, p.name{i}, id(i)); o.name = insert_object_in_a_one_dimensional_cell_array(o.name, p.name{i}, id(i));
o.tex = insert_object_in_a_one_dimensional_cell_array(o.tex, p.tex{i}, id(i)); o.tex = insert_object_in_a_one_dimensional_cell_array(o.tex, p.tex{i}, id(i));
o.ops = insert_object_in_a_one_dimensional_cell_array(o.ops, p.ops{i}, id(i)); o.ops = insert_object_in_a_one_dimensional_cell_array(o.ops, p.ops{i}, id(i));
if ~notags
for j=1:length(otagnames)
o.tags.(otagnames{j}) = insert_object_in_a_one_dimensional_cell_array(o.tags.(otagnames{j}), p.tags.(otagnames{j}){i}, id(i));
end
end
id = id+1; id = id+1;
end end
...@@ -83,6 +124,16 @@ end ...@@ -83,6 +124,16 @@ end
%$ % Instantiate two dseries objects. %$ % Instantiate two dseries objects.
%$ ts1 = dseries(A, A_init, A_name,[]); %$ ts1 = dseries(A, A_init, A_name,[]);
%$ ts2 = dseries(B, B_init, B_name,[]); %$ ts2 = dseries(B, B_init, B_name,[]);
%$ ts1.tag('t1');
%$ ts1.tag('t1','A1',1);
%$ ts1.tag('t1','A2',1);
%$ ts1.tag('t1','A3',0);
%$ ts2.tag('t1');
%$ ts2.tag('t1','B1',1);
%$ ts2.tag('t1','B2',1);
%$ ts2.tag('t2');
%$ ts2.tag('t2','B1','toto');
%$ ts2.tag('t2','B2','titi');
%$ %$
%$ try %$ try
%$ ts1 = insert(ts1,ts2,[1,2]); %$ ts1 = insert(ts1,ts2,[1,2]);
...@@ -91,11 +142,14 @@ end ...@@ -91,11 +142,14 @@ end
%$ t = 0; %$ t = 0;
%$ end %$ end
%$ %$
%$ if length(t)>1 %$
%$ t(2) = dassert(ts1.vobs,{'B1';'A1';'B2';'A3'}); %$ if t(1)
%$ t(2) = dassert(ts1.name,{'B1';'A1';'B2';'A2';'A3'});
%$ t(3) = dassert(ts1.nobs,10); %$ t(3) = dassert(ts1.nobs,10);
%$ eB = [NaN(2,2); B; NaN(3,2)]; %$ eB = [NaN(2,2); B; NaN(3,2)];
%$ t(4) = dassert(ts1.data,[eB(:,1), A(:,1), eB(:,2), A(:,2:3)], 1e-15); %$ t(4) = dassert(ts1.data,[eB(:,1), A(:,1), eB(:,2), A(:,2:3)], 1e-15);
%$ t(5) = dassert(ts1.tags.t1,{1; 1; 1; 1; 0});
%$ t(6) = dassert(ts1.tags.t2,{'toto'; []; 'titi'; []; []});
%$ end %$ end
%$ T = all(t); %$ T = all(t);
%@eof:1 %@eof:1
......
...@@ -73,6 +73,13 @@ if ~isequal(o.ops, p.ops) ...@@ -73,6 +73,13 @@ if ~isequal(o.ops, p.ops)
warning on backtrace warning on backtrace
end end
if ~isequal(o.tags, p.tags)
warning off backtrace
warning('dseries::isequal: Both input arguments have different tags!')
warning on backtrace
end
if nargin<3 if nargin<3
b = isequal(o.data, p.data); b = isequal(o.data, p.data);
else else
......
...@@ -42,12 +42,35 @@ if ~isequal(frequency(o), frequency(p)) ...@@ -42,12 +42,35 @@ if ~isequal(frequency(o), frequency(p))
end end
q = dseries(); q = dseries();
[q.name, IBC, junk] = unique([o.name; p.name], 'last'); [q.name, IBC, junk] = unique([o.name; p.name], 'last');
tex = [o.tex; p.tex]; tex = [o.tex; p.tex];
q.tex = tex(IBC); q.tex = tex(IBC);
ops = [o.ops; p.ops]; ops = [o.ops; p.ops];
q.ops = ops(IBC); q.ops = ops(IBC);
otagnames = fieldnames(o.tags);
ptagnames = fieldnames(p.tags);
qtagnames = union(otagnames, ptagnames);
if isempty(qtagnames)
q.tags = struct();
else
for i=1:length(qtagnames)
if ismember(qtagnames{i}, otagnames) && ismember(qtagnames{i}, ptagnames)
q.tags.(qtagnames{i}) = vertcat(o.tags.(otagnames{i}), p.tags.(ptagnames{i}));
elseif ismember(qtagnames{i}, otagnames)
q.tags.(qtagnames{i}) = vertcat(o.tags.(qtagnames{i}), cell(vobs(p), 1));
elseif ismember(qtagnames{i}, ptagnames)
q.tags.(qtagnames{i}) = vertcat(cell(vobs(o), 1), p.tags.(qtagnames{i}));
else
error('dseries::horzcat: This is a bug!')
end
q.tags.(qtagnames{i}) = q.tags.(qtagnames{i})(IBC);
end
end
if nobs(o) == 0 if nobs(o) == 0
q = copy(p); q = copy(p);
elseif nobs(p) == 0 elseif nobs(p) == 0
...@@ -93,22 +116,26 @@ q.dates = q_init:q_init+(nobs(q)-1); ...@@ -93,22 +116,26 @@ q.dates = q_init:q_init+(nobs(q)-1);
%$ % Define names %$ % Define names
%$ A_name = {'A1';'A2'}; B_name = {'A1'}; %$ A_name = {'A1';'A2'}; B_name = {'A1'};
%$ %$
%$ t = zeros(4,1); %$ % Instantiate two time series objects and merge.
%$
%$ % Instantiate a time series object.
%$ try %$ try
%$ ts1 = dseries(A,[],A_name,[]); %$ ts1 = dseries(A,[],A_name,[]);
%$ ts1.tag('type');
%$ ts1.tag('type', 'A1', 'Stock');
%$ ts1.tag('type', 'A2', 'Flow');
%$ ts2 = dseries(B,[],B_name,[]); %$ ts2 = dseries(B,[],B_name,[]);
%$ ts2.tag('type');
%$ ts2.tag('type', 'A1', 'Flow');
%$ ts3 = merge(ts1,ts2); %$ ts3 = merge(ts1,ts2);
%$ t(1) = 1; %$ t(1) = 1;
%$ catch %$ catch
%$ t = 0; %$ t = 0;
%$ end %$ end
%$ %$
%$ if length(t)>1 %$ if t(1)
%$ t(2) = dassert(ts3.vobs,2); %$ t(2) = dassert(ts3.vobs,2);
%$ t(3) = dassert(ts3.nobs,10); %$ t(3) = dassert(ts3.nobs,10);
%$ t(4) = dassert(ts3.data,[B, A(:,2)],1e-15); %$ t(4) = dassert(ts3.data,[B, A(:,2)],1e-15);
%$ t(5) = dassert(ts3.tags.type, {'Flow';'Flow'});
%$ end %$ end
%$ T = all(t); %$ T = all(t);
%@eof:1 %@eof:1
...@@ -120,12 +147,15 @@ q.dates = q_init:q_init+(nobs(q)-1); ...@@ -120,12 +147,15 @@ q.dates = q_init:q_init+(nobs(q)-1);
%$ % Define names %$ % Define names
%$ A_name = {'A1';'A2'}; B_name = {'B1'}; %$ A_name = {'A1';'A2'}; B_name = {'B1'};
%$ %$
%$ t = zeros(4,1); %$ % Instantiate two time series objects and merge them.
%$
%$ % Instantiate a time series object.
%$ try %$ try
%$ ts1 = dseries(A,[],A_name,[]); %$ ts1 = dseries(A,[],A_name,[]);
%$ ts1.tag('t1');
%$ ts1.tag('t1', 'A1', 'Stock');
%$ ts1.tag('t1', 'A2', 'Flow');
%$ ts2 = dseries(B,[],B_name,[]); %$ ts2 = dseries(B,[],B_name,[]);
%$ ts2.tag('t2');
%$ ts2.tag('t2', 'B1', 1);
%$ ts3 = merge(ts1,ts2); %$ ts3 = merge(ts1,ts2);
%$ t(1) = 1; %$ t(1) = 1;
%$ catch %$ catch
...@@ -136,6 +166,8 @@ q.dates = q_init:q_init+(nobs(q)-1); ...@@ -136,6 +166,8 @@ q.dates = q_init:q_init+(nobs(q)-1);
%$ t(2) = dassert(ts3.vobs,3); %$ t(2) = dassert(ts3.vobs,3);
%$ t(3) = dassert(ts3.nobs,10); %$ t(3) = dassert(ts3.nobs,10);
%$ t(4) = dassert(ts3.data,[A, B],1e-15); %$ t(4) = dassert(ts3.data,[A, B],1e-15);
%$ t(5) = dassert(ts3.tags.t1, {'Flow';'Flow';[]});
%$ t(6) = dassert(ts3.tags.t2, {[];[];1});
%$ end %$ end
%$ T = all(t); %$ T = all(t);
%@eof:2 %@eof:2
...@@ -69,6 +69,10 @@ if ~isequal(o.tex, p.tex) ...@@ -69,6 +69,10 @@ if ~isequal(o.tex, p.tex)
warning('dseries::ne: Both input arguments do not have the same tex names!') warning('dseries::ne: Both input arguments do not have the same tex names!')
end end
if ~isequal(o.tags, p.tags)
warning('dseries::ne: Both input arguments do not have the same tags!')
end
b = ne(o.data, p.data); b = ne(o.data, p.data);
%@test:1 %@test:1
......
...@@ -42,6 +42,10 @@ o.data(:,id) = []; ...@@ -42,6 +42,10 @@ o.data(:,id) = [];
o.name(id) = []; o.name(id) = [];
o.tex(id) = []; o.tex(id) = [];
o.ops(id) = []; o.ops(id) = [];
otagnames = fieldnames(o.tags);
for i=1:length(otagnames)
o.tags.(otagnames{i})(id) = [];
end
%@test:1 %@test:1
%$ % Define a datasets. %$ % Define a datasets.
...@@ -53,16 +57,21 @@ o.ops(id) = []; ...@@ -53,16 +57,21 @@ o.ops(id) = [];
%$ % Instantiate a time series object. %$ % Instantiate a time series object.
%$ try %$ try
%$ ts1 = dseries(A,[],A_name,[]); %$ ts1 = dseries(A,[],A_name,[]);
%$ ts1.tag('type');
%$ ts1.tag('type', 'A1', 1);
%$ ts1.tag('type', 'A2', 2);
%$ ts1.tag('type', 'A3', 3);
%$ ts1.pop_('A2'); %$ ts1.pop_('A2');
%$ t(1) = 1; %$ t(1) = 1;
%$ catch %$ catch
%$ t(1) = 0; %$ t(1) = 0;
%$ end %$ end
%$ %$
%$ if length(t)>1 %$ if t(1)
%$ t(2) = dassert(ts1.vobs,2); %$ t(2) = dassert(ts1.vobs,2);
%$ t(3) = dassert(ts1.nobs,10); ts1 %$ t(3) = dassert(ts1.nobs,10);
%$ t(4) = dassert(ts1.data,[A(:,1), A(:,3)],1e-15); %$ t(4) = dassert(ts1.data,[A(:,1), A(:,3)],1e-15);
%$ t(5) = dassert(ts1.tags.type, {1;3});
%$ end %$ end
%$ T = all(t); %$ T = all(t);
%@eof:1 %@eof:1
\ No newline at end of file
...@@ -65,10 +65,31 @@ switch format ...@@ -65,10 +65,31 @@ switch format
fprintf(fid,[ '''' o.ops{i} '''']); fprintf(fid,[ '''' o.ops{i} '''']);
end end
if i<vobs(o) if i<vobs(o)
fprintf(fid,'; '); fprintf(fid,';');
end end
end end
fprintf(fid,'};\n\n'); fprintf(fid,'};\n\n');
if ~isempty(fieldnames(o.tags))
% User has defined tags on the variables.
tagnames = fieldnames(o.tags);
TAGS__ = fprintf(fid, 'struct();\n');
for i=1:length(tagnames)
fprintf(fid, 'TAGS__.%s = cell(%s, 1);\n', tagnames{i}, num2str(vobs(o)));
for j=1:vobs(o)
if ~isempty(o.tags.(tagnames{i}){j})
if ischar(o.tags.(tagnames{i}){j})
fprintf(fid, 'TAGS__.%s(%s) = {''%s''};\n', tagnames{i}, num2str(j), o.tags.(tagnames{i}){j});
elseif isnumeric(o.tags.(tagnames{i}){j}) && iscalar(o.tags.(tagnames{i}){j})
fprintf(fid, 'TAGS__.%s(%s) = {%s};\n', tagnames{i}, num2str(j), o.tags.(tagnames{i}){j});
else
error('dseries::tags: Cannot save this type of tag!')
end