Cell2mat for unequal lengths - MATLAB - matlab

I've got a simple 2D cell array that contains data in the form of:
What is the simplest way to be able to use cat/cell2mat when each column can be of an unequal length? Is there a way to replace [] entries with 0?
EDIT:
Desired output would be a matrix with the same number of columns. For the example shown above, the rows in the first column would contain:
42
58
81
19
84
16
40
60
40
36
and so on while the rows of the second column would contain:
57
29
14
5
9
69
17
84
12
4
and so on.
EDIT2:
Adding zeroes would look something like this:

Maybe this is what you want. Zeros are added at the end.
c = {[1;2] [10; 20; 30]; [3;4;5] [40]; [6; 7] []}; %// example
c1 = vertcat(c{:,1});
c2 = vertcat(c{:,2});
n1 = numel(c1);
n2 = numel(c2);
result = zeros(max(n1,n2),2);
result(1:n1,1) = c1;
result(1:n2,2) = c2;
In this example:
>> celldisp(c)
c{1,1} =
1
2
c{2,1} =
3
4
5
c{3,1} =
6
7
c{1,2} =
10
20
30
c{2,2} =
40
c{3,2} =
[]
>> result
result =
1 10
2 20
3 30
4 40
5 0
6 0
7 0

This solution proposes an almost vectorized approach to solve the stated problem and also makes it a generic one for any number of columns in an input cell array. This is categorized as "almost-vectorized" one because it uses cellfun('length'..) (if you look at the code) and AFAIK cellfun is basically a wrapper to a for-loop. But in this case, cellfun('length'..) is very lightweight and rest of the code is vectorized and that's why the term "almost-vectorized".
Code -
%// 3 column sized input cell array
c = {[1;2] [10; 20; 30] [3;8;42]; [3;4;5] [40] [3]; [6; 7] [] [5;9;11;32]}
lens = sum(cellfun('length',c),1)
out = zeros(max(lens),numel(lens))
out(bsxfun(#le,[1:max(lens)]',lens)) = vertcat(c{:})
Output -
>> c
c =
[2x1 double] [3x1 double] [3x1 double]
[3x1 double] [ 40] [ 3]
[2x1 double] [] [4x1 double]
>> out
out =
1 10 3
2 20 8
3 30 42
4 40 3
5 0 5
6 0 9
7 0 11
0 0 32

I would urge you to take a look at cellfun. My solution below would work really well, were it not for the 'UniformOutput', false-part. This is only necessary because some cells in my example are empty matrices, on which histc behaves differently.
cellarray = cell(3,2);
cellarray(:,1)= {[3, 2, 1]; [12, 3, 4, 6]; []};
edges = 1:13; % If you do not know the range of values, find them using cellfun(min, cellarray), and max
x = cellfun(#(x)histc(x, edges), cellarray, 'UniformOutput', false)

Related

Repeat row vector as matrix with different offsets in each row

I want to repeat a row vector to create a matrix, in which every row is a slightly modified version of the original vector.
For example, if I have a vector v = [10 20 30 40 50], I want every row of my matrix to be that vector, but with a random number added to every element of the vector to add some fluctuations.
My matrix should look like this:
M = [10+a 20+a 30+a 40+a 50+a;
10+b 20+b 30+b 40+b 50+b;
... ]
Where a, b, ... are random numbers between 0 and 2, for an arbitrary number of matrix rows.
Any ideas?
In Matlab, you can add a column vector to a matrix. This will add the vector elements to each of the row values accordingly.
Example:
>> M = [1 2 3; 4 5 6; 7 8 9];
>> v = [1; 2; 3];
>> v + M
ans =
2 3 4
6 7 8
10 11 12
Note that in your case v is a row vector, so you should transpose it first (using v.').
As Sardar Usama and Wolfie note, this method of adding is only possible since MATLAB version R2016b, for earlier versions you will need to use bsxfun:
>> % instead of `v + M`
>> bsxfun(#plus, v, M)
ans =
2 4 6
5 7 9
8 10 12
If you have a MATLAB version earlier than 2016b (when implicit expansion was introduced, as demonstrated in Daan's answer) then you should use bsxfun.
v = [10 20 30 40 50]; % initial row vector
offsets = rand(3,1); % random values, add one per row (this should be a column vector)
output = bsxfun(#plus,offsets,v);
Result:
>> output =
10.643 20.643 30.643 40.643 50.643
10.704 20.704 30.704 40.704 50.704
10.393 20.393 30.393 40.393 50.393
This can be more easily understood with less random inputs!
v = [10 20 30 40 50];
offsets = [1; 2; 3];
output = bsxfun(#plus,offsets,v);
>> output =
11 21 31 41 51
12 22 32 42 52
13 23 33 43 53
Side note: to get an nx1 vector of random numbers between 0 and 2, use
offsets = rand(n,1)*2

Search particular matrix in cell array

So, I have this cell array contains n x 2 matrix in each cell. Here is the sample data :
[16 17;17 17]
<6x2 double>
<52x2 double>
[17 17;17 18]
[17 18;17 17]
What I am going to do is eliminate the duplicated matrix (matrices with same values or reversed values). in this case is [17 18; 17 17] (5th row), because we already have [17 17; 17 18] (4th row)
I tried using unique function but it says that the function just worked for strings. I also tried to find it with cellfun like this
cellfun(#(x) x==whatToSearch, lineTraced, 'UniformOutput', false)
but it says 'Matrix dimension must agree'
Thanks in advance.
Here is a solution. Given a m x 1 column cell array C of matrices, this code deletes the equivalent duplicates. (Here it will delete the 4th and 5th matrices, which are equivalent to the 1st).
C{1} = magic(3);
C{2} = magic(4);
C{3} = magic(5);
C{4} = C{1};
C{5} = flipud(C{1});
myEq = #(A,B) isequal(A,B) | isequal(A,flipud(B)); %// equivalence operator tests for same or up-down flipped matrix
C = C(:); %// ensure the cell array is a column
Crep = repmat(C,1,size(C,1)); %// repeat cell array along rows to get a square
comp = cellfun(myEq,Crep,Crep'); %'//get a comparison matrix by comparing with transpose
comp = tril(comp) - eye(size(comp)); %// ignore upper triangle and diagonal
idx = find( sum(comp,2)==0 ); %// get index of matrices we want to keep
result = C(idx); %// get result
The output is deletes the 4th and 5th matrices, leaving the first three magic matrices:
>> result
result =
[3x3 double] [4x4 double] [5x5 double]
>> result{1}, result{2}, result{3}
ans =
8 1 6
3 5 7
4 9 2
ans =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
ans =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
Here's code that does what you want.
mydup = rand(5,2);
mycell = {mydup;mydup;rand(7,2);rand(3,2);rand(5,2)}
myNewCell = trimCell(mycell)
Where trimCell is the function below:
function myNewCell = trimCell(myCell)
exclude = false(size(myCell));
for iter = 1:size(myCell,1)
toCompare = myCell{iter};
comparisons = cellfun(#(x) all(size(x)==size(toCompare)) && myEquals(x, toCompare),myCell);
% comparisons has at least 1 true in it, because toCompare==toCompare
exclude(iter) = sum(comparisons)>1;
end
myNewCell = myCell(~exclude);
end
function boolValue = myEquals(x,y)
assert(all(size(x)==size(y)));
boolValue = true;
for iter = 1:size(x,1)
thisRow = all(sort(x(iter,:))==sort(y(iter,:)));
boolValue = boolValue && thisRow;
if ~boolValue
return
end
end
end
Basically, what this function does, is, for each matrix in the cell it checks first if the sizes are equal. If the sizes aren't equal, then we know the matrices aren't equal, even if we reorder the rows.
If they are the same size, then we need to check if they're equal after row reordering. That's accomplished by the function myEquals which goes through row by row and sorts the rows before comparing them. It exits with false on the first row it finds that is not equal after reordering.
Hope this helps,
Andrew

Reading data file with rows of variable sizes

I have a data file containing rows of variable sizes:
16 54 1 -3 5
15 5
1 9 10 5
How can I load it into a cell array data so that
data{1} = [16 54 1 -3 5];
data{2} = [15 5];
data{3} = [1 9 10 5];
?
Let data.txt contains
16 54 1 -3 5
15 5
1 9 10 5
You can read it into a cell array data with the following:
fid = fopen('datatest.txt');
allData = textscan(fid,'%s','Delimiter','\n');
data = cellfun(#str2num, allData{1}, 'UniformOutput', false);
fclose(fid);
>> data =
ans =
[1x5 double]
[1x2 double]
[1x4 double]
>> data{1}
ans =
16 54 1 -3 5
You can try impordata appproach that is short and concise -
%// Assuming filepath1 is the path to your file
data = cellfun(#str2num,importdata(filepath1,'%s'),'uni',0)
You can visualize the data, using celldisp tool that displays contents of a cell array, like this - celldisp(data). The output would be -
data{1} =
16 54 1 -3 5
data{2} =
15 5
data{3} =
1 9 10 5

Run a function on multiple ranges in a matrix at once (in Matlab)

I have the following matrix:
MatrixA =
1 10 50
23 45 76
71 81 91
1 2 3
4 5 6
78 89 91
2 3 4
I would like to run the var function for multiple ranges in each column. More specifically, I would like to calculate var for rows 1 through 3, 2 through 4, 3 through 5, etc. for each column. The output I would like would be:
1281.33 1260.33 430.33
1281.33 1564.33 2216.33
1566.33 2004.33 2496.33
... ... ...
I was thinking the syntax would be something along the lines of:
var(MatrixA([1 2 3]:[3 4 5],:))
but this (obviously) does not work.
I can do this using loops, but I was wondering if there is a solution to this that does not involve loops?
You can get rid of one loop using hankel function to create the ranges:
V = [];
for C = MatrixA,
V = [V, var(hankel(C(1:3),C(3:end))).'];
end
For instance if C contains the first column of MatrixA then
>> hankel(C(1:3),C(3:end))
ans =
1 23 71 1 4
23 71 1 4 78
71 1 4 78 2
and
>> var(hankel(C(1:3),C(3:end)))
ans =
1281.3 1281.3 1566.3 1902.3 1876
You could be a little creative with bsxfun and reshape to compute an index array and then compute the variances:
n = 3;
idx = bsxfun(#plus, 1:size(MatrixA, 1) - n + 1, (0:n - 1)');
B = reshape(var(reshape(MatrixA(idx, :), 3, [])), [], size(MatrixA, 2));

Cell array manipulation matlab

I need elements from the last cell (say k-th) not occurring in cell before it i.e. (k-1)th cell where k = 1,2,...,p. An example, k=2, r=2^(k+2)+2, n=2^(k)+1;
for i=1:k
dt = 1:2^i:n;
for j=1:2^(k-i)+1
cd(j,:)= dt+ r*(j-1);
end
dd{i}=cd;
clear cd
end
dd{1} =[1 3 5; 11 13 15; 21 23 25]
dd{2} = [1 5;21 25]
I want all entries occurring in dd{2} removed from dd{1} i.e.
dd{1}= [3 11 13 15 23].
dd{2}= [1 5;21 25]
I think you can use setdiff to achieve your goal (difference of two arrays):
setdiff([1,2,3,4,5],[3,4])
ans =
1 2 5