time | Calls | line |
---|
| | 1 | function t = horzcat(varargin)
|
| | 2 | %HORZCAT Horizontal concatenation for tables.
|
| | 3 | % T = HORZCAT(T1, T2, ...) horizontally concatenates the tables T1, T2,
|
| | 4 | % ... . All inputs must have unique variable names.
|
| | 5 | %
|
| | 6 | % Row names for all tables that have them must be identical except for order.
|
| | 7 | % HORZCAT concatenates by matching row names when present, or by position for
|
| | 8 | % tables that do not have row names. HORZCAT assigns values for the
|
| | 9 | % Description and UserData properties in T using the first non-empty value
|
| | 10 | % for the corresponding property in the arrays T1, T2, ... .
|
| | 11 | %
|
| | 12 | % See also CAT, VERTCAT, JOIN.
|
| | 13 |
|
| | 14 | % Copyright 2012-2020 The MathWorks, Inc.
|
| | 15 |
|
| | 16 | import matlab.internal.datatypes.istabular
|
| | 17 | import matlab.internal.datatypes.defaultarrayLike
|
| | 18 |
|
< 0.001 | 2 | 19 | try
|
< 0.001 | 2 | 20 | haveCell = false;
|
< 0.001 | 2 | 21 | inputWidths = zeros(1,nargin);
|
< 0.001 | 2 | 22 | isIdentityElement = false(1,length(varargin));
|
< 0.001 | 2 | 23 | t = [];
|
| 2 | 24 | t_is0x0 = true;
|
< 0.001 | 2 | 25 | t_idx = 1; % Index of the input tabular from which template is generated
|
< 0.001 | 2 | 26 | for i = 1:nargin
|
< 0.001 | 4 | 27 | b = varargin{i};
|
< 0.001 | 4 | 28 | haveCell = haveCell || iscell(b); % _any_ input is a cell array
|
0.002 | 4 | 29 | inputWidths(i) = size(varargin{i},2); % dispatch to overloaded size, not built-in
|
< 0.001 | 4 | 30 | isIdentityElement(i) = (isnumeric(b) && isequal(b,[])); % treat as "identity element", and ignore
|
0.002 | 4 | 31 | [t, t_is0x0, updateIdx] = getTemplate(t, b, t_is0x0);
|
< 0.001 | 4 | 32 | if updateIdx
|
| | 33 | % Update the template index as the template was updated by the
|
| | 34 | % current input
|
< 0.001 | 2 | 35 | t_idx = i;
|
< 0.001 | 4 | 36 | end
|
< 0.001 | 4 | 37 | end
|
< 0.001 | 2 | 38 | nvarsTotal = sum(inputWidths);
|
< 0.001 | 2 | 39 | varargin(isIdentityElement) = []; % remove []'s
|
< 0.001 | 2 | 40 | inputWidths(isIdentityElement) = [];
|
| | 41 |
|
< 0.001 | 2 | 42 | if ~isa(varargin{1},'timetable') && isa(t,'timetable')
|
| | 43 | % Tables and cell arrays can be concatenated onto a timetable, but not
|
| | 44 | % vice-versa. Even a leading 0x0 table cannot be followed by a timetable.
|
| | 45 | throwTableAndTimetableError(iscell(varargin{1}));
|
< 0.001 | 2 | 46 | end
|
| | 47 |
|
| | 48 | % Initialize output properites using the template
|
| | 49 |
|
< 0.001 | 2 | 50 | t_nrows = height(t);
|
< 0.001 | 2 | 51 | t_nvars = 0;
|
< 0.001 | 2 | 52 | t_rowDim = t.rowDim;
|
0.003 | 2 | 53 | t_varDim = t.varDim.createLike(nvarsTotal);
|
| | 54 | % We assume that the metaDim labels are default and update them as soon as
|
| | 55 | % we encounter a tabular with non-default dim names.
|
< 0.001 | 2 | 56 | haveDefaultDimNames = true;
|
< 0.001 | 2 | 57 | t_metaDim = t.metaDim;
|
< 0.001 | 2 | 58 | t_arrayProps = t.arrayPropsDflts; % initialize with default arrayprops
|
< 0.001 | 2 | 59 | t_customVarProps = struct;
|
< 0.001 | 2 | 60 | if t_rowDim.hasLabels
|
< 0.001 | 1 | 61 | [t_rowLabelsSorted,t_rowOrder] = sort(t_rowDim.labels);
|
< 0.001 | 2 | 62 | end
|
< 0.001 | 2 | 63 | t_data = cell(1,0);
|
| | 64 |
|
< 0.001 | 2 | 65 | for j = 1:length(varargin)
|
< 0.001 | 4 | 66 | b = varargin{j};
|
< 0.001 | 4 | 67 | b_wasCell = iscell(b); % the current input is a cell array
|
< 0.001 | 4 | 68 | if b_wasCell
|
| | 69 | b = cell2table(b);
|
< 0.001 | 4 | 70 | end
|
< 0.001 | 4 | 71 | b_nrows = b.rowDim.length;
|
< 0.001 | 4 | 72 | b_nvars = b.varDim.length;
|
< 0.001 | 4 | 73 | vars_j = t_nvars+(1:size(b,2)); % var indices in t that b will go into
|
| | 74 |
|
< 0.001 | 4 | 75 | if b_nvars==0 && b_nrows==0 % special case to mimic built-in behavior
|
| | 76 | % do nothing with data, but need to manage metadata.
|
< 0.001 | 4 | 77 | elseif t_nrows ~= b_nrows
|
| | 78 | if haveCell
|
| | 79 | error(message('MATLAB:table:horzcat:SizeMismatchWithCell'));
|
| | 80 | else
|
| | 81 | error(message('MATLAB:table:horzcat:SizeMismatch'));
|
| | 82 | end
|
< 0.001 | 4 | 83 | else
|
| | 84 | % It's a non-0x0 input with the right size and type. Append it to
|
| | 85 | % the right edge of t.
|
< 0.001 | 4 | 86 | b_rowDim = b.rowDim;
|
< 0.001 | 4 | 87 | if isa(t,'timetable') && isa(b,'table')
|
| | 88 | t_data = horzcat(t_data, b.data); %#ok<AGROW>
|
< 0.001 | 4 | 89 | elseif t_rowDim.hasLabels && b_rowDim.hasLabels
|
< 0.001 | 1 | 90 | if j ~= t_idx
|
| | 91 | [b_rowLabelsSorted,b_rowOrder] = sort(b_rowDim.labels);
|
| | 92 | if (j ~= t_idx) && ~isequal(t_rowLabelsSorted,b_rowLabelsSorted)
|
| | 93 | % Check the row labels for tables other than the table from
|
| | 94 | % which the template was obtained.
|
| | 95 | if isa(t,'timetable')
|
| | 96 | error(message('MATLAB:table:horzcat:UnequalRowTimes'));
|
| | 97 | else
|
| | 98 | error(message('MATLAB:table:horzcat:UnequalRowNames'));
|
| | 99 | end
|
| | 100 | end
|
| | 101 | b_reord(t_rowOrder) = b_rowOrder; %#ok<AGROW>, full reassignment each time
|
| | 102 | t_data = horzcat(t_data, cell(1,b_nvars)); %#ok<AGROW>
|
| | 103 | for i = 1:b_nvars
|
| | 104 | bVar = b.data{i};
|
| | 105 | sizeOut = size(bVar);
|
| | 106 | if istabular(bVar)
|
| | 107 | t_data{t_nvars+i} = bVar.subsrefParens({b_reord,':'});
|
| | 108 | else
|
| | 109 | t_data{t_nvars+i} = reshape(bVar(b_reord,:),sizeOut);
|
| | 110 | end
|
| | 111 | end
|
| 1 | 112 | else
|
< 0.001 | 1 | 113 | t_data = horzcat(t_data, b.data); %#ok<AGROW>
|
< 0.001 | 1 | 114 | end
|
< 0.001 | 3 | 115 | else
|
< 0.001 | 3 | 116 | if b_rowDim.hasLabels % && ~t_rowDim.hasLabels
|
| | 117 | t_rowDim = b.rowDim;
|
| | 118 | [t_rowLabelsSorted,t_rowOrder] = sort(t_rowDim.labels);
|
| 3 | 119 | end
|
< 0.001 | 3 | 120 | t_data = horzcat(t_data, b.data); %#ok<AGROW>
|
< 0.001 | 4 | 121 | end
|
| | 122 |
|
| | 123 | % Make it official.
|
< 0.001 | 4 | 124 | t_nvars = t_nvars + b_nvars;
|
< 0.001 | 4 | 125 | end
|
| | 126 |
|
| | 127 | % If it was originally a cell array, there are no row labels or other
|
| | 128 | % properties to worry about.
|
< 0.001 | 4 | 129 | if ~b_wasCell
|
| | 130 | % Build up new customProps struct, initially with a cell array in
|
| | 131 | % each field that will be concatenated together.
|
< 0.001 | 4 | 132 | b_customProps = b.varDim.customProps;
|
< 0.001 | 4 | 133 | fn = fieldnames(b_customProps);
|
< 0.001 | 4 | 134 | for ii = 1:numel(fn)
|
| | 135 | if ~isfield(t_customVarProps,fn{ii}) % instantiate new customProp
|
| | 136 | t_customVarProps.(fn{ii}) = {};
|
| | 137 | end
|
| | 138 | if ~isequal(size(b_customProps.(fn{ii})),[0,0])
|
| | 139 | % fill in with default values if not yet done and there is an archetype to use.
|
| | 140 | if isequal(size(t_customVarProps.(fn{ii})),[0,0])
|
| | 141 | t_customVarProps.(fn{ii}) = cell(1,nargin);
|
| | 142 | dfltVarProp = defaultarrayLike([1,1],'Like',b_customProps.(fn{ii}), false);
|
| | 143 | for k = 1:nargin
|
| | 144 | t_customVarProps.(fn{ii}){k} = repmat(dfltVarProp,1,inputWidths(k));
|
| | 145 | end
|
| | 146 | end
|
| | 147 | t_customVarProps.(fn{ii}){j} = b_customProps.(fn{ii});
|
| | 148 | end
|
| | 149 | end
|
| | 150 | % Clear out the customProps from the varDim to avoid duplicating work in assignInto.
|
0.001 | 4 | 151 | b.varDim = b.varDim.setCustomProps(struct);
|
| | 152 |
|
| | 153 | % If it was originally a table/timetable, get its var labels and
|
| | 154 | % per-var properties.
|
0.005 | 4 | 155 | t_varDim = t_varDim.assignInto(b.varDim,vars_j);
|
| | 156 |
|
| | 157 | % Update the metaDim labels if this is the first time we are seeing
|
| | 158 | % a non-default dimension name
|
< 0.001 | 4 | 159 | if haveDefaultDimNames && ~isequal(b.metaDim.labels,b.defaultDimNames)
|
< 0.001 | 1 | 160 | t_metaDim = t_metaDim.setLabels(b.metaDim.labels);
|
< 0.001 | 1 | 161 | haveDefaultDimNames = false;
|
< 0.001 | 4 | 162 | end
|
| | 163 |
|
| | 164 | % Use any per-array property values not already present.
|
0.002 | 4 | 165 | t_arrayProps = tabular.mergeArrayProps(t_arrayProps,b.arrayProps);
|
< 0.001 | 4 | 166 | end
|
< 0.001 | 4 | 167 | end
|
< 0.001 | 2 | 168 | t.data = t_data;
|
| | 169 |
|
| | 170 | % Horzcat the per-variable customProps.
|
< 0.001 | 2 | 171 | fn = fieldnames(t_customVarProps);
|
< 0.001 | 2 | 172 | if ~isempty(fn)
|
| | 173 | for ii = 1:numel(fn)
|
| | 174 | try
|
| | 175 | t_customVarProps.(fn{ii}) = [t_customVarProps.(fn{ii}){:}];
|
| | 176 | nPropVals = numel(t_customVarProps.(fn{ii}));
|
| | 177 | if ~(nPropVals == nvarsTotal || nPropVals == 0)
|
| | 178 | error(message('MATLAB:table:CustomProperties:CellCatChangesSize'))
|
| | 179 | end
|
| | 180 | catch ME
|
| | 181 | throw(addCause(MException(message('MATLAB:table:CustomProperties:InvalidConcatenation',fn{ii})),ME))
|
| | 182 | end
|
| | 183 | end
|
< 0.001 | 2 | 184 | end
|
| | 185 | % Check for conflicts between per-var and per-table
|
| | 186 | % CustomProperties across tables.
|
< 0.001 | 2 | 187 | if any(isfield(t_customVarProps, fieldnames(t_arrayProps.TableCustomProperties)))
|
| | 188 | error(message('MATLAB:table:horzcat:CustomPropsClash'))
|
< 0.001 | 2 | 189 | end
|
< 0.001 | 2 | 190 | t_varDim = t_varDim.setCustomProps(t_customVarProps);
|
| | 191 |
|
| | 192 | % Error if var labels are duplicates, and create default labels where labels are
|
| | 193 | % not present from cell array inputs. Don't try to recognize and take one copy
|
| | 194 | % of variables that have the same name and data, duplicate variable names are an
|
| | 195 | % error regardless.
|
0.007 | 2 | 196 | t.varDim = t_varDim.setLabels(t_varDim.labels,[],false,true);
|
| | 197 |
|
| | 198 | % Detect conflicts between the combined var names of the result and the dim
|
| | 199 | % names of the leading time/table.
|
< 0.001 | 2 | 200 | t.metaDim = t_metaDim.checkAgainstVarLabels(t.varDim.labels);
|
| | 201 |
|
< 0.001 | 2 | 202 | t.rowDim = t_rowDim;
|
0.001 | 2 | 203 | t.arrayProps = t_arrayProps;
|
| | 204 | catch ME
|
| | 205 | throwAsCaller(ME)
|
< 0.001 | 2 | 206 | end
|
Other subfunctions in this file are not included in this listing.