Selecting simultaneously not NaN values in multiple matrices - matlab

I have three matlab matrices A, B, and C with the same size:
A = [1:3; 4:6; 7:9];
B = [2 NaN 5; NaN NaN 7; 0 1 NaN];
C = [3 NaN 2; 1 NaN NaN; 1 NaN 5];
%>> A = %>>B = %>>C =
% 1 2 3 % 2 NaN 5 % 3 NaN 2
% 4 5 6 % NaN NaN 7 % 1 NaN NaN
% 7 8 9 % 0 1 NaN % 1 NaN 5
I would like the three matrices to keep only values for which each of the 3 matrices does not have a NaN in that specific position. That is, I would like to obtain the following:
%>> A = %>>B = %>>C =
% 1 NaN 3 % 2 NaN 5 % 3 NaN 2
% NaN NaN NaN % NaN NaN NaN % NaN NaN NaN
% 7 NaN NaN % 0 NaN NaN % 1 NaN NaN
In my attempt, I'm stacking the three matrices along the third dimension of a new matrix ABC with size 3x3x3 and then I'm using a for loop to make sure all the three matrices do not have NaN in that specific position.
ABC(:,:,1)=A; ABC(:,:,2)=B; ABC(:,:,3)=C;
for i=1:size(A,1)
for j=1:size(A,2)
count = squeeze(ABC(i,j,:));
if sum(~isnan(count))<size(ABC,3)
A(i,j)=NaN;
B(i,j)=NaN;
C(i,j)=NaN;
end
end
end
This code works fine. However, as I have more than 30 matrices of bigger size I was wondering whether there is a more elegant solution to this problem.
Thank you for your help.

Lets do fancy indexing!
First, the solution:
indnan=sum(isnan(cat(3,A,B,C)),3)>0;
A(indnan)=NaN;
B(indnan)=NaN;
C(indnan)=NaN;
What this code does is essentially creates a 3D matrix, and computes how many NaN there are in each (i,j,:) arrays. Then, if there are more than 0 (i.e.any of them is NaN) it gets a logical index for it. Finally, we fill up all those with NaN, leaving only the non-NaN alive.

Ander’s answer is good, but for very large matrices it might be expensive to create that 3D matrix.
First of all, I would suggest putting the matrices into a cell array. That makes it a lot easier to programmatically manage many arrays. That is, instead of A, B, etc, work with C{1}, C{2}, etc:
C = {A,B,C};
It takes essentially zero cost to make this change.
Now, to find all elements where one of the matrices is NaN:
M = isnan(C{1});
for ii=2:numel(C)
M = M | isnan(C{ii});
end
A similar loop then sets the corresponding elements to NaN:
for ii=1:numel(C)
C{ii}(M) = NaN,
end
This latter loop can be replaced by a call to cellfun, but I like explicit loops.
EDIT: Here are some timings. This is yet another example of loops being faster in modern MATLAB than the equivalent vectorized code. Back in the old days, the loop code would have been 100x slower.
This is the test code:
function so(sz) % input argument is the size of the arrays
C3 = cell(1,3);
for ii=1:numel(C3)
C3{ii} = create(sz,0.05);
end
C20 = cell(1,20);
for ii=1:numel(C20)
C20{ii} = create(sz,0.01);
end
if(~isequal(method1(C3),method2(C3))), error('not equal!'), end
if(~isequal(method1(C20),method2(C20))), error('not equal!'), end
fprintf('method 1, 3 arrays: %f s\n',timeit(#()method1(C3)))
fprintf('method 2, 3 arrays: %f s\n',timeit(#()method2(C3)))
fprintf('method 1, 20 arrays: %f s\n',timeit(#()method1(C20)))
fprintf('method 2, 20 arrays: %f s\n',timeit(#()method2(C20)))
% method 1 is the vectorized code from Ander:
function mask = method1(C)
mask = sum(isnan(cat(3,C{:})),3)>0;
% method 2 is the loop code from this answer:
function mask = method2(C)
mask = isnan(C{1});
for ii=2:numel(C)
mask = mask | isnan(C{ii});
end
function mat = create(sz,p)
mat = rand(sz);
mat(mat<p) = nan;
These are the results on my machine (with R2017a):
>> so(500)
method 1, 3 arrays: 0.003215 s
method 2, 3 arrays: 0.000386 s
method 1, 20 arrays: 0.016503 s
method 2, 20 arrays: 0.001257 s
The loop is 10x faster! For small arrays I see much less of a difference, but the loop code is still several times faster, even for 5x5 arrays.

Related

How to fill in missing NAN's?

I want to change [1 nan 1 2 2 nan nan 3 nan 4 nan nan 5] into [1 1.5 1 2 2 2 3 3 3.5 4 4 5 5]. If there is a single NAN, I want the NAN to be filled in with the average of the numbers before and after. If there is more than one NAN.
I want the NAN to be filled in with the nearest number.
So far, I only have the code to find the single NAN's:
max_x = x(:, 2);
min_x = x(:, 3);
for jj = 1:length(max_x)
for kk = 1:length(min_x)
if isnan(max_x(jj))
max_x (jj) = ((max_x(jj-1)+max_x(jj+1))/2);
elseif isnan (min_x(kk))
min_x (kk) = ((min_x(kk-1)+min_x(kk+1))/2);
end
end
end
How do I fill in the NAN's that aren't single?
Much thanks.
The title of this question is also nearly the answer - Fill in missing values using fillmissing.
A = [1 nan 1 2 2 nan nan 3 nan 4 nan nan 5];
B = fillmissing(A,'linear');
This function was introduced in R2016b.
The same logic can be implemented using interp1 and isnan.
idx = ~isnan( A );
x = 1:numel(A);
B = interp1( x(idx), A(idx), x, 'linear', 'extrap' );
Note that the extrapolation here gives slightly different behaviour for NaN values at each end of the input vectors.
A sample code:
% To paste in main .m file
A = [1 nan 1 2 2 nan nan 3 nan 4 nan nan 5]; % Input array
[A] = new_array(A) % Function to get a new array
% To paste in individual .m file as function
function [x]= new_array(x)
is_nan_ar = isnan(x); % Getting 0/1 array of nan elements
array_l = length(x); % Getting length of x array (just to do it only once)
for k = 1:array_l % Checking every element of input array whether it's...
if (k==1) && (is_nan_ar(k)==1) % First element and nan
kk = 2; % Initial index for searching the nearest non-nan element
while (isnan(is_nan_ar(kk))==1) % Checking elements for being nan
kk=kk+1; % Increasing index while we're searching
end
x(k) = x(kk); % Writing down the first not nan element
elseif (k==array_l) && (is_nan_ar(k)==1) % The same search for the last
kk = array_l-1; % Intial index
while (isnan(is_nan_ar(kk))==1) % Reversed search for not not nan
kk=kk-1;
end
x(k) = x(kk); % Writing down what we found
elseif (is_nan_ar(k)==1) % When we're checking not the first and not the last
s_r = 1; % Search range (1 element to the left/right)
while (is_nan_ar(k-s_r)==1) && (is_nan_ar(k+s_r)==1) %Looking for not nan
s_r = s_r+1; % Increasment of the range if didn't find
end
if (is_nan_ar(k-s_r)==0) && (is_nan_ar(k+s_r)==0) % Two non-nans are near
x(k) = (x(k-s_r)+x(k+s_r))/2;
elseif (is_nan_ar(k-s_r)==0) % Only one non-nan on the left
x(k) = x(k-s_r);
else % Only one non-nan on the right
x(k) = x(k+s_r);
end
end
end
end

Matching matrices of different size and creating new matrix in MATLAB

I have a matrix A and B as the following:
A = [1 NaN 3 4 5 NaN NaN 8 9 10];
B = [2 6 7];
Matrix B has the same size as there are NaN values in matrix A (so 3x1 in this case).
I would like to replace the NaN values in the same order as the values appear in B. So the output should look like:
C = [1 2 3 4 5 6 7 8 9 10];
I can replace the NaN, if both matrices have the same size. For T = 10 and N = 1, I would use:
for t=1:T
for i=1:N
if A == NaN
C(t,i) = B;
else
C(t,i) = A(t,i);
end
end
end
However, I would like to know whether I could compare these matrices and replace the values even if the matrices are of different size? Saying differently, if A = NaN take the first value of B. For the next A = NaN take the second value in B.
You can simply do:
A(find(isnan(A))) = B; % store the result of find(...) to keep track of NaN indices
isnan() is the proper way of determining whether a value is NaN (since NaN ~= NaN), while find() returns the indices of A where an element is NaN in this case.
As per #Adiel's suggestion, you can use logical indexing instead to more compactly achieve the same result, provided you don't need the indices of NaN elements later on:
A(isnan(A)) = B;

Replace non-NaN values with their row indices within matrix

I have the 4x2 matrix A:
A = [2 NaN 5 8; 14 NaN 23 NaN]';
I want to replace the non-NaN values with their associated indices within each column in A. The output looks like this:
out = [1 NaN 3 4; 1 NaN 3 NaN]';
I know how to do it for each column manually, but I would like an automatic solution, as I have much larger matrices to handle. Anyone has any idea?
out = bsxfun(#times, A-A+1, (1:size(A,1)).');
How it works:
A-A+1 replaces actual numbers in A by 1, and keeps NaN as NaN
(1:size(A,1)).' is a column vector of row indices
bsxfun(#times, ...) multiplies both of the above with singleton expansion.
As pointed out by #thewaywewalk, in Matlab R2016 onwards bsxfun(#times...) can be replaced by .*, as singleton expansion is enabled by default:
out = (A-A+1) .* (1:size(A,1)).';
An alternative suggested by #Dev-Il is
out = bsxfun(#plus, A*0, (1:size(A,1)).');
This works because multiplying by 0 replaces actual numbers by 0, and keeps NaN as is.
Applying ind2sub to a mask created with isnan will do.
mask = find(~isnan(A));
[rows,~] = ind2sub(size(A),mask)
A(mask) = rows;
Note that the second output of ind2sub needs to be requested (but neglected with ~) as well [rows,~] to indicate you want the output for a 2D-matrix.
A =
1 1
NaN NaN
3 3
4 NaN
A.' =
1 NaN 3 4
1 NaN 3 NaN
Also be careful the with the two different transpose operators ' and .'.
Alternative
[n,m] = size(A);
B = ndgrid(1:n,1:m);
B(isnan(A)) = NaN;
or even (with a little inspiration by Luis Mendo)
[n,m] = size(A);
B = A-A + ndgrid(1:n,1:m)
or in one line
B = A-A + ndgrid(1:size(A,1),1:size(A,2))
This can be done using repmat and isnan as follows:
A = [ 2 NaN 5 8;
14 NaN 23 NaN];
out=repmat([1:size(A,2)],size(A,1),1); % out contains indexes of all the values
out(isnan(A))= NaN % Replacing the indexes where NaN exists with NaN
Output:
1 NaN 3 4
1 NaN 3 NaN
You can take the transpose if you want.
I'm adding another answer for a couple of reasons:
Because overkill (*ahem* kron *ahem*) is fun.
To demonstrate that A*0 does the same as A-A.
A = [2 NaN 5 8; 14 NaN 23 NaN].';
out = A*0 + kron((1:size(A,1)).', ones(1,size(A,2)))
out =
1 1
NaN NaN
3 3
4 NaN

turning a cellarray column into a matrix [duplicate]

So, I have a cell-array of 1xN vectors of different lengths. I want to append them into a matrix so I can display them with imagesc. Obviously the matrix must be the width of the largest vector. My current code for this is below:
tcell = {[1,2,3], [1,2,3,4,5], [1,2,3,4,5,6], [1], []};
lens = cellfun('length', tcell);
rmat = NaN(length(tcell), max(lens));
for i = 1:length(tcell)
rmat(i, 1:lens(i)) = tcell{i};
end
Does anyone know a vectorized solution for this type of problem? I'm not really worried about the speed of this loop because of MATLAB's JIT. I'm just trying to expand my knowledge and this is a case that I come across quite often in my programming.
Here's one solution that uses cellfun with an anonymous function to first pad each cell with NaN values, then vertcat to put the cell contents into a matrix:
tcell = {[1 2 3], [1 2 3 4 5], [1 2 3 4 5 6], [1], []}; % Sample cell array
maxSize = max(cellfun(#numel, tcell)); % Get the maximum vector size
fcn = #(x) [x nan(1, maxSize-numel(x))]; % Create an anonymous function
rmat = cellfun(fcn, tcell, 'UniformOutput', false); % Pad each cell with NaNs
rmat = vertcat(rmat{:}); % Vertically concatenate cells
And the output:
rmat =
1 2 3 NaN NaN NaN
1 2 3 4 5 NaN
1 2 3 4 5 6
1 NaN NaN NaN NaN NaN
NaN NaN NaN NaN NaN NaN

Matlab Trimming at Bounds

I have a matrix A:
NaN NaN NaN NaN NaN NaN NaN 10 1 8 7 2 5 6 2 3 49 NaN NaN NaN NaN NaN NaN
I was wondering if there was a way to detect when the NaNs first turn to numbers and turn the 1st 2 points to NaNs, such as the 10 & 1 both to NaN.
Then find when the numbers turn to NaNs and turn the last two number points, 3 and 49 to NaNs.
Originally I was thinking of using the following, but I was wondering if this was the best way:
i= 2;
while i < 1440
if isnan(A(i)) < isnan(A(i-1)) //Transitioning from NaN to numbers
A(i:i+2) = NaN;
i = i+ 4;
elseif isnan(A(i)) > isnan(A(i-1)) //Transitioning from numbers to NaNs
A(i-2:i) = NaN;
i = i + 1;
else
i = i + 1;
end
end
but was wondering if there was any other way I could optimize it?
First I assume that your vector A is organized with NaN's at the start and end and a continous set of numerics in the middle, as in
A = [NaN ... NaN, contiguous numeric data, NaN ... NaN]
First, I suggest locating the numeric data and working from there, as in,
flagNumeric = ~isnan(A);
Now flagNumeric will be a true for the entries that are numeric and false for NaN's.
So the first numeric will be at
firstIndex = find(flagNumeric,1,'first');
and the last numeric at
lastIndex = find(flagNumeric,1,'last');
You can then use firstIndex and lastIndex to change the first and last numeric data to NaN's
A(firstIndex:firstIndex+1) = NaN;
A(lastIndex-1:lastIndex) = NaN;
% Set the first two non-NaN numbers to NaN
first = find(isfinite(A), 1, 'first');
A(first:first+1) = NaN;
% Set the last two non-NaN numbers to NaN
last = find(isfinite(A), 1, 'last');
A(last-1:last) = NaN;
Of course, the above code will break in special cases (e.g. when last == 1), but these should be straightforward to filter out.
Here's a slightly simpler version based on the same assumption as Azim's answer:
nums = find(~isnan(A));
A( nums([1 2 end-1 end]) ) = NaN;