MATLAB Answers

In a cell how do I extract a specific range of values from a few columns that have a certain value in another column?

12 views (last 30 days)
mdon
mdon on 5 Aug 2016
Commented: mdon on 5 Aug 2016
I'm quite new to MATLAB so apologies if my question is a bit confusing.
I have a cell with subject names in the first column, time points in the second column and experiment values in the third column:
Bob 1 100
Bob 3 200
Bob 4 500
Bill 1 200
Bill 2 300
Bill 5 600
I want to create a struct with 2 fields using this cell; the first with the subjects name and the second field with a matrix that has the time and experiment values for that subject. For example, for the above cell I want it to make a 1x2 struct with 2 fields with the first entry being:
Name: 'Bob'
Values: [1,100 ; 3,200 ; 4,500]
And the seecond entry in the struct being:
Name: 'Bill'
Values: [1,200 ; 2,300 ; 5,600]

Accepted Answer

Stephen Cobeldick
Stephen Cobeldick on 5 Aug 2016
Edited: Stephen Cobeldick on 5 Aug 2016
This method will correctly preserve the order of the input data:
C = {...
'Bob',1,100;
'Bob',3,200;
'Bob',4,500;
'Bill',1,200;
'Bill',2,300;
'Bill',5,600;
'Bob',8,1000;
'Anna',2,900;
'Anna',9,000;
};
[uni,idx,idy] = unique(C(:,1),'stable');
mat = cell2mat(C(:,2:3));
[idy,idz] = sort(idy);
tmp = accumarray(idy,idz,[],@(r){mat(r,:)});
S = struct('name',uni,'values',tmp)
And checking:
for k = 1:numel(S) S(k).name S(k).values end
prints:
ans =
Bob
ans =
1 100
3 200
4 500
8 1000
ans =
Bill
ans =
1 200
2 300
5 600
ans =
Anna
ans =
2 900
9 0

More Answers (3)

Guillaume
Guillaume on 5 Aug 2016
Edited: Guillaume on 5 Aug 2016
If I understood correctly:
c ={'Bob', 1, 100; 'Bob', 3, 200; 'Bob', 4, 500; 'Bill', 1, 200; 'Bill', 2, 300; 'Bill', 5 , 600};
[names, ~, rows] = unique(c(:, 1));
allvalues = cell2mat(c(:, 2:end));
%%option 1: group using splitapply, R2015b or later
values = splitapply(@(row) {row}, allvalues, rows);
%%option 2: group using accumarray, any version
values = accumarray(rows, (1:size(c, 1))', [], @(row) {allvalues(row, :)});
s = struct('Names', names, 'Values', values)
The splitapply (requires R2015b or later) syntax is simpler as you need to subvert accumarray to make it work on several columns at once. On the other hand accumarray is probably faster as it's a compiled function whereas splitapply is a just a plain m file.
  3 Comments
mdon
mdon on 5 Aug 2016
Thank you for the responses Guillaume and Stephen! Apologies for not being as clear in my question. Yes, I was hoping to have the order preserved and I unfortunately don't have MATLAB R2015b.
Some of the entries are actually not in a normal time sequence order (ie. it isn't always increasing) and thus I don't think a sorting would work for me. Thank you though!

Sign in to comment.


Thorsten
Thorsten on 5 Aug 2016
You can also achieve this without accumarray or splitat, using a for-loop.
c ={'Bob', 1, 100; 'Bob', 3, 200; 'Bob', 4, 500; 'Bill', 1, 200; 'Bill', 2, 300; 'Bill', 5 , 600};
[names, ~, rows] = unique(c(:, 1));
allvalues = cell2mat(c(:, 2:end));
for i = 1:numel(names)
S(i).name = names{i};
S(i).values = allvalues(strcmp(c(:,1), names{i}), :);
end

Bekay.Kang
Bekay.Kang on 5 Aug 2016
Edited: Guillaume on 5 Aug 2016
hello mdon.
hope this will be helpful for you !
struct_you_want=struct('Name',cell_data(:,1),'Value',cell2mat(cell_data(:,2:3)));
:)

Community Treasure Hunt

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

Start Hunting!

Translated by