function [map_x2y, map_y2x] = cosmo_align(x, y)
% find permutation so that values in two inputs are matched
%
% [map_x2y, map_y2x]=cosmo_align(x,y)
%
% Inputs:
% x } both x and y must be a cell with K elements, each
% y } being a vector or a cellstring with the same number
% of elements (say N). Alternatively it can
% be a single vector or cellstrings v, which is
% interpreted as {v}.
%
% Outputs:
% map_x2y a vector with N elements, so that for all values I in
% 1:K it holds that map_x2y(x{I}) is equal to y{I}.
% map_y2x a vector with N elements, so that for all values I in
% 1:K it holds that map_y2x(y{I}) is equal to x{I}.
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
[x_cell, x_label] = as_cell(x, 1);
[y_cell, y_label] = as_cell(y, 2);
ensure_matching_labels(x_label, y_label);
nx = numel(x_cell);
ny = numel(y_cell);
if nx ~= ny
error(['number of elements in each cell is not equal; first '...
'input has %d elements, second input has %d'], ...
nx, ny);
end
[xi, xv] = cosmo_index_unique(x_cell);
[yi, yv] = cosmo_index_unique(y_cell);
if ~cosmo_isequaln(xv, yv)
error(['the two inputs do not have the same unique '...
'combination of elements']);
end
nxi = cellfun(@numel, xi);
nyi = cellfun(@numel, yi);
if ~isequal(nxi, nyi)
error(['the two inputs do have matching elements '...
'but their number of occurrences is not matched']);
elseif any(cellfun(@numel, xi) > 1)
error('combinations of elements are non-unique');
end
xv = cat(1, xi{:});
yv = cat(1, yi{:});
n = numel(xi);
map_x2y = zeros(1, n);
map_x2y(yv) = xv;
map_y2x = zeros(1, n);
map_y2x(xv) = yv;
function [v_cell, labels] = as_cell(v, pos)
if iscell(v) && ~iscellstr(v)
n = numel(v);
for j = 1:n
ensure_vector(v{j}, pos, j);
end
v_cell = v;
labels = [];
elseif isstruct(v)
labels = sort(fieldnames(v));
n = numel(labels);
v_cell = cell(n, 1);
for k = 1:n
label = labels{k};
v_value = v.(label);
ensure_vector(v_value, pos, label);
v_cell{k} = v_value;
end
else
ensure_vector(v, pos, 1);
v_cell = {v(:)};
labels = [];
end
function ensure_vector(v, pos, i)
if ~isvector(v)
msg = 'only input with vectors is supported';
elseif ~(isnumeric(v) || iscellstr(v))
msg = ['only inputs with numeric vectors or cellstrings '...
'are supported'];
else
% all fine
return
end
if ischar(i)
elem_str = ['field ' i];
else
elem_str = sprintf('element %d', i);
end
% throw error
error('input %d, %s: %s', pos, elem_str, msg);
function ensure_matching_labels(x_label, y_label)
if ~isequal(x_label, y_label)
if iscellstr(x_label) && iscellstr(y_label)
error(['field name mismatch between two inputs: '...
'[%s] ~= [%s]'], ...
cosmo_strjoin(x_label), cosmo_strjoin(y_label));
else
error(['either both inputs must be cells or vectors, ', ...
'or both must be structs']);
end
end