access cell array by two vector not pairwise - matlab

If I have a Cell array 2*2 where A{i,j} is a matrix, and I have two vectors v=1:2,c=1:2.
I want A(v,c) to return only A{1,1} and A{2,2} but matlab returns every combination of the two(aka also returns A{1,2} and A{2,1}).
Is there a way without using loops or cellfun ?

What I suspect you are doing is something like this:
B = A(v, c);
When you specify vectors to index into A, it finds the intersection of coordinates and gives you those elements. As such, with your indexing you are basically returning all of the elements in A.
If you want just the top left and lower right elements, use sub2ind instead. You can grab the column-major indices of those locations in your cell array, then slice into your cell array with these indices:
ind = sub2ind(size(A), v, c);
B = A(ind);
Example
Let's create a sample 2 x 2 cell array:
A = cell(2,2);
A{1,1} = ones(2);
A{1,2} = 2*ones(2);
A{2,1} = 3*ones(2);
A{2,2} = 4*ones(2);
Row 1, column 1 is a 2 x 2 matrix of all 1s. Row 1, column 2 is a 2 x 2 matrix of 2s, row 2 column 1 is a 2 x 2 matrix of all 3s and the last entry is a 2 x 2 matrix of all 4s.
With v = 1:2; c=1:2;, running the above code gives us:
>> celldisp(B)
B{1} =
1 1
1 1
B{2} =
4 4
4 4
As you can see, we picked out the top left and bottom right entries exactly.
Minor Note
If it's seriously just a cell array of 2 x 2, and you only want to pick out the top left and lower right elements, you can just do:
B = A([1 4]);
sub2ind would equivalently return 1 and 4 as the column major indices for the top left and lower right elements. This avoids the sub2ind call and still achieves what you want.

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

find row indices of different values in matrix

Having matrix A (n*2) as the source and B as a vector containing a subset of elements A, I'd like to find the row index of items.
A=[1 2;1 3; 4 5];
B=[1 5];
F=arrayfun(#(x)(find(B(x)==A)),1:numel(B),'UniformOutput',false)
gives the following outputs in a cell according to this help page
[2x1 double] [6]
indicating the indices of all occurrence in column-wise. But I'd like to have the indices of rows. i.e. I'd like to know that element 1 happens in row 1 and row 2 and element 5 happens just in row 3. If the indices were row-wise I could use ceil(F{x}/2) to have the desired output. Now with the variable number of rows, what's your suggested solution? As it may happens that there's no complete inclusion tag 'rows' in ismember function does not work. Besides, I'd like to know all indices of specified elements.
Thanks in advance for any help.
Approach 1
To convert F from its current linear-index form into row indices, use mod:
rows = cellfun(#(x) mod(x-1,size(A,1))+1, F, 'UniformOutput', false);
You can combine this with your code into a single line. Note also that you can directly use B as an input to arrayfun, and you avoid one stage of indexing:
rows = arrayfun(#(x) mod(find(x==A)-1,size(A,1))+1, B(:), 'UniformOutput', false);
How this works:
F as given by your code is a linear index in column-major form. This means the index runs down the first column of B, the begins at the top of the second column and runs down again, etc. So the row number can be obtained with just a modulo (mod) operation.
Approach 2
Using bsxfun and accumarray:
t = any(bsxfun(#eq, B(:), reshape(A, 1, size(A,1), size(A,2))), 3); %// occurrence pattern
[ii, jj] = find(t); %// ii indicates an element of B, and jj is row of A where it occurs
rows = accumarray(ii, jj, [], #(x) {x}); %// group results according to ii
How this works:
Assuming A and B as in your example, t is the 2x3 matrix
t =
1 1 0
0 0 1
The m-th row of t contains 1 at column n if the m-th element of B occurs at the n-th row of B. These values are converted into row and column form with find:
ii =
1
1
2
jj =
1
2
3
This means the first element of B ocurrs at rows 1 and 2 of A; and the second occurs at row 3 of B.
Lastly, the values of jj are grouped (with accumarray) according to their corresponding value of ii to generate the desired result.
One approach with bsxfun & accumarray -
%// Create a match of B's in A's with each column of matches representing the
%// rows in A where there is at least one match for each element in B
matches = squeeze(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Get the indices values and the corresponding IDs of B
[indices,B_id] = find(matches)
%// Or directly for performance:
%// [indices,B_id] = find(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Accumulate the indices values using B_id as subscripts
out = accumarray(B_id(:),indices(:),[],#(x) {x})
Sample run -
>> A
A =
1 2
1 3
4 5
>> B
B =
1 5
>> celldisp(out) %// To display the output, out
out{1} =
1
2
out{2} =
3
With arrayfun,ismember and find
[r,c] = arrayfun(#(x) find(ismember(A,x)) , B, 'uni',0);
Where r gives your desired results, you could also use the c variable to get the column of each number in B
Results for the sample input:
>> celldisp(r)
r{1} =
1
2
r{2} =
3
>> celldisp(c)
c{1} =
1
1
c{2} =
2

combine two n dimensional cell arrays into a n by n dimensional one

I wonder how to do this in MATLAB.
I have a={1;2;3} and would like to create a cell array
{{1,1};{1,2};{1,3};{2,1};{2,2};{2,3};{3,1};{3,2};{3,3}}.
How can I do this without a for loop?
You can use allcomb from MATLAB File-exchange to help you with this -
mat2cell(allcomb(a,a),ones(1,numel(a)^2),2)
Just for fun, using kron and repmat:
a = {1;2;3}
b = mat2cell([kron(cell2mat(a),ones(numel(a),1)) repmat(cell2mat(a),numel(a),1)])
Here square brackets [] are used to perform a concatenation of both column vectors, where each is defined either by kron or repmat.
This can be easily generalized, but I doubt this is the most efficient/fast solution.
Using repmat and mat2cell
A = {1;2;3};
T1 = repmat(A',[length(A) 1]);
T2 = repmat(A,[1 length(A)]);
C = mat2cell(cell2mat([T1(:),T2(:)]),ones(length(T1(:)),1),2);
You can use meshgrid to help create unique permutations of pairs of values in a by unrolling both matrix outputs of meshgrid such that they fit into a N x 2 matrix. Once you do this, you can determine the final result using mat2cell to create your 2D cell array. In other words:
a = {1;2;3};
[x,y] = meshgrid([a{:}], [a{:}]);
b = mat2cell([x(:) y(:)], ones(numel(a)*numel(a),1), 2);
b will contain your 2D cell array. To see what's going on at each step, this is what the output of the second line looks like. x and y are actually 2D matrices, but I'm going to unroll them and display what they both are in a matrix where I've concatenated both together:
>> disp([x(:) y(:)])
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
Concatenating both vectors together into a 2D matrix is important for the next line of code. This is a vital step in order to achieve what you want. After the second line of code, the goal will be to make each element of this concatenated matrix into an individual cell in a cell array, which is what mat2cell is doing in the end. By running this last line of code, then displaying the contents of b, this is what we get:
>> format compact;
>> celldisp(b)
b{1} =
1 1
b{2} =
1 2
b{3} =
1 3
b{4} =
2 1
b{5} =
2 2
b{6} =
2 3
b{7} =
3 1
b{8} =
3 2
b{9} =
3 3
b will be a 9 element cell array and within each cell is another cell array that is 1 x 2 which stores one row of the concatenated matrix as individual cells.

compare multiple matrices matlab

I have multiple matrices of the same size and want to compare them.
As a result I need a matrix which gives me the biggest of the 3 for every value.
I will clarify what i mean with an example:
I have 3 matrices with data of 3 persons.
I would like to compare these 3 and get a matrix as result.
In that matrix every cell/value should be the name of the matrix who had the highest value for that cell. So if in the 3 matrices the first value (1 colum, 1 row) is accordingly 2, 5, 8 the first value of the result matrix should be 3 (or the name of the 3 matrix).
If the three matrices are A, B, C, do this:
[~, M] = max(cat(3,A,B,C),[],3);
It creates a 3D "matrix" and maximizes across the third dimension.
Concatenate them on the 3rd dimension, and the use the SECOND output from max to get exactly what you want
A = rand(3,3);
B = rand(3,3);
C = rand(3,3);
D = cat(3, A, B, C)
[~, Solution] = max(D, [], 3)
e.g.:
D =
ans(:,:,1) =
0.70101 0.31706 0.83874
0.89421 0.33783 0.55681
0.68520 0.11697 0.45631
ans(:,:,2) =
0.268715 0.213200 0.124450
0.869847 0.999649 0.153353
0.345447 0.023523 0.338099
ans(:,:,3) =
0.216665 0.297900 0.604734
0.103340 0.767206 0.660668
0.127052 0.430861 0.021584
Solution =
1 1 1
1 2 3
1 3 1
edit
As I did not know about the second argument of the max-function, here is what you should NOT use:
old
Well, quick&dirty:
x=[2 5 8];
w=max(x)
[~,loc] = ismember(w,x)

Can someone explain this example of deleting elements from a matrix in MATLAB?

The following example appears in the MATLAB tutorial:
X = [16 2 13;
5 11 8;
9 7 12;
4 14 1]
Using a single subscript deletes a single element, or sequence of elements, and reshapes the remaining elements into a row vector. So:
X(2:2:10) = []
results in:
X = [16 9 2 7 13 12 1]
Mysteriously, the entire 2nd row and the first two elements in the 4th row have been deleted, but I can't see the correspondence between the position of the deleted elements and the index vector 2:2:10. Can someone please explain?
The example you gave shows linear indexing. When you have a multidimensional array and you give it a single scalar or vector, it indexes along each column from top to bottom and left to right. Here's an example of indexing into each dimension:
mat = [1 4 7; ...
2 5 8; ...
3 6 9];
submat = mat(1:2, 1:2);
submat will contain the top left corner of the matrix: [1 4; 2 5]. This is because the first 1:2 in the subindex accesses the first dimension (rows) and the second 1:2 accesses the second dimension (columns), extracting a 2-by-2 square. If you don't supply an index for each dimension, separated by commas, but instead just one index, MATLAB will index into the matrix as though it were one big column vector:
submat = mat(3, 3); % "Normal" indexing: extracts element "9"
submat = mat(9); % Linear indexing: also extracts element "9"
submat = mat([1 5 6]); % Extracts elements "1", "5", and "6"
See the MATLAB documentation for more detail.
It's very simple.
It basically starts from the second element in this example and goes upto tenth element (column wise) in steps of 2 and deletes corresponding elements. The remaining elements result in a row vector.