How to manipulate a cell array

Dear Matlab Community,
Well, the truth is that I am a newbie in Matlab and conconfrontated with a (simple) problem.
I have created the following cell
Cell = transpose({1, 2, 3, 'func1(4545, 4545, 3222)', 'func2(111,222)', 7, 8, 9});
which looks like this
[ 1]
[ 2]
[ 3]
'func1(4545, 4545, 3222)'
'func2(111,222)'
[ 7]
[ 8]
[ 9]
Next thing I did was looking for cell entries which are non-numeric by
CellsInd = cellfun(@(x) isnumeric(x), Cells, 'UniformOutput', true);
while in the next step non-numeric cell entries are splited by
Cells(~CellsInd, 1) = regexp(Cells(~CellsInd, 1), '\(|,|\)', 'split');
to get
[ 1]
[ 2]
[ 3]
{1x5 cell}
{1x4 cell}
[ 7]
[ 8]
[ 9]
So far it works. What I want to have is the following structure
[ 1] [ ] [ ] [ ]
[ 2] [ ] [ ] [ ]
[ 3] [ ] [ ] [ ]
[-1] [4545] [4545] [3222]
[-2] [ 111] [ 222] [ ]
[ 7] [ ] [ ] [ ]
[ 8] [ ] [ ] [ ]
[ 9] [ ] [ ] [ ]
i.e. func1 should be replaced by -1, and func2 by -2. Moreover the {1x5 cell} and {1x4 cell} contains an empty cell due to regexp.
How to get rid of this (simple) problem?
Thank you for your reading and effort.
Best wishes gimlii

Answers (1)

First, note that while your code works, conceptually, there is a bug. The splitting of the non-numeric entries should read:
Cells(~CellsInd) = regexp(Cells(~CellsInd), '\(|,|\)', 'split'); %CellsInd is already the same shape as Cells
Your original code works, because you start with a vector cell array, but try it with:
c = {1, 2, '3'; '4', 5, 6}; %by the way naming a variable Cell or Cells is a bad idea. Too close to the cell function.
and you'll see it fails.
Anyway, one way to expand your cell array:
c = {1, 2, 3, 'func1(4545, 4545, 3222)', 'func2(111,222)', 7, 8, 9}';
numericcells = cellfun(@isnumeric, c); %simpler syntax than yours. Also, 'UniformOutput, true is default.
c(~numericcells) = regexp(c(~numericcells), '\(|,|\)', 'split');
maxcols = max(cellfun(@numel, c));
c2 = cell(size(c, 1), maxcols); %destination
c2(numericcells, 1) = c(numericcells); %note the numericcells, 1 is intended for c2
textrows = cellfun(@(x) [x, cell(1, maxcols-numel(x))], c(~numericcells), 'UniformOutput', false); %expand text rows to the same number of columns
c2(~numericcells, :) = vertcat(textrows{:})
Finally, to remove columns with all empty:
c2(:, all(cellfun(@isempty, c2))) = []

6 Comments

Many thanks for your help. But, executing the code above leads to the following output
[ 1] [] [] []
[ 2] [] [] []
[ 3] [] [] []
'func1' '4545' ' 4545' ' 3222'
'func2' '111' '222' ''
[ 7] [] [] []
[ 8] [] [] []
[ 9] [] [] []
What I wanted to have is the following output
[ 1] [ ] [ ] [ ]
[ 2] [ ] [ ] [ ]
[ 3] [ ] [ ] [ ]
[-1] [4545] [4545] [3222]
[-2] [ 111] [ 222] [ ]
[ 7] [ ] [ ] [ ]
[ 8] [ ] [ ] [ ]
[ 9] [ ] [ ] [ ]
Is there a clever way to make from 'func1' a [-1] and from 'func2' a [-2]. Also, the other strings should be converted to integers.
Of course, I can use strrep() and sscanf(x, '%d') to convert the strings to integers. But I don't know if this is the best way to operate within cells.
Thank you in advance. gimlii
I'm unclear on the rule to transform 'func1', 'func2' into -1, -2, so I rename them in order:
c2(~numericcells, 1) = num2cell(-[1:sum(~numericcells)]')
To convert the strings of numbers into actual numbers:
c2(~numericcells, 2:end) = num2cell(str2double(c2(~numericcells, 2:end)))
Dear Guillaume,
thank you for your response. There is one thing, which I don't really explained and which also lead to a misunderstanding.
It was a coincidence that 'func1' should be converted to [-1] and 'func2' to [-2]. Usually there are no strings like 'func1' or 'func2', but strings like 'asdasd' or 'wrfsdfs' (without numbers).
Is there a clever way to check the string in the cell and replace it with an integer value? That's why I asked if strrep() is a good choice.
I have a general question: in your code above, you are using str2double to convert a string to a double value. When I would write in Matlab something like
var = 1;
Is var an integer or double value?
Cheers, gimlii
By default all variables in matlab are double, so yes when you write var = 1, var is double. There probably very little benefit to make the number explicitly an integer (in matlab, in other languages it's different). You can store all integers accurately as double up to flintmax.
There are many ways to check a string for whatever pattern you want. I'd probably use a regular expression to detect strings with alphanumeric characters (that assumes you don't have numbers like 1e5 encoded as strings) and replace them directly by an integer. So, no strrrep.
Something similar to this should work for you:
c = {1, 2, 3, 'func1(4545, 4545, 3222)', 'func2(111,222)', 7, 8, 9}';
%processing of the string cells
isstring = cellfun(@ischar, c); %find elements of cell array that are strings
c(isstring) = regexp(c(isstring), '\(|,|\)', 'split'); %split strings on brackets or comma
maxcols = max(cellfun(@numel, c)); %get number of columns
textcells = cellfun(@(x) [x, repmat({''}, 1, maxcols-numel(x))], c(isstring), 'UniformOutput', false); %expand text cells to maxcols columns with empty strings
textcells = vertcat(textcells{:}); %convert into a 2d cell array
emptycells = cellfun(@isempty, textcells); %cells that have empty strings
numbercells = cellfun(@isempty, regexp(textcells, '[a-zA-Z]', 'once')); %find text that does not contain letters
textcells(numbercells & ~emptycells) = num2cell(str2double(textcells(numbercells & ~emptycells))); %convert number strings to numbers
textcells(~numbercells) = num2cell(-(1:sum(~numbercells))); %replace non numeric strings by consecutive negative integers
textcells(emptycells) = {[]}; %and empty cells by empty matrices?
textcells(:, all(emptycells)) = []; %delete empty columns
%putting together the final array
c2 = cell(size(c, 1), size(textcells, 2)); %prepare destination cell array
c2(~isstring, 1) = c(~isstring); %copy numbers in first column of destination
c2(isstring, :) = textcells %copy text cells
Please use comments, rather than answers. Original comment by grima gandolf, posted as an answer moved here:
Dear Guillaume, Thank you for your effort and help.
Indeed, there is one fact, which I don't considered yet. Well, it happens sometimes that the cell array have the following structure
[ 1]
[ 2]
[ 3]
'asdasd(4545, 4545, 3222)'
'wrfsdfs(111,222)'
'wrfsdfs(41, 311)'
'asdasd(33,55)'
[ 7]
[ 8]
[ 9]
where some rows contain the same string, i.e., 'asdasd' or 'wrfsdfs'. Thus, for my purposes, it is very important that 'asdasd' is always replaced by [-1] and 'wrfsdfs' by [-2], in such a way that
[ 1] [ ] [ ] [ ]
[ 2] [ ] [ ] [ ]
[ 3] [ ] [ ] [ ]
[-1] [4545] [4545] [3222]
[-2] [ 111] [ 222] [ ]
[-2] [ 41] [ 311] [ ]
[-1] [ 33] [ 55] [ ]
[ 7] [ ] [ ] [ ]
[ 8] [ ] [ ] [ ]
[ 9] [ ] [ ] [ ]
is obtained. Do you think, that strrep() is a good choice to do it?
Cheers, gimlii
No strrep is never a good choice. You're replacing strings by numbers, strrep is only useful to replace part of a string by another string.
I think that you need to spend more time trying to understand how the pieces of code I've posted work and experiment with modifying it yourself. It's the only way you'll gain enough experience to write the same on your own. Otherwise, you're just going to keep coming back asking for more and more refinements.
The code allows you to identify which cells contain text that is not a number, textcells(~numbercells). You can manipulate these cells any way you want and replace them by anything you want according to whatever rule.
If you want to identify unique text and where the repetitions are it's straightforward with matlab: simply use unique.
So, a simple way to do what you want is with:
[~, ~, unidx] = unique(textcells(~numbercells));
textcells(~numbercells) = num2cell(-unidx); %replace non numeric strings with unique indices

Sign in to comment.

Categories

Asked:

on 13 Dec 2015

Edited:

on 18 Dec 2015

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!