turning a cellarray column into a matrix [duplicate] - matlab

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

Related

Selecting simultaneously not NaN values in multiple matrices

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.

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

how to join N vectors into matrix in matlab?

I tried looking this up, but don't really know what to look for...
I need to "table-join" N vectors, meaning,
to create a matrix in which every input vector has a row, and every possible entry has a column.
Also it would be nice to have a translation vector to has easy access to which column is responsible for which entry
for example
a = [3, 2, 4, 9]
b = [3, 1, 5, 9]
c = [2, 4, 9, 6]
then
join(a, b, c) =
[
3; 2; nan; 4; nan; 9; nan,
3; nan; 1; nan; 5; 9; nan,
nan; 2; nan; 4; nan; 9; 6,
]
with a translation vector
[3,2,1,4,5,9,6]
so if I find out something about the i'th column, I can easily know what that column represents.
I prefer the join operation to be able to take in n vectors (they can be of the same length), but 2 is also ok.
Also, on a second glance, this data representation seems a bit redundant at some points. Perhaps there is a better way to even represent the "join-matrix"
Thanks
Basically you want to construct your translation vector using all the possible unique inputs in the order that they were received. To do this, we can concatenate all of the inputs together than find the unique values.
values = cat(1, [3, 2, 4, 9], [3, 1, 5, 9], [2, 4, 9, 6])
%// 3 2 4 9
%// 3 1 5 9
%// 2 4 9 6
translationVector = unique(values, 'stable')
%// 3 2 1 4 5 9 6
Then we want to use ismember to return a logical array for any given input to specify which values of our translation vector are present in the input argument.
columns = ismember(translationVector, [3 2 4 9])
%// 1 1 0 1 0 1 0
We then want to set just those columns in the output matrix.
output(1, columns) = [3 2 4 9];
%// 3 2 NaN 4 NaN 9 NaN
%// NaN NaN NaN NaN NaN NaN NaN
%// NaN NaN NaN NaN NaN NaN NaN
We then repeat this for all of your input arrays.
Implementation
Here is some code that accomplishes that.
function [out, translationVector] = yourjoin(varargin)
%// Make sure all inputs are row vectors
varargin = cellfun(#(x)x(:).', varargin, 'uni', 0); %'
%// compute the translation vector
translationVector = unique(cat(1, varargin{:}), 'stable');
%// Pre-allocate your matrix of NaNs
out = nan(numel(varargin), numel(translationVector));
%// Fill in each row using each input argument
for k = 1:numel(varargin)
%// Identify columns that we have
toreplace = ismember(translationVector, varargin{k});
%// Set the values of those columns to the input values
out(k,toreplace) = varargin{k};
end
end
And then as a test:
a = [3 2 4 9];
b = [3 1 5 9];
c = [2 4 9 6];
D = yourjoin(a,b,c)
3 2 NaN 4 NaN 9 NaN
3 NaN 1 NaN 5 9 NaN
NaN 2 NaN 4 NaN 9 6

How to remove variable amount of consecutive NaN values from vector in Matlab?

I have a vector of values such as the following:
1
2
3
NaN
4
7
NaN
NaN
54
5
2
7
2
NaN
NaN
NaN
5
54
3
2
NaN
NaN
NaN
NaN
4
NaN
How can I use
interp1
in such way that only a variable amount of consecutive NaN-values would be interpolated? That is for example I would want to interpolate only those NaN-values where there are at most three consecutive NaN-values. So NaN, NaN NaN and NaN NaN NaN would be interpolated but not NaN NaN NaN NaN.
Thank you for any help =)
P.S. If I can't do this with interp1, any ideas how to do this in another way? =)
To give an example, the vector I gave would become:
1
2
3
interpolated
4
7
interpolated
interpolated
54
5
2
7
2
interpolated
interpolated
interpolated
5
54
3
2
NaN
NaN
NaN
NaN
4
interpolated
First of all, find the positions and lengths of all sequences of NaN values:
nan_idx = isnan(x(:))';
nan_start = strfind([0, nan_idx], [0 1]);
nan_len = strfind([nan_idx, 0], [1 0]) - nan_start + 1;
Next, find the indices of the NaN elements not to interpolate:
thr = 3;
nan_start = nan_start(nan_len > thr);
nan_end = nan_start + nan_len(nan_len > thr) - 1;
idx = cell2mat(arrayfun(#colon, nan_start, nan_end, 'UniformOutput', false));
Now, interpolate everything and replace the elements that shouldn't have been interpolated back with NaN values:
x_new = interp1(find(~nan_idx), x(~nan_idx), 1:numel(x));
x_new(idx) = NaN;
I know this is an bad habit in matlab, but I would think this particular case requires a loop:
function out = f(v)
out = zeros(numel(v));
k = 0;
for i = 1:numel(v)
if v(i) ~= NaN
if k > 3
out(i-k:i - 1) = ones(1, k) * NaN;
else
out(i-k: i - 1) = interp1();%TODO: call interp1 with right params
end
out(i) = v(i)
k = 0
else
k = k + 1 % number of consecutive NaN value encoutered so far
end
end