Commit 8114a56f authored by Houtan Bastani's avatar Houtan Bastani

template for making a report with one table

parent d2ddbc2b
Pipeline #1729 passed with stage
in 1 minute and 12 seconds
function make_report_table(M_, options_, title, table_dir, table_root_name, column_names, row_names, values, val_precis)
%
% Copyright (C) 2015-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/>.
if options_.noprint
return
end
nv = rows(values);
if ~isempty(row_names)
assert(length(row_names) == nv);
end
rep = report('directory', CheckPath('Output', M_.dname));
rep.addPage();
rep.addSection();
rep.addTable('title', title, ...
'column_names', column_names, ...
'precision', val_precis, ...
'tableDirName', table_dir, ...
'tableName', [M_.fname '_' table_root_name '.tex']);
for i = 1:nv
if isempty(row_names)
rep.addData('label', '', 'data', values(i, :));
else
rep.addData('label', row_names{i}, 'data', values(i, :));
end
end
rep.write();
rep.compile();
end
......@@ -44,7 +44,7 @@ if strcmpi(o.orientation, 'landscape')
fprintf(fid, ',landscape');
end
fprintf(fid, ']{geometry}\n');
fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox, multicol}\n');
fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox, multicol, caption}\n');
fprintf(fid, '\\pgfplotsset{compat=1.5.1}');
fprintf(fid, ['\\makeatletter\n' ...
'\\def\\blfootnote{\\gdef\\@thefnmark{}\\@footnotetext}\n' ...
......
function tf = hasLabel(o)
%function tf = hasLabel(o)
% Returns true if the label is nonempty, false otherwise
%
% INPUTS
% o [report_data] report_data object
%
%
% OUTPUTS
% tf [boolean] label empty status
%
% 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/>.
tf = ~isempty(o.label);
end
function n = nCols(o)
%function n = nCols(o)
% Returns the number of columns of data
%
% INPUTS
% o [report_data] report_data object
%
%
% OUTPUTS
% n [integer] number of columns of data
%
% 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/>.
n = length(o.data);
end
......@@ -18,14 +18,15 @@ classdef report_data < handle
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
properties (SetAccess = private)
data = ''
tableSubSectionHeader = ''
tableAlignRight = false
tableRowColor = 'white'
tableRowIndent = 0
tableNaNSymb = 'NaN'
tablePrecision = ''
zeroTol = 1e-6
data = '' % The data for the table. Default: none.
label = '' % Row label for data. Default: none.
tableSubSectionHeader = '' % A header for a subsection of the table. No data will be associated with it. It is equivalent to adding an empty series with a name. Default: ''
tableAlignRight = false % Whether or not to align the series name to the right of the cell. Default: false.
tableRowColor = 'white' % The color that you want the row to be. Predefined values include LightCyan and Gray. Default: white.
tableRowIndent = 0 % The number of times to indent the name of the series in the table. Used to create subgroups of series. Default: 0.
tableNaNSymb = 'NaN' % Replace NaN values with the text in this option. Default: NaN.
tablePrecision = '' % The number of decimal places to report in the table data. Default: the value set by precision.
zeroTol = 1e-6 % The zero tolerance. Anything smaller than zeroTol and larger than -zeroTol will be set to zero before being graphed or written to the table. Default: 1e-6.
end
methods
function o = report_data(varargin)
......@@ -69,9 +70,20 @@ classdef report_data < handle
error('@report_data.report_data: %s is not a recognized option.', pair{1});
end
end
assert(~isempty(o.tableSubSectionHeader) || (isnumeric(o.data) && isvector(o.data)), '@report_data.report_data: data must be a numeric vector');
assert(ischar(o.label), '@report_data.report_data: label must be a string');
assert(ischar(o.tableSubSectionHeader), '@report_data.report_data: tableSubSectionHeader must be a string');
assert(ischar(o.tableRowColor), '@report_data.report_data: tableRowColor must be a string');
assert(isint(o.tableRowIndent) && o.tableRowIndent >= 0, '@report_data.report_data: tableRowIndent must be an integer >= 0');
assert(islogical(o.tableAlignRight), '@report_data.report_data: tableAlignRight must be true or false');
assert(ischar(o.tableNaNSymb), '@report_data.report_data: tableNaNSymb must be a string');
assert(isempty(o.tablePrecision) || (isint(o.tablePrecision) && o.tablePrecision >= 0), '@report_data.report_data: tablePrecision must be a non-negative integer');
end
end
methods (Access = ?report_table, Hidden = true)
n = nCols(o)
tf = hasLabel(o)
writeDataForTable(o, fid, precision)
end
end
......@@ -32,83 +32,62 @@ function writeDataForTable(o, fid, precision)
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
%% Validate options provided by user
assert(ischar(o.tableSubSectionHeader), '@report_data.writeDataForTable: tableSubSectionHeader must be a string');
if isempty(o.tableSubSectionHeader)
assert(~isempty(o.data) && iscell(o.data), ...
'@report_data.writeDataForTable: must provide data as a cell');
end
assert(ischar(o.tableRowColor), '@report_data.writeDataForTable: tableRowColor must be a string');
assert(isint(o.tableRowIndent) && o.tableRowIndent >= 0, ...
'@report_data.writeDataForTable: tableRowIndent must be an integer >= 0');
assert(islogical(o.tableAlignRight), '@report_data.writeDataForTable: tableAlignRight must be true or false');
assert(ischar(o.tableNaNSymb), '@report_data.writeDataForTable: tableNaNSymb must be a string');
if ~isempty(o.tablePrecision)
assert(isint(o.tablePrecision) && o.tablePrecision >= 0, ...
'@report_data.writeDataForTable: tablePrecision must be a non-negative integer');
precision = o.tablePrecision;
end
rounding = 10^precision;
%% Write Output
fprintf(fid, '%% Table Data (report_data)\n');
nrows = length(o.data{1});
ncols = length(o.data);
for i = 1:nrows
if ~isempty(o.tableRowColor) && ~strcmpi(o.tableRowColor, 'white')
fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
else
fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
end
if ~isempty(o.tableSubSectionHeader)
fprintf(fid, '\\textbf{%s}', o.tableSubSectionHeader);
fprintf(fid, '%s', repmat(' &', 1, ncols-1));
fprintf(fid, '\\\\%%\n');
return
if ~isempty(o.tableRowColor) && ~strcmpi(o.tableRowColor, 'white')
fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
else
fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
end
if ~isempty(o.tableSubSectionHeader)
fprintf(fid, '\\textbf{%s}', o.tableSubSectionHeader);
fprintf(fid, '%s', repmat(' &', 1, ncols-1));
fprintf(fid, '\\\\%%\n');
return
end
if o.tableAlignRight
fprintf(fid, '\\multicolumn{1}{r}{');
end
if o.tableRowIndent == 0
fprintf(fid, '\\noindent ');
else
for j=1:o.tableRowIndent
fprintf(fid,'\\indent ');
end
if o.tableAlignRight
fprintf(fid, '\\multicolumn{1}{r}{');
end
if o.tableAlignRight
fprintf(fid, '}');
end
if ~isempty(o.label)
fprintf(fid, '$%s$ & ', o.label);
end
for j = 1:ncols
val = o.data(j);
if val < o.zeroTol && val > -o.zeroTol
val = 0;
elseif ~isnan(val)
% Use round half away from zero rounding
val = round(val*rounding)/rounding;
end
if o.tableRowIndent == 0
fprintf(fid, '\\noindent ');
if isnan(val)
val = o.tableNaNSymb;
dataString = '%s';
else
for j=1:o.tableRowIndent
fprintf(fid,'\\indent ');
end
end
if o.tableAlignRight
fprintf(fid, '}');
dataString = sprintf('%%.%df', precision);
end
for j = 1:ncols
val = o.data{j}(i);
if iscell(val)
val = val{:};
end
if isnan(val)
val = o.tableNaNSymb;
dataString = '%s';
elseif isnumeric(val)
dataString = sprintf('%%.%df', precision);
if val < o.zeroTol && val > -o.zeroTol
val = 0;
end
% Use round half away from zero rounding
val = round(val*rounding)/rounding;
if isnan(val)
val = o.tableNaNSymb;
dataString = '%s';
end
else
dataString = '%s';
val = regexprep(val, '_', '\\_');
end
fprintf(fid, dataString, val);
if j ~= ncols
fprintf(fid, ' & ');
else
fprintf(fid, '\\\\%%\n');
end
fprintf(fid, dataString, val);
if j ~= ncols
fprintf(fid, ' & ');
else
fprintf(fid, '\\\\%%\n');
end
end
end
......@@ -22,11 +22,7 @@ classdef report_table < handle
end
properties (Access = private)
series = {}
table_data = {}
% Not documented
preamble = {''}
afterward = {''}
column_names = ''
table_data = {} % The cell that provides the data for the table. Defalut: none
end
properties (SetAccess = private)
tableDirName = 'tmpRepDir' % The name of the folder in which to store this table. Default: tmpRepDir.
......@@ -34,15 +30,18 @@ classdef report_table < handle
title = {''} % Table Title. Default: none.
titleFormat = '' % A string representing the valid LATEX markup to use on title. The number of cell array entries must be equal to that of the title option if you do not want to use the default value for the title (and subtitles). Default: \large\bfseries.
showHlines = false % Whether or not to show horizontal lines separating the rows. Default: false.
showVlines = false %
showVlines = false % Whether or not to show vertical lines separating the columns. Default: false.
vlineAfter = '' % Show a vertical line after the specified date (or dates if a cell array of dates is passed). Default: empty.
vlineAfterEndOfPeriod = false % Show a vertical line after the end of every period (i.e. after every year, after the fourth quarter, etc.). Default: false.
data = '' % The dseries that provides the data for the graph. Default: none.
data = '' % The dseries that provides the time series data for the table. Default: none.
seriesToUse = '' % The names of the series contained in the dseries provided to the data option. If empty, use all series provided to data option. Default: empty.
range = {} % The date range of the data to be displayed. Default: all.
precision = 1 % The number of decimal places to report in the table data (rounding done via the round half away from zero method). Default: 1.
writeCSV = false % Whether or not to write a CSV file containing the data displayed in the table. The file will be saved in the directory specified by tableDirName with the same base name as specified by tableName with the ending .csv. Default: false.
highlightRows = {''} % A cell array containing the colors to use for row highlighting. See shadeColor for how to use colors with reports. Highlighting for a specific row can be overridden by using the tableRowColor option to addSeries. Default: empty.
column_names = '' % The column names when using table_data. Default: none
preamble = {''} % Text before table. Default: none
afterward = {''} % Text after table. Default: none
end
methods (Access = ?section)
function o = report_table(varargin)
......@@ -114,8 +113,8 @@ classdef report_table < handle
assert(isempty(o.range) || length(o.range) <=2 && allCellsAreDatesRange(o.range), ...
['@report_table.report_table: range is specified as a dates range, e.g. ' ...
'''dates(''1999q1''):dates(''1999q3'')''.']);
assert(isempty(o.data) || isdseries(o.data), ...
'@report_table.report_table: data must be a dseries');
assert(isempty(o.data) || isdseries(o.data) || iscell(o.data), ...
'@report_table.report_table: data must be a dseries or a cell');
assert(isempty(o.seriesToUse) || iscellstr(o.seriesToUse), ...
'@report_table.report_table: seriesToUse must be a cell array of string(s)');
assert(isempty(o.vlineAfter) || allCellsAreDates(o.vlineAfter), ...
......@@ -134,15 +133,27 @@ classdef report_table < handle
assert(islogical(o.writeCSV), '@report_table.report_table: writeCSV must be either true or false');
assert(iscellstr(o.highlightRows), '@report_table.report_table: highlightRowsmust be a cell string');
% using o.seriesToUse, create series objects and put them in o.series
% create individual series/table_data objects given the `data` option
if ~isempty(o.data)
if isempty(o.seriesToUse)
for i=1:o.data.vobs
o.series{end+1} = report_series('data', o.data{o.data.name{i}});
if isdseries(o.data)
if isempty(o.seriesToUse)
for i=1:o.data.vobs
o.series{end+1} = report_series('data', o.data{o.data.name{i}});
end
else
for i=1:length(o.seriesToUse)
o.series{end+1} = report_series('data', o.data{o.seriesToUse{i}});
end
end
else
for i=1:length(o.seriesToUse)
o.series{end+1} = report_series('data', o.data{o.seriesToUse{i}});
if isnumeric(o.data(1,1))
labels = cell(rows(o.data), 1);
else
labels = o.data(:, 1);
o.data = cell2mat(o.data(:, 2:end));
end
for i = 1:rows(o.data)
o.table_data{end+1} = report_data('data', o.data(i, :), 'label', labels{i});
end
end
end
......
......@@ -33,9 +33,7 @@ function tableName = writeTableFile(o, pg, sec, row, col, rep_dir)
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
ne = length(o.series);
is_data_table = ~isempty(o.table_data);
if ne == 0 && ~is_data_table
if isempty(o.series) && isempty(o.table_data)
warning('@report_table.write: no series to plot, returning');
return
end
......@@ -55,9 +53,23 @@ if fid == -1
end
fprintf(fid, '%% Report_Table Object written %s\n', datestr(now));
fprintf(fid, '\\begin{tabular}[t]{l}\n');
fprintf(fid, '\\noindent\\begin{minipage}[tc]{\\textwidth}\n\\begin{center}');
if ~isempty(o.title)
fprintf(fid, '\\captionof{table}{');
for i=1:length(o.title)
if ~isempty(o.title{i})
fprintf(fid, '%s %s', o.titleFormat{i}, o.title{i});
if i ~= length(o.title)
fprintf(fid, '\\\\\n');
end
end
end
fprintf(fid, '}\n');
end
fprintf(fid, '\\begin{tabular}{c}\n');
fprintf(fid, '\\setlength{\\parindent}{6pt}\n');
fprintf(fid, '\\setlength{\\tabcolsep}{4pt}\n');
fprintf(fid, '\\maxsizebox{\\textwidth}{!}{%%\n');
%number of left-hand columns, 1 until we allow the user to group data,
% e.g.: GDP Europe
......@@ -65,7 +77,7 @@ fprintf(fid, '\\setlength{\\tabcolsep}{4pt}\n');
% GDP Germany
% this example would be two lh columns, with GDP Europe spanning both
nlhc = 1;
if ~is_data_table
if isempty(o.table_data)
fprintf(fid, '\\begin{tabular}{@{}l');
if isempty(o.range)
dates = getMaxRange(o.series);
......@@ -105,15 +117,8 @@ if ~is_data_table
fprintf(fid, '|');
end
end
nrhc = length(rhscols);
ncols = ndates+nlhc+nrhc;
ncols = ndates+nlhc+length(rhscols);
fprintf(fid, '@{}}%%\n');
for i=1:length(o.title)
if ~isempty(o.title{i})
fprintf(fid, '\\multicolumn{%d}{c}{%s %s}\\\\\n', ...
ncols, o.titleFormat{i}, o.title{i});
end
end
fprintf(fid, '\\toprule%%\n');
% Column Headers
......@@ -172,33 +177,15 @@ if ~is_data_table
end
end
end
else
fprintf(fid, '\\begin{tabular}{');
if o.showVlines
fprintf(fid, '|');
end
for i = 1:length(o.column_names)
if isempty(o.column_names{i})
fprintf(fid, 'l');
else
fprintf(fid, 'r');
end
if o.showVlines
fprintf(fid, '|');
end
end
fprintf(fid,'}');
end
% Write Report_Table Data
if ~is_data_table
% Write Report_Table Data
fprintf(fid, '\\\\[-2pt]%%\n');
fprintf(fid, '\\hline%%\n');
fprintf(fid, '%%\n');
if o.writeCSV
csvseries = dseries();
end
for i=1:ne
for i=1:length(o.series)
o.series{i}.writeSeriesForTable(fid, o.range, o.precision, ncols, o.highlightRows{mod(i,length(o.highlightRows))+1});
if o.writeCSV
if isempty(o.series{i}.tableSubSectionHeader)
......@@ -216,25 +203,63 @@ if ~is_data_table
csvseries.save(strrep(o.tableName, '.tex', ''), 'csv');
end
else
% Write Data Table
fprintf(fid, '\\begin{tabular}{');
ncols = o.table_data{1}.nCols();
if o.table_data{1}.hasLabel()
ncols = ncols + 1;
end
if o.showVlines
fprintf(fid, '|');
end
if o.table_data{1}.hasLabel()
fprintf(fid, 'l');
else
fprintf(fid, 'r');
end
if isempty(o.column_names)
fprintf(fid, repmat('r', 1, ncols-1));
else
for i = 2:ncols
keyboard
if isempty(o.column_names{i})
fprintf(fid, 'l');
else
fprintf(fid, 'r');
end
if o.showVlines
fprintf(fid, '|');
end
end
end
fprintf(fid,'}');
fprintf(fid, '\\toprule%%\n');
% Write Report_Table Data
fprintf(fid, '%%\n');
fprintf(fid, '\\hline%%\n');
fprintf(fid, '%%\n');
for i = 1:length(o.column_names)
if ~isempty(o.column_names{i})
fprintf(fid, '%s', o.column_names{i});
end
if i ~= length(o.column_names)
fprintf(fid, ' & ');
if ~isempty(o.column_names)
for i = 1:ncols
if ~isempty(o.column_names{i})
fprintf(fid, '%s', o.column_names{i});
end
if i ~= ncols
fprintf(fid, ' & ');
end
end
fprintf(fid, '\\\\[-2pt]%%\n');
fprintf(fid, '\\hline%%\n');
end
for i=1:length(o.table_data)
o.table_data{i}.writeDataForTable(fid, o.precision);
end
fprintf(fid, '\\\\[-2pt]%%\n');
fprintf(fid, '\\hline%%\n');
o.table_data{1}.writeDataForTable(fid, o.precision);
end
fprintf(fid, '\\bottomrule\n');
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\\end{tabular}}\n');
fprintf(fid, '\\setlength{\\parindent}{0pt}\n \\par \\medskip\n\n');
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\\end{center}\\end{minipage}\n');
fprintf(fid, '%% End Report_Table Object\n');
if fclose(fid) == -1
error('@report_table.writeTableFile: closing %s\n', o.filename);
......
......@@ -32,23 +32,18 @@ function write(o, fid, pg, sec, rep_dir)
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <http://www.gnu.org/licenses/>.
fprintf(fid, '%% Section Object written %s\n', datestr(now));
if isempty(o.elements)
warning(['reporting:section.write(): trying to print a section ' ...
'with no elements. Perhaps you forgot to overwrite the report object.']);
warning('@report:section.write(): trying to print a section with no elements.');
return
end
fprintf(fid, '%% Section Object written %s\n', datestr(now));
fprintf(fid, '\\noindent\\begin{minipage}[tc]{\\textwidth}\n');
if ~isempty(o.height)
fprintf(fid, '\\setlength\\sectionheight{%s}%%\n', o.height);
end
fprintf(fid, '\\maxsizebox{\\linewidth}{');
if isempty(o.height)
fprintf(fid, '!');
fprintf(fid, '\\maxsizebox{\\linewidth}{%s}{%%\n', o.height);
else
fprintf(fid, '\\sectionheight');
fprintf(fid, '\\maxsizebox{\\linewidth}{!}{%%\n');
end
fprintf(fid, '}{%%\n');
fprintf(fid, '\\begin{tabular}[t]{');
for i=1:o.cols
if ~isa(o.elements{1}, 'paragraph')
......@@ -61,17 +56,14 @@ for i=1:o.cols
end
fprintf(fid, '}\n');
ne = numElements(o);
row = 1;
col = 1;
for i=1:ne
for i=1:numElements(o)
if isa(o.elements{i}, 'vspace')
o.elements{i}.write(fid);
fprintf(fid, '\\\\\n');
if col ~= o.cols
fprintf(fid, '\\end{tabular}}\\\\\n');
fprintf(fid, '%% End Section Object\n\n');
return
break
end
else
if isa(o.elements{i}, 'paragraph')
......@@ -89,6 +81,7 @@ for i=1:ne
end
end
end
fprintf(fid, '\\end{tabular}}\\\\\n');
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '}\\end{minipage}\n');
fprintf(fid, '%% End Section Object\n\n');
end
......@@ -297,11 +297,11 @@ rep.addSeries('data', d, ...
'graphFanShadeColor', 'red', 'graphFanShadeOpacity', 20);
% Page 26
% rep.addPage('title', {'report\_data test'}, ...
% 'titleFormat', {'\large\bfseries', '\large'});
% rep.addSection('cols', 1);
% rep.addTable('title', {'Table Data', 'subtitle 2'});
% rep.addData('data', [repmat({'a'},10,1) num2cell(rand(10,6))]);
rep.addPage('title', {'report\_data test'}, ...
'titleFormat', {'\large\bfseries', '\large'});
rep.addSection();
rep.addTable('title', {'Table Data', 'subtitle 2'}, ...
'data', [repmat({'a'},10,1) num2cell(rand(10,6))]);
%% Write & Compile Report
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment