Reputation: 8747
I have a map defined as:
diagonal = eye(4);
v = {diagonal(1,:), diagonal(2,:), diagonal(3,:), diagonal(4,:)}
k = {1, 3, 7, 8}
class_labels = containers.Map(k, v)
Now I need a reverse map, but Matlab does not allow key to be array and hence I need to convert each array to string.
So my class_labels map look like:
1 => [0 0 0 1]
3 => [0 0 1 0]
7 => [0 1 0 0]
8 => [1 0 0 0]
I need something like:
0001 => 1
0010 => 3
0100 => 7
1000 => 8
Upvotes: 1
Views: 911
Reputation: 112749
The reverse map can be done using logical indexing:
allValues = [1 3 7 8];
value = allValues(logical(key));
For example,
>> allValues = [1 3 7 8];
>> key = [0; 0; 1; 0];
>> value = allValues(logical(key))
value =
7
If the key happens to contain several ones, the corresponding values are returned as a vector
>> key = [1; 0; 1; 0];
>> value = allValues(logical(key))
value =
1 7
Upvotes: 0
Reputation: 104545
You can use the keys
and values
methods associated with the containers.Map
class to extract the keys and values, then apply a string conversion to the values by concatenating all of the bits together.... then simply construct another containers.Map
. What you'll do is use cellfun
to iterate through each cell element of the values cell array and apply a function such that it converts the sequence of numbers in the array into a concatenated string.
Let's assume for the moment that you don't have access to the keys and values already defined by you and let's say we only have access to the containers.Map
itself. You want to invert the dictionary, and so:
%// Your code
diagonal = eye(4);
v = {diagonal(1,:), diagonal(2,:), diagonal(3,:), diagonal(4,:)};
k = {1, 3, 7, 8};
class_labels = containers.Map(k, v);
%// New - Get the keys and labels
kr = keys(class_labels);
vr = values(class_labels);
%// Concatenate all of the bits of the values into a string
vr = cellfun(@(x) char(48+x), vr, 'uni', 0);
%// Create new dictionary
new_labels = containers.Map(vr, kr);
This line here is probably the most confusing: vr = cellfun(@(x) char(48+x), vr, 'uni', 0);
. cellfun
iterates over all cells in a cell array and applies a function to each cell. This function is the first input into cellfun
. I declared an anonymous function where it takes in the contents of a cell in the cell array... so this would be the array of values, then adds 48 to each of the digits. This gives us an array of 48/49
instead of 0/1
. Once we do this, we cast this array to char
so that the digits are represented as their ASCII or string equivalents. The ASCII code for 0/1
is 48/49
. By using char
on this modified array, what is produced is a string that concatenates all of these characters together. The second input is the cell array we're working on, and the third and fourth parameters tell you that the output of cellfun
is not a numeric vector but another cell array of values. 'uni'
is short for 'UniformOutput
', and this is set to 0/false
because the output of this function is not a numeric vector, but a cell array of vectors. Each cell would be the string created by concatenating all of the numbers in the numeric array together.
If we show the keys and values, we get:
>> keys(new_labels)
ans =
'0001' '0010' '0100' '1000'
>> values(new_labels)
ans =
[8] [7] [3] [1]
You can see that each string key maps to the right inverse value.
Upvotes: 1
Reputation: 2236
The problem is to map your arrays to strings or doubles, and possibly
back again. From the constraints you've given, you can use find
to
"encode" the vectors; decoding is not achievable in a one-liner (I
don't think), but something like
function idx2vec(i,n)
% I - index to set to 1
% N - length of vector
v = zeros([1,n]);
v(i) = 1;
end
You could use this technique to get a single argument key->array converter:
function f= fidx2vec(n)
function v=idx2vec(i)
v = zeros([1,n]);
v(i) = 1;
end
f = @idx2vec;
end
For general arrays, you could use mat2str
(array to key) and eval
(key to array) instead.
For readability, I wrapped find in vec2idx:
function i=vec2idx(v)
i = find(v);
end
and added this to your code:
diagonal = eye(4);
v = {diagonal(1,:), diagonal(2,:), diagonal(3,:), diagonal(4,:)};
k = {1, 3, 7, 8};
class_labels = containers.Map(k, v);
rk = cellfun(@vec2idx, v, 'uniformoutput', false);
reverse_map = containers.Map(rk, k);
for iv = 1:length(v)
fprintf('%s -> %g\n',mat2str(v{iv}),reverse_map(vec2idx(v{iv})));
end
to produce:
[1 0 0 0] -> 1
[0 1 0 0] -> 3
[0 0 1 0] -> 7
[0 0 0 1] -> 8
A final comment: this solution maps your arrays to the positive
integers. Matlab has a perfectly good container mapping positive
integers to doubles, called arrays! If your keys are dense (i.e., all
or most values from 1:n will be used), I'd just use an array instead
of a containers.Map. You could use nan
to mark non-existent
entries. If your keys are sparse, that is your arrays are length
(say) 1000, but only 50 possibilities are used, containers.Map is a
reasonable option.
Upvotes: 0