1function df = df_pad(df, dim, n, coltype)
2  %# function resu = df_pad(df, dim, n, coltype = [])
3  %# given a dataframe, insert n rows or columns, and adjust everything
4  %# accordingly. Coltype is a supplemental argument:
5  %# dim = 1 => not used
6  %# dim = 2 => type of the added column(s)
7  %# dim = 3 => index of columns receiving a new sheet (default: all)
8
9  %% Copyright (C) 2009-2017 Pascal Dupuis <cdemills@gmail.com>
10  %%
11  %% This file is part of the dataframe package for Octave.
12  %%
13  %% This package is free software; you can redistribute it and/or
14  %% modify it under the terms of the GNU General Public
15  %% License as published by the Free Software Foundation;
16  %% either version 2, or (at your option) any later version.
17  %%
18  %% This package is distributed in the hope that it will be useful,
19  %% but WITHOUT ANY WARRANTY; without even the implied
20  %% warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
21  %% PURPOSE.  See the GNU General Public License for more
22  %% details.
23  %%
24  %% You should have received a copy of the GNU General Public
25  %% License along with this package; see the file COPYING.  If not,
26  %% see <http://www.gnu.org/licenses/>.
27
28  if (nargin < 4), coltype = []; end
29  try
30    NA = NA;
31  catch
32    NA = NaN;
33  end
34
35  switch dim
36    case 1
37      if (~isempty (df.x_name{1})),
38        if (length (df.x_name{1}) < df.x_cnt(1)+n)
39          %# generate a name for the new row(s)
40          df.x_name{1}(df.x_cnt(1)+(1:n), 1) = {'_'};
41          df.x_over{1}(1, df.x_cnt(1)+(1:n), 1) = true;
42        end
43      end
44      %# complete row indexes: by default, row number.
45      if (isempty (df.x_ridx))
46        (1:n);
47        dummy = ans(:);
48      else
49        dummy = vertcat (df.x_ridx, repmat (size (df.x_ridx, 1)+(1:n).', ...
50                                           1, size (df.x_ridx, 2)));
51      end
52      df.x_ridx = dummy;
53      %# pad every line
54      for indi = (1:min (size (df.x_data, 2), df.x_cnt(2)))
55        neff = n + df.x_cnt(1) - size (df.x_data{indi}, 1);
56        if (neff > 0)
57          m = max(1, size (df.x_data{indi}, 2));
58          switch df.x_type{indi}
59            case {'char'}
60              %# there is no 'string NA'
61              dummy = {}; dummy(1:neff, 1:m) = 'NA';
62              dummy = vertcat (df.x_data{indi}, dummy);
63            case { 'double'}
64              dummy = vertcat (df.x_data{indi}, repmat (NA, neff, m));
65            %# there is no 'NA' with logical values, avoid casting error
66            case {'logical'}
67              dummy = vertcat (df.x_data{indi}, repmat (false, neff, m));
68            otherwise
69              dummy = cast (vertcat (df.x_data{indi}, repmat (NA, neff, m)), ...
70                            df.x_type{indi});
71          end
72          df.x_data{indi} = dummy;
73          if (isempty (df.x_rep{indi}))
74            df.x_rep{indi} = 1;
75          end
76        end
77      end
78      df.x_cnt(1) = df.x_cnt(1) + n;
79
80    case 2
81      %# create new columns
82      if (isempty (coltype))
83        error ('df_pad: dim equals 2, and coltype undefined');
84      end
85      if (length (n) > 1) %#second value is an offset
86        indc =  n(2); n = n(1);
87        if (indc < df.x_cnt(2)),
88          %# shift to the right
89          df.x_name{2}(n + (indc+1:end)) =  df.x_name{2}(indc+1:end);
90          df.x_over{2}(n + (indc+1:end)) =  df.x_over{2}(indc+1:end);
91          dummy = cstrcat (repmat ('_', n, 1), ...
92                           strjust (num2str(indc + (1:n).'), 'left'));
93          df.x_name{2}(indc + (1:n)) = cellstr (dummy);
94          df.x_over{2}(indc + (1:n)) = true;
95          df.x_type(n+(indc+1:end)) = df.x_type(indc+1:end);
96          df.x_type(indc + (1:n)) = NA;
97          df.x_data(n + (indc+1:end)) = df.x_data(indc+1:end);
98          df.x_rep(n + (indc+1:end)) = df.x_rep(indc+1:end);
99          df.x_data(indc + (1:n)) = NA;
100          df.x_rep(indc + (1:n)) = 1;
101        end
102      else
103        %# add new values after the last column
104        indc = min (size (df.x_data, 2), df.x_cnt(2));
105      end
106      if (~isa (coltype, 'cell')) coltype = {coltype}; end
107      if (isscalar (coltype) && n > 1)
108        coltype = repmat (coltype, 1, n);
109      end
110      for indi = (1:n)
111        switch coltype{indi}
112          case {'char'}
113            dummy = {repmat(NA, df.x_cnt(1), 1) };
114            dummy(:, 1) = '_';
115          case { 'double'}
116            dummy = repmat (NA, df.x_cnt(1), 1);
117          case {'logical'} %# there is no NA in logical type
118            dummy = repmat (false, df.x_cnt(1), 1);
119          otherwise
120            try
121              dummy = cast (repmat (NA, df.x_cnt(1), 1), coltype{indi});
122            catch
123              %# There was an issue -- transfer coltype to data
124              if (indc+indi > df.x_cnt(2))
125                dummy = {coltype{indi}}; coltype{indi} = 'char';
126                if (df.x_cnt(1) < 1)
127                  %# nothing defined yet -- pad with one line
128                  df.x_type{indc+indi} = coltype{indi};
129                  df = df_pad (df, 1, 1);
130                end
131              else
132                dummy = sprintf ('Trying to change type of column %d, which was %s, to %s', ...
133                                 indc+indi, df.x_type{indi}, coltype{indi});
134                error (dummy);
135              end
136            end
137        end
138        df.x_data{indc+indi} = dummy;
139        df.x_rep{indc+indi} = 1;
140        df.x_type{indc+indi} = coltype{indi};
141      end
142
143      if (size (df.x_data, 2) > df.x_cnt(2)),
144        df.x_cnt(2) =  size (df.x_data, 2);
145      end
146      if (length (df.x_name{2}) < df.x_cnt(2)),
147        %# generate a name for the new column(s)
148        dummy = cstrcat (repmat ('_', n, 1), ...
149                         strjust (num2str (indc + (1:n).'), 'left'));
150        df.x_name{2}(indc + (1:n)) = cellstr (dummy);
151        df.x_over{2}(1, indc + (1:n)) = true;
152      end
153
154    case 3
155      if (n <= 0) return; end
156      if (isempty (coltype)),
157        coltype = 1:df.x_cnt(2);
158      end
159      dummy = max (n+cellfun (@length, df.x_rep(coltype)));
160      if (size (df.x_ridx, 2) < dummy),
161        df.x_ridx(:, end+1:dummy) = NA;
162      end
163      for indi = (coltype)
164        switch df.x_type{indi}
165          case {'char'}
166            if (isa (df.x_data{indi}, 'char')) %# pure char
167              dummy = horzcat (df.x_data{indi}(:, df.x_rep{indi}), ...
168                               repmat({NA}, df.x_cnt(1), 1));
169              keyboard
170            else
171              dummy =  horzcat (df.x_data{indi}(:, df.x_rep{indi}), ...
172                                repmat({NA}, df.x_cnt(1), 1));
173            end
174          case {'double'}
175            dummy = horzcat (df.x_data{indi}(:, df.x_rep{indi}), ...
176                             repmat (NA, df.x_cnt(1), 1));
177          case {'logical'}
178            %# there is no logical 'NA' -- fill empty elems with false
179            dummy = horzcat (df.x_data{indi}(:, df.x_rep{indi}), ...
180                             repmat (false, df.x_cnt(1), 1));
181          otherwise
182            dummy = cast (horzcat (df.x_data{indi}(:, df.x_rep{indi}), ...
183                                   repmat (NA, df.x_cnt(1), 1)), ...
184                          df.x_type{indi});
185        end
186        df.x_data{indi} = dummy;
187        df.x_rep{indi} = [df.x_rep{indi} length(df.x_rep{indi})+ones(1, n)];
188        try
189          assert (size(df.x_data{indi}, 2), max(df.x_rep{indi}))
190        catch
191          keyboard
192        end
193      end
194      df =  df_thirddim (df);
195    otherwise
196      error ('Invalid dimension in df_pad');
197  end
198
199end
200