find returns empty matrix - matlab

I have two vectors which are of length 200,000 approximately. They consist of dates in the datenum format.
%datenums
date_exp = datenum(data_exp(:,1:6));
date_sim = datenum(data_sim(:,1:6));
I want to find the dates in date_exp that exists in date_sim.
Then remove the values from date_exp
I have used the the ismember tool but ending up at i=38 find retunrs: Improper assignment with rectangular empty matrix.
Error in filter (line 18)
c(i)= find(ismember(date_sim(:),date_exp(i)),1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
c = zeros(length(date_sim),1);
for i=1:length(date_sim)
c(i)= find(ismember(date_sim(:),date_exp(i)),1);
if isempty(c(i)) == 1
c(i) = 0;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
I would be really helpful if anyone could help me out here.

The issue is because date_exp(38) must not be within date_sim. When there are no 1's in the input, find returns an empty array ([]).
Your code does not handle this as you would expect though because of this line.
c(i) = find(...)
In this case, if there are no matches (find() == []), then you are essentially calling
c(i) = [];
This deletes the ith element of c element
Therefore the following line is never true!
if isempty(c(i)) == 1
Instead, you should probably do something to handle the empty value.
index = find(ismember(date_sim(:), date_exp(i)), 1);
%// Only assign the index if it isn't an empty array
if ~isempty(index)
c(i) = index;
end
You don't have to worry about assigning zeros because your initial matrix is already full of zeros.
A Better Option
All of that aside, it would likely be a much better approach to not loop at all and to instead use the second output of ismember (on the arrays before you pass them to datenum) to give you the same result in a much more efficient way.
[~, c] = ismember(date_exp(:,1:6), date_sim(:,1:6), 'rows');

Related

Matlab find function throws size difference error when there is no apparent one

My matlab code is attempting to find the indices in a 601 by 1 matrix that correspond to a given value but says the left and right sides have a different number of elements
pH_fine = pH(1):0.01:pH(end);
pH_labvals = [7.72,9.87,7.4,7.63,7.06,6.85,8.29,9.37,11.1];
index_labvals = [];
a = find(pH_fine == 8); %This works perfectly
for i = 1:length(pH_labvals)
index_labvals(i) = find(pH_fine == pH_labvals(i)); %This throws an error
end
Your problem is that the find(pH_fine == pH_labvals(i)) on the right side sometimes doesn't find any match, and returns an empty result for an index, specifically a 1-by-0 row vector. This doesn't match the size of the left side, which is indexing a 1-by-1 element from your vector index_labvals.
You need to check first if the result of find is empty, and decide what you will put in the index vector in that case, like a 0 or NaN. You will also need to deal with find giving you a vector of indices if pH_labvals has the same value repeated. If you simply want to remove repeated values, you could use unique like so:
pH_labvals = unique(pH_labvals, 'stable');
If you're wondering why you're getting an empty result from find, you should read through this post about the perils of floating-point comparison. One possible solution, assuming pH_labvals contains non-repeated values with 2 decimal places of precision, is to first round your pH_fine vector to 2 decimal places:
pH_fine = round(pH(1):0.01:pH(end), 2);
This should allow you to avoid the errors from floating-point comparison.
An alternative approach is to use interp1 for table lookup:
pH = [1,14]; % Not sure what values you use here, it doesn't matter for the example.
pH_fine = pH(1):0.01:pH(end);
pH_labvals = [7.72,9.87,7.4,7.63,7.06,6.85,8.29,9.37,11.1];
index_labvals = interp1(pH_fine,1:numel(pH_fine),pH_labvals,'nearest')
Here, we're finding the nearest index within pH_fine that matches each of the values in pH_labvals. 1:numel(pH_fine) are the indices into pH_fine.
Note that there's no need for a loop, as interp1 will lookup all pH_labvals at once.

Matrix as input and output of Matlab function

For example I want to have a function which let me delete rows of my matrix where the highest value is 1. Thus I wrote:
% A is an input matrix
% pict2 suppose to be output cleared matrix
function pict2 = clear_(A)
B=A
n = size(A,1)
for i=1:n
if(max(B(i,:))==1)
B(i,:)=[]
end
end
But after I call:
pict2=clear_(pict) the Matlab resposes:
"warning: clear_: some elements in list of return values are undefined
warning: called from
clear_ at line 5 column 1 pict2 = "
I'm not sure which elements were left undefined?
The variable name of your output argument must match the variable that you want to be returned. So you'll want to change the first line to the following so that your modifications to B are saved and returned.
function B = clear_(A)
As far as your algorithm is concerned, it's not going to work because you are modifying B while trying to loop through it. Instead, you can replace your entire function with the following expression which computes the maximum value of each row, then determines if this value is equal to 1 and removes the rows where this is the case.
B(max(B, [], 2) == 1, :) == [];
I believe that, alternatively to the suggestions you already recieved, you might want to try the following. Using logicals is probably one of the best options for such a problem, since you don't need to use for-loops:
function out = clear_matr(A)
% ind is true for all the rows of A, where the highest value is not equal to 1
ind = ~(max(A, [], 2) == 1);
% filter A accordingly
out = A(ind, :);
end

How to check for identical Vectors concurrently?

Trying to check for identical vectors. Currently using the isequal function to check for identical ones.
It runs like this.
if isequal (vectorA, vectorB) == 0
It will then run an instruction.
end
if isequal (vectorA, vectorB) == 1
It will run another instruction.
end
I now have a set of vectors more from A to F. Is there anyway to check all of them (B,C,D,F) against Vector A and do the same thing as mentioned?
Meaning
if vectorA matches any of B,C,D,F == 0
The same with the second case where
if vectorA matches any of B,C,D,F == 1
The vectors are constantly changing and this is running in a loop to check for identical vectors. Seems like the isequal function only works between 2 Vectors.
all the vectors are 1xi where i can be a number up to 50
ANY help in this would be greatly apreciated!
I suggest you keep your other vectors in a matrix rather so something like:
a = vectorA;
B = [vectorB, vectorC, vectorD, vectorE];
Then just use a simple for loop:
flag = false;
for k = 1:size(B,2)
flag = flag || isequal(a,B(:,k));
if flag
break
end;
end
or if you prefer a completely vecotrized one-liner over the loop (but in this case, I suspect the early exit clause in the loop might actually provide a performance benefit, depends on your data though):
flag = any(all(bsxfun(#eq,a,B)))
then
if flag
%// do stuff if any was equal
else
%// Do stuff if none were equal to a
BTW if you wanted to check if they ALL match instead of if ANY match then change to:
flag = all(all(bsxfun(#eq,a,B)))
or in the loop change to
flag = true;
for k = 1:size(B,2)
flag = flag && isequal(a,B(:,k));
if ~flag
break
end;
end
This can be done very easily with ismember:
To check whether it a vector matches any of the rows:
v = 1:3
M = [1:3;2:4;3:5]
ismember(v,M,'rows')
To check whether it matches all of the rows, you can extend it by also checking something like:
size(unique(M,'rows'),1)==1
This assumes that the vectors are stacked below each other, but of course it is easily adjusted to match the situation when they are next to each other.
To check if A equals any of B, C, D, F (with possibly different sizes), use cellfun on a cell array containing your vectors:
if any(cellfun(#(x) isequal(A,x), {B,C,D,F}))
If all vectors are row vectors with an equal number columns, it may be faster to use bsxfun on a matrix containing your vectors:
if any(all(bsxfun(#eq, A, [B; C; D; F]).'))

remove cells that satisfy a specific condition

I have a cell array which each cell is an n-by-n matrix. I want to delete the cells for which inv(cell{i}'*cell{i}) gives warning that matrix is close to singular.
Thank you
In general, removing elements is the easy part. If C is your array, removing cells specified by the indices in vector idx can be done by:
C(idx) = {};
Regarding your specific problem, to check if a matrix is "almost" singular or not can be done with rcond (if the result is close to zero, it's probably singular). To apply it to all cells you can use cellfun in the following way:
idx = cellfun(#(x)(rcond(x' * x) < 1e-12), C);
Adjust the threshold value to your liking. The resulting idx is a logical array with 1s at the location of singular matrices. Use idx to remove these elements from C as shown above.
Create a function that check for your condition:
function state = CheckElement(element)
if ([condition])
state = 1;
else
state = 0;
end
end
then do cellfun on all you cell array elements like the following:
indices = cellfun(#CheckElement,myCellArray);
cellArray(indices ) = [];
Assuming you have already defined a issingular function defined you can use cellfun to get the indices of the cells that contain the matrices you want to remove.
c; % cell arry
singularIdx = cellfun((#x) issingular( inv(x' * x)), c); %' added single quote for SO syntax
cFiltered = c(~singluarIdx);
You'll probably need to write your own function to check for singularity, for more information on that see this question: Fast method to check if a Matrix is singular? (non-invertible, det = 0)

How to change row number in a FOR loop... (MATLAB newbie)

I have a set of data that is <106x25 double> but this is inside a struct and I want to extract the data into a matrix. I figured a simple FOR loop would accomplish this but I have hit a road block quite quickly in my MATLAB knowledge.
This is the only piece of code I have, but I just don't know enough about MATLAB to get this simple bit of code working:
>> x = zeros(106,25); for i = 1:106, x(i,:) = [s(i).surveydata]; end
??? Subscripted assignment dimension mismatch.
's' is a very very large file (in excess of 800MB), it is a <1 x 106 struct>. Suffice it to say, I just need to access a small portion of this which is s.surveydata where most rows are a <1 x 25 double> (a row vector IIRC) and some of them are empty and solely return a [].
s.surveydata obviously returns the results for all of the surveydata contained where s(106).surveydata would return the result for the last row. I therefore need to grab s(1:106).surveydata and put it into a matrix x. Is creating the matrix first by using x = zeros(106,25) incorrect in this situation?
Cheers and thanks for your time!
Ryan
The easiest, cleanest, and fastest way to write all the survey data into an array is to directly catenate it, using CAT:
x = cat(1,s.surveydata);
EDIT: note that if any surveydata is empty, x will have fewer rows than s has elements. If you need x to have the same amount of rows as s has elements, you can do the following:
%# find which entries in s have data
%# note that for the x above, hasData(k) contains the
%# element number in s that the k-th row of x came from
hasData = find(arrayfun(#(x)~isempty(x.surveydata),s));
%# initialize x to NaN, so as to not confuse the
%# real data with missing data entries. The call
%# to hasData when indexing makes this robust to an
%# empty first entry in s
x = NaN(length(s),length(s(hasData(1)).surveydata);
%# fill in only the rows of x that contain data
x(hasData,:) = cat(1,s(hasData).surveydata);
No, creating an array of zeroes is not incorrect. In fact it's a good idea. You don't have to declare variables in Matlab before using them, but for loops, pre-allocating has speed benefits.
x = zeros(size(s), size(s(1)));
for i = 1:106
if ~isempty(s(i).surveydata)
x(i, :) = s(i).surveydata;
end
end
Should accomplish what you want.
EDIT: Since OP indicated that some rows are empty, I accounted for that like he said.
what about this?
what s is?
if s(i).surveydata is scalar:
x = zeros(106,25);
for i = 1:106
x(i,1) = [s(i).surveydata];
end
I am guessing that is what you want tough it is not clear at all :
if s(i).surveydata is row vector:
x = zeros(106,25);
for i = 1:106
x(i,:) = [s(i).surveydata];
end
if s(i).surveydata is column vector:
x = zeros(106,25);
for i = 1:106
x(i,:) = [s(i).surveydata]';
end