Most efficient way of unpacking cell array with one nested level - matlab

I am using cellfun with UniformOutput set to 0. This applies the specified function to each cell, returning the output as a cell in a cell array.
Each of these cells are a cell array with 1 row and 6 columns. For example, a cell array with two cells:
ans =
{
[1,1] =
{
[1,1] = 1
[1,2] = 1
[1,3] = 1
[1,4] = 9
[1,5] = hello
[1,6] = 3
}
[2,1] =
{
[1,1] = 1
[1,2] = 1
[1,3] = 1
[1,4] = -33
[1,5] = world
[1,6] = 3
}
}
I would like to know the most efficient way to convert this into the 2x6 cell array, i.e.:
ans =
{
[1,1] = 1
[1,2] = 1
[1,3] = 1
[1,4] = 9
[1,5] = hello
[1,6] = 3
[2,1] = 1
[2,2] = 1
[2,3] = 1
[2,4] = -33
[2,5] = world
[2,6] = 3
}

Use cat(1,data{:}) which uses the comma separated list data{:} to unpack the cell and cat to concatenate.

Related

How to convert string cell array to int and NaN in Matlab?

I have a string cell array that contains a mix of numbers and None values. I want to convert the None into NaN and numbers into int.
x = {'23','3','None'}
new_x = {23,3,NaN}
You can try cellfun with str2double, e.g.,
>> cellfun(#str2double,x,"UniformOutput", false)
ans =
{
[1,1] = 23
[1,2] = 3
[1,3] = NaN
}
or another option (thank #Luis Mendo)
>> num2cell(str2double(x))
ans =
{
[1,1] = 23
[1,2] = 3
[1,3] = NaN
}

Find the position of equal elements in a matrix using Matlab

Suppose I have:
m = [1,2,3;1,4,5;6,4,7]
I want to get a list containing the positions of the elements in the matrix m so that the positions of equal elements are grouped together. The output for matrix m must be:
{{1,1},{2,1}},{{2,2},{3,2}},{1,2},{1,3},{2,3},{3,1},{3,3}
% 1 2 3 4 5 6 7
We can see here that the positions for the elements that are all equal to each other are grouped together.
The simplest way would be to loop through every unique value and determine the row and column positions that match each value. Something like this could work:
val = unique(m);
pos = cell(1, numel(val));
for ii = 1 : numel(val)
[r,c] = find(m == val(ii));
pos{ii} = [r,c];
end
pos would be a cell array containing all of the positions for each unique value. We can show what these positions are by:
>> format compact; celldisp(pos)
pos{1} =
1 1
2 1
pos{2} =
1 2
pos{3} =
1 3
pos{4} =
2 2
3 2
pos{5} =
2 3
pos{6} =
3 1
pos{7} =
3 3
This of course is not meaningful unless you specifically show each unique value per group of positions. Therefore, we can try something like this instead where we can loop through each element in the cell array as well as display the corresponding element that each set of positions belongs to:
for ii = 1 : numel(val)
fprintf('Value: %f\n', val(ii));
fprintf('Positions:\n');
disp(pos{ii});
end
What I get is now:
Value: 1.000000
Positions:
1 1
2 1
Value: 2.000000
Positions:
1 2
Value: 3.000000
Positions:
1 3
Value: 4.000000
Positions:
2 2
3 2
Value: 5.000000
Positions:
2 3
Value: 6.000000
Positions:
3 1
Value: 7.000000
Positions:
3 3
This gives you what you want, except for the fact that indices of unique elements are also wrapped in cell twice, just like the indices of repeating elements:
m = [1,2,3;1,4,5;6,4,7];
[~, idx] = ismember(m(:), unique(m(:)));
linInd = 1:numel(m);
[i,j] = ind2sub(size(m), linInd);
res = accumarray(idx, linInd, [], #(x) {num2cell([i(x);j(x)]',2)});
Result:
>> celldisp(res)
res{1}{1} =
2 1
res{1}{2} =
1 1
res{2}{1} =
1 2
res{3}{1} =
1 3
res{4}{1} =
2 2
res{4}{2} =
3 2
res{5}{1} =
2 3
res{6}{1} =
3 1
res{7}{1} =
3 3

remove rows from cell based on multiple conditions

I have a cell with dimensions of 50 x 2. A subset of the table is shown below.
code num
AAA 5
AAA 6
BBB 12
AAA 4
CCC 5
I want to find any rows where the code is equal to AAA and the num is not equal to 4. Then remove these rows to leave me with,
code num
AAA 4
BBB 12
CCC 5
I have tried the following,
indx_remove = rf_cell(:, 1) == 'AAA' && rf_cell(:, 2) ~= '4';
This line gives me undefined function eq for input arguments of type cell.
Use the following code:
A(strcmp(A(:,1),'AAA') &([A{:,2}]'~=4),:) = []
I believe I am doing this the hard way, but I hope it is not too stupid.
code num
AAA 5
AAA 6
BBB 12
AAA 4
CCC 5
%generate code vector and num vector
code = ['AAA', 'AAA', 'BBB', 'AAA','CCC']
code = AAAAAABBBAAACCC
num = [5;6;12;4;5]
k = strfind(code, 'AAA') %find your index
k = 1 2 3 4 10 %because the vector code is just a concatenation of your sub-strings, you will need to sort the index out
%here, you can do something smart, your choice, I use modulo, since your char length is 3 characters, the modulo 3 should return 1 for it to be the starting index.
b = mod(k,3)
b = 1 2 0 1 1
index = k(find(b==1)) % 1, 4, 10 returned
column1 = floor(index/3+1) %output 1 2 4, which is the rows with AAA
check = num(floor(column1/3+1)) % just a checking stage, output 5 6 4 of num.
now you have the index of your column 1 for the strings that has AAA for value. Now you find for you column 2 the value 4s
column2 = find(num==4) % output 4
you can write a if statement to remove index [number 4] if both column1 and column2 contains the same number and remove that value (which refers to the index)
Happy coding!
ind = cellfun(#(x,y) strcmp(x,'AAA') & y~=4, {A{:,1}}, {A{:,2}}) '
A(find(ind==0),:)
ans =
{
[1,1] = BBB
[2,1] = AAA
[3,1] = CCC
[1,2] = 12
[2,2] = 4
[3,2] = 5
}
Details
% // Create the values
A = {'AAA', 5;
'AAA' , 6;
'BBB' , 12;
'AAA' , 4;
'CCC' , 5};
%// Create a cell array of the values
{A{:,1}}, {A{:,2}}
ans =
{
[1,1] = AAA
[1,2] = AAA
[1,3] = BBB
[1,4] = AAA
[1,5] = CCC
}
ans =
{
[1,1] = 5
[1,2] = 6
[1,3] = 12
[1,4] = 4
[1,5] = 5
}
%// Create an anonymous function that will be applied to each element of our cell.
%// It will take the elements of the first cell (represented by `x` in the anonymous function)
%// and compare it to `AAA` and the elements of the second cell (`y`) and compare it to `4`.
%// The result is an array with the logical result of the conditions.
ind = cellfun(#(x,y) strcmp(x,'AAA') & y~=4, {A{1:size(A,1),1}}, {A{1:size(A,1),2}}) '
ind =
1
1
0
0
0
%// Then find the rows where these were zero as we wanted to exclude those values
A(find(ind==0),:)
ans =
{
[1,1] = BBB
[2,1] = AAA
[3,1] = CCC
[1,2] = 12
[2,2] = 4
[3,2] = 5
}

Making a match-and-append code more efficient without 'for' loop

I am trying to match 1st column of A with 1st to 3rd columns of B, and append corresponding 4th column of B to A.
For example,
A=
1 2
3 4
B=
1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5
I compare A(:,1) and B(:, 1:3)
1 and 3 are in A(:,1)
1 is in the 1st, 2nd, 3rd rows of B(:, 1:3), so append B([1 2 3], 4:end)' to A's 1st row.
3 is in the 2nd and 4th rows of B(:,1:3), so append B([2 4], 4:end)' to A's 2nd row.
So that it becomes:
1 2 5 4 5 3 1 2
3 4 5 3 6 5 0 0
I could code this using only for and if.
clearvars AA A B mem mem2 mem3
A = [1 2 ; 3 4]
B = [1 2 4 5 4; 1 2 3 5 3; 1 1 1 1 2; 3 4 5 6 5]
for n=1:1:size(A,1)
mem = ismember(B(:,[1:3]), A(n,1));
mem2 = mem(:,1) + mem(:,2) + mem(:,3);
mem3 = find(mem2>0);
AA{n,:} = horzcat( A(n,:), reshape(B(mem3,[4,5])',1,[]) ); %'
end
maxLength = max(cellfun(#(x)numel(x),AA));
out = cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false))
I am trying to make this code efficient, by not using for and if, but couldn't find an answer.
Try this
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
row2 = mat2cell(permute(sum(ab==b,2),[3,1,2]),ones(1,numel(a)));
AA = cellfun(#(x)(reshape(B(x>0,4:end)',1,numel(B(x>0,4:end)))),row2,'UniformOutput',0);
maxLength = max(cellfun(#(x)numel(x),AA));
out = cat(2,A,cell2mat(cellfun(#(x)cat(2,x,zeros(1,maxLength-length(x))),AA,'UniformOutput',false)))
UPDATE Below code runs in almost same time as the iterative code
a = A(:,1);
b = B(:,1:3);
z = size(b);
b = repmat(b,[1,1,numel(a)]);
ab = repmat(permute(a,[2,3,1]),z);
df = permute(sum(ab==b,2),[3,1,2])';
AA = arrayfun(#(x)(B(df(:,x)>0,4:end)),1:size(df,2),'UniformOutput',0);
AA = arrayfun(#(x)(reshape(AA{1,x}',1,numel(AA{1,x}))),1:size(AA,2),'UniformOutput',0);
maxLength = max(arrayfun(#(x)(numel(AA{1,x})),1:size(AA,2)));
out2 = cell2mat(arrayfun(#(x,i)((cat(2,A(i,:),AA{1,x},zeros(1,maxLength-length(AA{1,x}))))),1:numel(AA),1:size(A,1),'UniformOutput',0));
How about this:
%# example data
A = [1 2
3 4];
B = [1 2 4 5 4
1 2 3 5 3
1 1 1 1 2
3 4 5 6 5];
%# rename for clarity & reshape for algorithm's convenience
needle = permute(A(:,1), [2 3 1]);
haystack = B(:,1:3);
data = B(:,4:end).';
%# Get the relevant rows of 'haystack' for each entry in 'needle'
inds = any(bsxfun(#eq, haystack, needle), 2);
%# Create data that should be appended to A
%# All data and functionality in this loop is local and static, so speed
%# should be optimal.
append = zeros( size(A,1), numel(data) );
for ii = 1:size(inds,3)
newrow = data(:,inds(:,:,ii));
append(ii,1:numel(newrow)) = newrow(:);
end
%# Now append to A, stripping unneeded zeros
A = [A append(:, ~all(append==0,1))]

How do I append the same row to each element of a 3D matrix?

a = ones(2,2,2)
a(:,:,1) =
1 1
1 1
a(:,:,2) =
1 1
1 1
I want to append ones(1,1) to the bottom row of each of a(:,:,1) and a(:,:,2)
It's easy to do with CAT function:
a = cat(1, a, ones(1,2,2));
or VERTCAT:
a = vertcat(a, ones(1,2,2));