Find corresponding array in a cell - matlab

Suppose I have a cell of arrays of the same size, for example
arr = {[1 NaN 2 ], ...
[NaN 4 7 ], ...
[3 4 NaN] };
and I also have a vector, for example
vec = [1 2 2];
How do I find the corresponding cell entry that matches the vector vec. Matching means the entries in the same location are the same, except for NaNs?
For this particular vector vec I would like to have 1 returned, since it matches the first row.
Another vector [5 4 7] would return 2.
Vectors that don't match like [7 7 7] and vectors that match more than one entry like [3 4 7] should throw an error.
Note that the vector [3 7 4] does not match the second entry, because the order is important.

For each cell element, just check if
all(isnan(cellElement) | cellElement == vec)
is true, which means, you found a match. If you convert your cell to a matrix checkMatrix with multiple rows and each row corresponding to one cellElement, you can even do it without implementing a loop by repeating vec vertically and comparing the whole matrix in a single step. You will have to tell all() to check along dimension 2 rather than dimension 1 and have find() detect all the matches, like so:
find( all( ...
isnan(checkMatrix) | checkMatrix == repmat(vec,size(checkMatrix, 1),1) ...
, 2)); % all() along dimension 2

So I thought about it and came up with this:
matching_ind = #(x, arr) find(...
cellfun(#(y) max(abs(not(x-y==0).*not(isnan(x-y)))),...
arr) == 0);
inds = matching_ind(vec, arr);
if length(inds) ~= 1
error('42');
end

See if this bsxfun based approach works for you -
A = vertcat(arr{:});
matching_ind = find(all(bsxfun(#eq,A,vec(:).') | isnan(A),2)) %//'
if numel(matching_ind)~=1
error('Error ID : 42.')
else
out = matching_ind(1);
end

Related

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

access cell array by two vector not pairwise

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.

How to check if the beginning elements of array in matlab is the same

I would like to see if an array starts with the same elements as another array without having to write a bunch of for loops going through each element individually.
For example if I had the arrays below
Array1 = [1 2 3 4]
Array2 = [1 2 3 4 5 3 2 5 7]
Array3 = [1 2 3 5]
Then comparing Array1 with Array2 would return true.
and comparing Array3 with Array2 would return false.
Is there any quick and easy way of doing this. I would not know the lengths of the arrays I would be comparing. The number of elements I want to compare equals the length of the shortest vector.
Thanks!
You can check if all elements in two vectors are the same using isequal. To check only the first n elements, you can do Array(1:n), thus the entire function will be like this:
Array1 = [1 2 3 4]
Array2 = [1 2 3 4 5 3 2 5 7]
Array3 = [1 2 3 5]
n = 4; % Compare the first n elements
isequal(Array1(1:n), Array2(1:n))
ans = 1
isequal(Array2(1:n), Array3(1:n))
ans = 0
If you use Array1(1:n) == Array2(1:n) you will get a piece-wise comparison resulting in 1 1 1 1. Of course, this means you could also do:
all(Array1(1:n) == Array2(1:n))
ans = 1
all(Array2(1:n) == Array3(1:n))
ans = 0
If you want n to be the number of elements in the smallest vector (per your comment), as Chris and Ben interpret the question, you can solve it this way:
isequal(Array1(min([numel(Array1) numel(Array2)])), Array2(min([numel(Array1) numel(Array2)])))
or a bit cleaner:
n = min([numel(Array1) numel(Array2)])
isequal(Array1(1:n), Array2(1:n))
Here's a function that will compare the initial segments of any two vectors, up to the length of the shortest vector. It returns true if they are identical, and false if they are not identical.
Note that
It only works correctly with vectors, not with matrices (although you could extend it to deal with matrices)
If any entries are NaN then it will always return false, since NaN == NaN is false.
Here it is -
function result = equal_initial_segment(x, y)
N = min(length(x), length(y));
result = isequal(x(1:N), y(1:N));
end
It seems like you are just comparing the all of the elements of the shorter list to the first elements of the longer list, in which case you can just do this:
function same = compareLists(list1, list2)
if length(list1) > length(list2)
same = isequal(list2, list1(1:length(list2));
else if
same = isequal(list1, list2(1:length(list1));
end
end
You can use strmatch for that:
~(isempty(strmatch(Array1, Array2)) && isempty(strmatch(Array2, Array1)))

Searching a cell array of vectors and returning indices

I have a 3000x1 cell array of vectors of different lengths and am looking for a way to search them all for a number and return the cell indices for the first and last occurrence of that number.
So my data looks like this:
[1]
[1 2]
[1 2]
[3]
[6 7 8 9]
etc
And I want to my results to look like this when I search for the number 1:
ans = 1 3
All the indices (e.g. [1 2 3] for 1) would also work, though the above would be better. So far I'm unable to solve either problem.
I've tried
cellfun(#(x) x==1, positions, 'UniformOutput', 0)
This returns a logical array, effectively putting me back at square 1. I've tried using find(cellfun...) but this gives the error undefined function 'find' for input arguments of type 'cell'. Most of the help I can find is for searching for strings within a cell array. Do I need to convert all my vectors to strings for this to work?
C = {[1]
[1 2]
[1 2]
[3]
[6 7 8 9]}; %// example data
N = 1; %// sought number
ind = cellfun(#(v) any(v==N), C); %// gives 1 for cells which contain N
first = find(ind,1);
last = find(ind,1,'last');
result = [ first last ];

How can I insert each row of a matrix into cells in Matlab?

Suppose A = [1 2 3;4 5 6;7 8 9]
I want to convert it to B = [{[1,2,3]};{[4,5,6]};{[7,8,9]}]
How can I do that in an easy way?
You can use mat2cell function.
From the documentation:
C = mat2cell(A,dim1Dist,...,dimNDist) divides array A into smaller
arrays within cell array C. Vectors dim1Dist,...dimNDist specify how
to divide the rows, columns, and (when applicable) higher dimensions
of A.
You can do it like this:
A = [1 2 3; 4 5 6; 7 8 9];
B = mat2cell(A, [1 1 1], 3);
will give you:
B={[1 2 3];[4 5 6];[7 8 9]}
Documentation also says:
C = mat2cell(A,rowDist) divides array A into an n-by-1 cell array C,
where n == numel(rowDist).
So, if you are always going to split your matrix to rows, but not to columns, you can do it without the second parameter.
B = mat2cell(A, [1 1 1]);
A better, generalized way would be:
mat2cell(A, ones(1, size(A, 1)), size(A, 2));
You can't have a "matrix of cells" like your notation for B implied.
A cell array allows you to store "any data type" in the individual cells. You can't store a cell as a data type in an array.
So let's assume you meant to say you wanted B = {[1,2,3], [4,5,6], [7,8,9]};
If that is the case, then
B = cell(1,3);
for ii=1:3
B(ii) = {A(ii, :)};
end
should do the trick.
Note - edited based on Hadi's comment.