I have a n*m cell array Cell_In:
a b * * * * c * * d * * * f
* --> represents empty string (''). Here is what I need:
a b a b a b c b c d c d c f
For a particular column I need to fill the empty cell with the previous non-empty cell until another non-empty cell is found. Following is the code what I wrote.
b = ~cellfun(@isempty,a); c = [find(b(:,1) == 1);size(a,1)+1]; e = diff(c); d = [find(b(:,2) == 1);size(a,1)+1]; f = diff(d); s1 = ''; s2 = ''; for i = 1:length(e) s1 = [s1,repmat(a(c(i),1),1,e(i))]; end for i = 1:length(f) s2 = [s2,repmat(a(d(i),2),1,f(i))]; end Cell_Out = [s1',s2'];
It's working fine but I want to know the best solution?
Assuming A
to be the input cell array, there could be two approaches here.
Approach #1
%// Initlialize output array Aout = cell(size(A)); for k = 1:size(A,2) %// Select one column Ak = A(:,k); %// Logical array with size of Ak and ones at places with non-empty strings pos = cellfun(@(x) ~isempty(x), Ak); %// Find unique strings and find indices for all places in that column %// with respect to those unique strings [unq_str,~,str_idx] = unique(Ak,'stable'); %// Perform cumsum on pos to get an array with a "stepped" array that %// steps up at each non-empty string position. %// Then replace each stepping number with the string IDs idx = changem(cumsum(pos),str_idx(pos),1:sum(pos)); %// Index into each column with those replaced IDs for the final output Aout(:,k) = unq_str(idx); end
With the input slightly changed for testing out the solution code a bit more aggressively, we had after code run -
A = 'a' 'b' '' '' '' 'a' 'c' '' '' 'd' 'a' '' '' 'f' 'c' 'a' Aout = 'a' 'b' 'a' 'b' 'a' 'a' 'c' 'a' 'c' 'd' 'a' 'd' 'a' 'f' 'c' 'a'
Approach #2 [Compact and maybe more efficient]
You can reshape the input cell array into a single columned cell array and as such you won't need to loop through the columns of the cell array and this could lead to a more efficient and compact code -
%// Reshape all cells into a single columned cell array A1 = A(:); %// Rest of the code borrowed from previous approach with reshaping added %// at the end to bring the output back to the size of input array pos = ~cellfun('isempty', A1); [unq_str,~,str_idx] = unique(A1,'stable'); Aout = reshape(unq_str(changem(cumsum(pos),str_idx(pos),1:sum(pos))),size(A));
Bonus: Customized implementation of changem
The codes listed earlier uses changem
that needs Mapping Toolbox
. So, if you do have it, here's a customized version of it, implemented with bsxfun
and max
and is merely a polished version of an earlier solution code posted here. Here goes the custom function code -
%// CHANGEM_CUSTOM Home-cooked vesion of CHANGEM with MAX, BSXFUN function A = changem_custom(A,newvals,oldvals) [valid,id] = max(bsxfun(@eq,A(:),oldvals(:).'),[],2); %//' A(valid) = newvals(id(valid)); return;
So, to use this custom function to replace changem
, just replace the function call name there in the earlier codes.
This solution gets the indices of empty cells and fills it with the data from the previous one. To fill larger gaps multiple steps are required.
ix=find(cellfun(@isempty,M)) while numel(ix)>0, M(ix)=M(ix-1);ix=find(cellfun(@isempty,M)); end