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'
Related
I have a multi-level cell array. The individual levels could be of different size. I am wondering how I can apply cellfunto the lowest level. Imagine the following mulit-level cell array:
a = {randi(10,5,1), randi(5,5,1)}
b = randi(100,5,1,10)
f = {a,b}
Now, I would like to drill down as much as possible and apply cellfun to the deepest level possible of f. At the and of each level, there is a 2D/3D matrix. Let's say, I simply want to add 5 to each value. What's the most efficient way?
Here's the result I am looking for.
[a_nRows, a_nCols, a_nPages] = size(a)
x = cellfun(#plus, f{1}, repmat({5}, a_nRows, a_nCols, a_nPages), 'UniformOutput', false)
y = cellfun(#plus, f(2), {5}, 'UniformOutput', false)
You can use recursion for this.
Firstly, define a function which does one of two things
If the input is a numeric matrix, apply some operation.
If the input is a cell, call this same function with the cell's contents as inputs.
The function would look something like this (defined locally to another function or in its own m file):
function out = myfunc( in, op )
if iscell( in )
out = cellfun( #(x) myfunc(x, op), in, 'UniformOutput', false );
elseif isnumeric( in )
out = op( in );
else
error( 'Cell contents must be numeric or cell!' )
end
end
Then you can call myfunc on your cell. Here is an example similar to yours:
a = {[1 2; 3 4], {eye(2), 10}}; % Nested cell arrays with numeric contents
op = #(M) M + 5; % Some operation to apply to all numeric contents
myfunc( a, op )
% >> ans =
% { [6 7; 8 9], {[6 5; 5 6], 15}}
Directly using your example, the output of myfunc(f, #(M)M+5) is the same as your {x, y{1}} - i.e. the operation op is applied to every cell and nested cell with the result structured the same as the input.
The questions is:
A=[9 10];
And I want to obtain B={'09','10'};
I made this:
for hij=1:size(A,1)
if A{hij}<9
B{hij}=strcat('0',num2str(A{hij}),'');
else
B{hij}=strcat('',num2str(A{hij}),'');
end
end
But I was wondering if there is any posibility to make that without using a loop, maybe using a "cellfun"; thanks!
Is this what you want?
>> B = num2str(A(:),'%02d'); %// second argument to num2str is format spec
B =
09
10
This gives a string matrix B. To convert B into a cell array of strings:
>> B = mat2cell(B,ones(1,size(B,1))).';
B =
'09' '10'
or, as noted by Divakar,
>> B = cellstr(B).';
B =
'09' '10'
Luis answer is good. Just for completeness, you can use arrayfun if you really want:
C = arrayfun(#num2str, A,'UniformOutput', 0 );
lets assume I have the following array:
a = {1; 'abc'; NaN}
Now I want to find out in which indices this contains NaN, so that I can replace these with '' (empty string).
If I use cellfun with isnan I get a useless output
cellfun(#isnan, a, 'UniformOutput', false)
ans =
[ 0]
[1x3 logical]
[ 1]
So how would I do this correct?
Indeed, as you found yourself, this can be done by
a(cellfun(#(x) any(isnan(x)),a)) = {''}
Breakdown:
Fx = #(x) any(isnan(x))
will return a logical scalar, irrespective of whether x is a scalar or vector.
Using this function inside cellfun will then erradicate the need for 'UniformOutput', false:
>> inds = cellfun(Fx,a)
inds =
0
0
1
These can be used as indices to the original array:
>> a(inds)
ans =
[NaN]
which in turn allows assignment to these indices:
>> a(inds) = {''}
a =
[1]
'abc'
''
Note that the assignment must be done to a cell array itself. If you don't understand this, read up on the differences between a(inds) and a{inds}.
I found the answer on http://www.mathworks.com/matlabcentral/answers/42273
a(cellfun(#(x) any(isnan(x)),a)) = {''}
However, I do not understant it...
a(ind) = [] will remove the entries from the array
a(ind)= {''} will replace the NaN with an empty string.
If you want to delete the entry use = [] instead of = {''}.
If you wanted to replace the NaNs with a different value just set it equal to that value using curly braces:
a(ind) = {value}
I want to count all elements in a cell array, including those in "nested" cells.
For a cell array
>> C = {{{1,2},{3,4,5}},{{{6},{7},{8}},{9}},10}
C = {1x2 cell} {1x2 cell} [10]
The answer should be 10.
One way is to use [C{:}] repeatedly until there are no cells left and then use numel but there must be a better way?
Since you are only interested in the number of elements, here is a simplified version of flatten.m that #Ansari linked to:
function n = my_numel(A)
n = 0;
for i=1:numel(A)
if iscell(A{i})
n = n + my_numel(A{i});
else
n = n + numel(A{i});
end
end
end
The result:
>> C = {{{1,2},{3,4,5}},{{{6},{7},{8}},{9}},10};
>> my_numel(C)
ans =
10
EDIT:
If you are feeling lazy, we can let CELLPLOT do the counting:
hFig = figure('Visible','off');
num = numel( findobj(cellplot(C),'type','text') );
close(hFig)
Basically we create an invisible figure, plot the cell array, count how many "text" objects were created, then delete the invisible figure.
This is how the plot looks like underneath:
Put this in a function (say flatten.m) (code from MATLAB Central):
function C = flatten(A)
C = {};
for i=1:numel(A)
if(~iscell(A{i}))
C = [C,A{i}];
else
Ctemp = flatten(A{i});
C = [C,Ctemp{:}];
end
end
Then do numel(flatten(C)) to find the total number of elements.
If you don't like making a separate function, you can employ this clever (but nasty) piece of code for defining a flatten function using anonymous functions (code from here):
flatten = #(nested) feval(Y(#(flat) #(v) foldr(#(x, y) ifthenelse(iscell(x) | iscell(y), #() [flat(x), flat(y)], #() {x, y}), [], v)), nested);
Either way, you need to recurse to flatten the cell array then count.
I have two cell arrays of strings, and I want to check if they contain the same strings (they do not have to be in the same order, nor do we know if they are of the same lengths).
For example:
a = {'2' '4' '1' '3'};
b = {'1' '2' '4' '3'};
or
a = {'2' '4' '1' '3' '5'};
b = {'1' '2' '4' '3'};
First I thought of strcmp but it would require looping over one cell contents and compare against the other. I also considered ismember by using something like:
ismember(a,b) & ismember(b,a)
but then we don't know in advance that they are of the same length (obvious case of unequal). So how would you perform this comparison in the most efficient way without writing too many cases of if/else.
You could use the function SETXOR, which will return the values that are not in the intersection of the two cell arrays. If it returns an empty array, then the two cell arrays contain the same values:
arraysAreEqual = isempty(setxor(a,b));
EDIT: Some performance measures...
Since you were curious about performance measures, I thought I'd test the speed of my solution against the two solutions listed by Amro (which use ISMEMBER and STRCMP/CELLFUN). I first created two large cell arrays:
a = cellstr(num2str((1:10000).')); %'# A cell array with 10,000 strings
b = cellstr(num2str((1:10001).')); %'# A cell array with 10,001 strings
Next, I ran each solution 100 times over to get a mean execution time. Then, I swapped a and b and reran it. Here are the results:
Method | Time | a and b swapped
---------------+---------------+------------------
Using SETXOR | 0.0549 sec | 0.0578 sec
Using ISMEMBER | 0.0856 sec | 0.0426 sec
Using STRCMP | too long to bother ;)
Notice that the SETXOR solution has consistently fast timing. The ISMEMBER solution will actually run slightly faster if a has elements that are not in b. This is due to the short-circuit && which skips the second half of the calculation (because we already know a and b do not contain the same values). However, if all of the values in a are also in b, the ISMEMBER solution is significantly slower.
You can still use ISMEMBER function like you did with a small modification:
arraysAreEqual = all(ismember(a,b)) && all(ismember(b,a))
Also, you can write the loop version with STRCMP as one line:
arraysAreEqual = all( cellfun(#(s)any(strcmp(s,b)), a) )
EDIT: I'm adding a third solution adapted from another SO question:
g = grp2idx([a;b]);
v = all( unique(g(1:numel(a))) == unique(g(numel(a)+1:end)) );
In the same spirit, Im performed the time comparison (using the TIMEIT function):
function perfTests()
a = cellstr( num2str((1:10000)') ); %#' fix SO highlighting
b = a( randperm(length(a)) );
timeit( #() func1(a,b) )
timeit( #() func2(a,b) )
timeit( #() func3(a,b) )
timeit( #() func4(a,b) )
end
function v = func1(a,b)
v = isempty(setxor(a,b)); %# #gnovice answer
end
function v = func2(a,b)
v = all(ismember(a,b)) && all(ismember(b,a));
end
function v = func3(a,b)
v = all( cellfun(#(s)any(strcmp(s,b)), a) );
end
function v = func4(a,b)
g = grp2idx([a;b]);
v = all( unique(g(1:numel(a))) == unique(g(numel(a)+1:end)) );
end
and the results in the same order of functions (lower is better):
ans =
0.032527
ans =
0.055853
ans =
8.6431
ans =
0.022362
Take a look at the function intersect
What MATLAB Help says:
[c, ia, ib] = intersect(a, b) also
returns column index vectors ia and ib
such that c = a(ia) and b(ib) (or c =
a(ia,:) and b(ib,:)).