Adding column to cell array - matlab

I have a cell that has the following data:
Tom Student
Jim Faculty
Clare Student
What I want to do is add in another column in front to be a serial number.
1 Tom Student
2 Jim Faculty
3 Clare Student
Could someone give some advise please?

You have A defined as:
>> A={'Tom', 'Student'; 'Jim', 'Faculty'; 'Clare', 'Student'}
A =
'Tom' 'Student'
'Jim' 'Faculty'
'Clare' 'Student'
To add a column:
>> newCellCol = strsplit(num2str(1:size(A,1)))'
newCellCol =
'1'
'2'
'3'
>> A = [newCellCol A]
A =
'1' 'Tom' 'Student'
'2' 'Jim' 'Faculty'
'3' 'Clare' 'Student'
>>
For numeric arrays in the first column instead:
>> newCellCol = mat2cell(1:size(A,1),1,ones(1,size(A,1)))';
>> A = [newCellCol A]
A =
[1] 'Tom' 'Student'
[2] 'Jim' 'Faculty'
[3] 'Clare' 'Student'
You can also use num2cell(1:size(A,1))' in place of mat2cell above, as noted by Dan.

Not sure exactly how your cell array is organized, but if like below, you can do as follows:
A={{'Tom', 'Student'}, ...
{'Jim', 'Faculty'}, ...
{'Clare', 'Student'}};
sizeA = size(A,2);
for i = 1:sizeA
A{i} = [i, A{i}]
end
% alternatively, instead of a for loop, you can use cellfun
% A = cellfun(#(x, i)[i x], A, num2cell(1:size(A, 2)), 'UniformOutput',0)
A{1}
A{2}
A{3}
ans =
[1] 'Tom' 'Student'
ans =
[2] 'Jim' 'Faculty'
ans =
[3] 'Clare' 'Student'

Related

Apply cellfun to only one column in cell array

I know cellfun can be applied to an entire cell array and understand its syntax. However is it possible to apply cellfun only to one column in a cell array and not have it affect the other columns?
As user1543042 and It's magic said in the comments, you can apply the cell function to just one column using ':', but you want to add an assignment step. Also, as you want the cell function to return a cell array, you need to flag non-uniformoutput. So, you end up with:
C(:,i) = cellfun(#foo, C(:,i), 'UniformOutput', false)
To see an example in action:
>> C = {1,2,3;4 5 6};
>> C
C =
[1] [2] [3]
[4] [5] [6]
>> size(C)
ans =
2 3
>> cellfun(#(x)x.^2,C(:,1))
ans =
1
16
>> C(:,1) = cellfun(#(x)x.^2,C(:,1))
Conversion to cell from double is not possible.
>> C(:,1) = cellfun(#(x)x.^2,C(:,1),'UniformOutput',false)
C =
[ 1] [2] [3]
[16] [5] [6]
>>

Behavior of 'subsref' for arrays of objects

Imagine a simple array of structures, say:
A = struct('x', {1 2 3}, 'y', {'a' 'b' 'c'});
Asking for a given property for all this array's elements will give something like:
>> A.x
ans =
1
ans =
2
ans =
3
Now, if I explicitly call the subsref function directly on this array, it only retrieves the first element's property:
>> builtin('subsref', A, substruct('.', 'x'))
ans =
1
Why? And is there a possibility to call explicitly another built-in method that will retrieve the property for all the array's elements?
The subsref method can return it but not as a comma separated list the way you get it in the interpreter. It returns them as separate output arguments that means:
>> [a,b,c]=builtin('subsref', A(:), substruct('.', 'x'))
a =
1
b =
2
c =
3
you can capture the output in a cell array if you like
>> [x{1:numel(A)}]=builtin('subsref', A(:), substruct('.', 'x'))
x =
[1] [2] [3]

Left join for cell arrays in MATLAB

I've 2 cell arrays in MATLAB, for example:
A= {jim,4,paul,5 ,sean ,5,rose, 1}
and the second:
B= {jim, paul, george, bill, sean ,rose}
I want to make an SQL left join so I'll have all the values from B and their match from A. If they don't appear in A so it will be '0'. means:
C= {jim, 4, paul, 5, george, 0, bill, 0, sean, 5, rose, 1}
didn't find any relevant function for help.
thanks.
Approach #1
%// Inputs
A= {'paul',5 ,'sean' ,5,'rose', 1,'jim',4}
B= {'jim', 'paul', 'george', 'bill', 'sean' ,'rose'}
%// Reshape A to extract the names and the numerals separately later on
Ar = reshape(A,2,[]);
%// Account for unsorted A with respect to B
[sAr,idx] = sort(Ar(1,:))
Ar = [sAr ; Ar(2,idx)]
%// Detect the presence of A's in B's and find the corresponding indices
[detect,pos] = ismember(B,Ar(1,:))
%// Setup the numerals for the output as row2
row2 = num2cell(zeros(1,numel(B)));
row2(detect) = Ar(2,pos(detect)); %//extracting names and numerals here
%// Append numerals as a new row into B and reshape as 1D cell array
out = reshape([B;row2],1,[])
Code run -
A =
'paul' [5] 'sean' [5] 'rose' [1] 'jim' [4]
B =
'jim' 'paul' 'george' 'bill' 'sean' 'rose'
out =
'jim' [4] 'paul' [5] 'george' [0] 'bill' [0] 'sean' [5] 'rose' [1]
Approach #2
If you want to work with the numerals in the cell arrays as strings, you can use this modified version -
%// Inputs [Please edit these to your actual inputs]
A= {'paul',5 ,'sean' ,5,'rose', 1,'jim',4};
B= {'jim', 'paul', 'george', 'bill', 'sean' ,'rose'}
%// Convert the numerals into string format for A
A = cellfun(#(x) num2str(x),A,'Uni',0)
%// Reshape A to extract the names and the numerals separately later on
Ar = reshape(A,2,[]);
%// Account for unsorted A with respect to B
[sAr,idx] = sort(Ar(1,:));
Ar = [sAr ; Ar(2,idx)];
%// Detect the presence of A's in B's and find the corresponding indices
[detect,pos] = ismember(B,Ar(1,:));
%// Setup the numerals for the output as row2
row2 = num2cell(zeros(1,numel(B)));
row2 = cellfun(#(x) num2str(x),row2,'Uni',0); %// Convert to string formats
row2(detect) = Ar(2,pos(detect)); %//extracting names and numerals here
%// Append numerals as a new row into B and reshape as 1D cell array
out = reshape([B;row2],1,[])
Code run -
B =
'jim' 'paul' 'george' 'bill' 'sean' 'rose'
A =
'paul' '5' 'sean' '5' 'rose' '1' 'jim' '4'
out =
'jim' '4' 'paul' '5' 'george' '0' 'bill' '0' 'sean' '5' 'rose' '1'

Matlab - Return only the rows of a matrix 'A' that not contain some values of matrix 'B'

How do I return only the rows of a matrix 'A' that not contain some values (These values ​​are an array 'B')?
A = {'A1', 5 'P01,P02,P03,P04,P07';
'A2' 7, 'P07,P10';
'A3' 8, 'P07,P09';
'A4' 8, 'P10,P11'};
B = { 'P07'; 'P10'; 'P11'};
I need to return only:
'A1' ( P01,P02,P03,P04 not exist in B)
'A3' (P09 not exist in B)
Thanks in advance for your help
Since you are dealing with weirdly shaped cell arrays and some strange string operations, I do not know how to solve this cleanly with one statement. You can try the following loop:
R = {};
for i = 1 : size(A, 1)
test = strsplit(A{i, 3}, ',');
for j = 1 : length(test)
if nnz(strcmp(B, test{j})) == 0
R = [R; A(i, 1)];
break;
end
end
end
The result is:
R =
'A1'
'A3'
Of course these calculations could be made much faster if it was possible to work with just the numerical components of each search string in an actual matrix rather than cell arrays of strings.

Matlab: Divide a cell array of strings into 2 separate cell arrays

I have the following cell array:
MyArray = {'12345'; '67890'; '12345'}
I would like to create two new cell arrays that contain "split-up" versions of the above arrays. In particular, I would like one cell array (MyArray2) to contain the first 3 characters of each element and the second cell array (MyArray3) to contain the last 2 characters of each element:
MyArray2 = {'123'; '678'; '123'}
MyArray3 = {'45'; '90'; '45'}
Is there some sort of "reverse" strcat function?
It depends how you want to split it. For your case (before your edit with square brackets) it's very easy:
A = ['12345'; '67890'; '12345']
B = A(:,1:3)
C = A(:,4:end)
For more complicated cases, have a look at strsplit (a quite new function, otherwise available at File Exchange)
Actually you mentioned you'd have a cell array, and also if your strings have different lengths it's a little more tricky. Have a look at the cellfun documentation.
days = {'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'};
abbrev = cellfun(#(x) x(1:3), days, 'UniformOutput', false)
The syntax #(x) creates an anonymous function. This code returns
abbrev =
'Mon' 'Tue' 'Wed' 'Thu' 'Fri'
Therefore:
A = {'12345'; '67890'; '12345'}
B = cellfun(#(x) x(1:3), A, 'UniformOutput', false)
C = cellfun(#(x) x(4:5), A, 'UniformOutput', false)
B =
'123'
'678'
'123'
C =
'45'
'90'
'45'
How about this?
sol = mat2cell(cell2mat(MyArray),ones(1,numel(MyArray)),[3 2]);
Your desired results are sol(:,1) and sol(:,2):
>> sol(:,1)
ans =
'123'
'678'
'123'
>> sol(:,2)
ans =
'45'
'90'
'45'