Left join for cell arrays in MATLAB - 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'

Related

Group cell-array row by an ID in another row

I have got this cell-array:
QueueArr = ...
{ [1] [5] [1] [2] [1] [ 1] [ 5] [ 1] [ 2] ;
[6] [8] [7] [9] [5] [10] [18] [17] [19] }
Now I want to group the second row depending on the first one. My result cell-array should look like this:
loopCell = ...
{ [ 1] [ 2] [ 5] ;
[6 7 5 10 17] [ 9 19] [ 8 18] }
I solved this problem with this code:
%// convert from cell to a matrix
loopMatrix = cell2mat(QueueArr);
%// get the unique elements from the first row
loopMatrixUnique = unique(loopMatrix(1,:));
%// create the result cell
loopCell = cell(2,size(loopMatrixUnique,2));
%// iterate through the unique indexes
for i = 1:size(loopMatrixUnique,2)
%// saving the first row
loopCell{1,i} = loopMatrixUnique(i);
%// calculating the grouped elements
loopCell{2,i} = loopMatrix(2,loopMatrix(1,:) == loopMatrixUnique(i));
end
My question now is whether there is an easier or more ideal solution to my problem.
As you were told in comments, the 3rd output of unique is very usefull for your case.
Once you have that, cellfun can also be used to rebuild your cell array quickly:
b = cell2mat(QueueArr(2,:)) ; %// convert bottom line to array for convenience
[C,~,ic]= unique( cell2mat(QueueArr(1,:)) ) ;
R = [ num2cell(C) ; ... %// top row
cellfun( #(x) b(ic==x) , num2cell(1:length(C)) , 'uni',0) ] %// bottom row
I solved it myself with accumarray.
thx to #Dan for the hint.
%// save the second row
sections = [QueueArr{2,:}];
%// get unique chapters and subs
[chapters, ~, subs] = unique([QueueArr{1,:}]);
%// create the grouped cell-array
groups = accumarray(subs, sections, [], #(x) {x});
%// create the result cell-array
loopCell = [num2cell(chatpers); groups.'];

In Matlab, How to eliminate empty columns from the cell array?

So in 3 X 18 cell array, 7 columns are empty and I need a new cell array that's 3 X 11. Any suggestions without going for looping ?
Let's consider the following cell array. Its second column consists only of [], so it should be removed.
>> c = {1 , [], 'a'; 2, [], []; 3, [], 'bc'}
c =
[1] [] 'a'
[2] [] []
[3] [] 'bc'
You can compute a logical index to tell which columns should be kept and then use it to obtain the result:
>> keep = any(~cellfun('isempty',c), 1); %// keep columns that don't only contain []
keep =
1 0 1 %// column 2 should be removed
>> result = c(:,keep)
result =
[1] 'a'
[2] []
[3] 'bc'
How it works:
cellfun('isempty' ,c) is a matrix the same size as c. It contains 1 at entry (m,n) if and only if c{m,n} is empty.
~cellfun('isempty' ,c) is the logical negation of the above, so it contains 1 where c is not empty.
any(~cellfun('isempty' ,c), 1) applies any to each column of the above. So it's a row vector such that its m-th entry equals 1 if any of the cells of c in that column are non-empty, and 0 otherwise.
The above is used as a logical index to select the desired columns of c.
Use cellfun to detect elements, then from that find columns with empty elements and delete those:
cellarray(:, any(cellfun(#isempty, cellarray), 1)) = [];
If instead you'd like to keep columns with at least one non-empty element, use all instead of any.
For example:
>> cellarray = {1 2 ,[], 4;[], 5, [], 3}
[1] [2] [] [4]
[] [5] [] [3]
>> cellarray(:,any(cellfun(#isempty, cellarray), 1))=[]
cellarray =
[2] [4]
[5] [3]

Adding column to cell array

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'

Replace a string in cell array into 1x3 numeric cell array

Cell array data as below:
data=
'A' [0.006] 'B'
'C' [3.443] 'C'
i would like to convert character in first column in to 1x3 vector, mean that
'A' replace by [0] [0] [0],
'C' replace by [0] [1] [0]..
the result will be
[0] [0] [0] [0.006] 'B'
[0] [1] [0] [3.443] 'C'
the code i tried as below:
B=data(1:end,1);
B=regexprep(B,'C','[0 0 0]');
B=regexprep(B,'A','[0 1 0]');
the result show me
B=
'[0 0 0]'
'[0 1 0]'
which is wrong, each character does not change to 1x3 array...please help...
Since you did not specify the rule to convert letters to numbers,
I assumed you want to replace A with 000, B with 001, ..., H with 111
(ie numbers from 0 to 7 in binary, corresponding to letters A to H).
In case you want to go up to Z, the code below can be easily changed.
%# you data cell array
data = {
'A' [0.006] 'B'
'C' [3.443] 'C'
};
%# compute binary numbers equivalent to letters A to H
binary = num2cell(dec2bin(0:7)-'0'); %# use 0:25 to go up to Z
%# convert letters in to row indices in the above cell array "binary"
idx = cellfun(#(c) c-'A'+1, upper(data(:,1)));
%# replace first column, and build new data
newData = [binary(idx,:) data(:,2:end)]
The result:
newData =
[0] [0] [0] [0.006] 'B'
[0] [1] [0] [3.443] 'C'

cell to matrix matching / map / cellOperations (MATLAB)

I cannot find the string equivalent of the finalAnswer using the data below. Please, I cannot use if/for loops! A final answer is preferred with each element as an array (i.e. the format of mainData)
mainData = {'IBM' [201] [1] ;
'GE' [403] [1] ;
'MSFT' [502] [3] ;
'GM' [101] [2] } ;
finalAns = [ 101 2 0.5; 403 1 0.6 ] ;
%% I tried doing this ->
temp = cell2mat(mainData(:,[2 3])) ;
tf = ismember(temp, finalAns(:,[1 2],'rows') ;
secIDs = mainData(tf) ;
In order to get the entries in each row of mainData that match those in finalAns (based on the last two columns of mainData and the first two columns of finalAns) and to get them in the same order that they appear in finalAns and with the last column of finalAns appended, you can do this:
>> temp = cell2mat(mainData(:,2:3));
>> [isThere,index] = ismember(finalAns(:,1:2),temp,'rows');
>> output = [mainData(index(isThere),:) num2cell(finalAns(isThere,3))]
output =
'GM' [101] [2] [0.5000]
'GE' [403] [1] [0.6000]
The output is a 2-by-4 cell array with each value in a separate cell. If you want the last three columns to be collected in a vector, you can replace the calculation of output above with this:
>> temp = [temp(index(isThere),:) finalAns(isThere,3)];
>> output = [mainData(index(isThere),1) num2cell(temp,2)]
output =
'GM' [1x3 double]
'GE' [1x3 double]
Note that now you have a 2-by-2 cell array where cells in the second column contain 1-by-3 double arrays.