brad
brad

Reputation: 89

Find the maximum of unique sets of rows in cell array

I have a cell array that looks like:

 ID         Weight    Position
'img1'    [23.6793]    [6]
'img1'    [22.6368]    [2]
'img1'    [22.8294]    [4]
'img2'    [24.3452]    [8]
'img2'    [25.0608]    [3]
'img2'    [25.2548]    [9]
'img2'    [27.1751]    [12]
'img2'    [25.7463]    [5]
'img2'    [23.6599]    [2]
'img3'    [27.1899]    [4]
'img3'    [28.0790]    [1]
'img3'    [27.6633]    [2]
'img3'    [28.9362]    [3]

I want to create a new column that contains the maximum weight for each ID and the position for that maximum weight. The result should look like:

 ID         Weight    Position   Max_Weight    Max_Weight_Pos
'img1'    [23.6793]    [6]        [23.6793]      [6]
'img1'    [22.6368]    [2]        [23.6793]      [6]
'img1'    [22.8294]    [4]        [23.6793]      [6]
'img2'    [24.3452]    [8]        [27.1751]      [12]
'img2'    [25.0608]    [3]        [27.1751]      [12]
'img2'    [25.2548]    [9]        [27.1751]      [12]
'img2'    [27.1751]    [12]       [27.1751]      [12]
'img2'    [25.7463]    [5]        [27.1751]      [12]
'img2'    [23.6599]    [2]        [27.1751]      [12]
'img3'    [27.1899]    [4]        [28.9362]      [3]
'img3'    [28.0790]    [1]        [28.9362]      [3]
'img3'    [27.6633]    [2]        [28.9362]      [3]
'img3'    [28.9362]    [3]        [28.9362]      [3]

Is there an easy way to do this?

Thanks.

Upvotes: 1

Views: 50

Answers (2)

rayryeng
rayryeng

Reputation: 104503

First let's recreate your cell array:

>> C = {'img1'    [23.6793]    [6]
'img1'    [22.6368]    [2]
'img1'    [22.8294]    [4]
'img2'    [24.3452]    [8]
'img2'    [25.0608]    [3]
'img2'    [25.2548]    [9]
'img2'    [27.1751]    [12]
'img2'    [25.7463]    [5]
'img2'    [23.6599]    [2]
'img3'    [27.1899]    [4]
'img3'    [28.0790]    [1]
'img3'    [27.6633]    [2]
'img3'    [28.9362]    [3]};

I'll approach this in another way instead of using accumarray as per thewaywewalk.... but I would have done it that way if I had the chance!

Another suggestion I have would be to loop over all unique labels, find the maximum within the groups and get the corresponding position information as well as the maximum of each group.

Something like this:

%// Get the labels
labels = unique(C(:,1));

%// Convert data and positions to numerical
data = vertcat(C{:,2});
positions = vertcat(C{:,3});

%// To store the maximum values and positions of each group
out_data = zeros(size(labels,1), 1);
pos_data = out_data;

for idx = 1 : numel(labels)
    lbl = labels{idx}; %// Get the ith label

    %// Determine the indices of the labels that match
    ind = strcmp(lbl, C(:,1));

    %// Get the numbers and positions
    num = data(ind);
    pos = positions(ind);

    %// Find maximum value and position
    [out_data(idx) ind] = max(num);
    pos_data(idx) = pos(ind);
end

% // Now place into cell array
C(:,4) = num2cell(out_data(id));
C(:,5) = num2cell(pos_data(id));

We get:

>> C

C = 

    'img1'    [23.6793]    [ 6]    [23.6793]    [ 6]
    'img1'    [22.6368]    [ 2]    [23.6793]    [ 6]
    'img1'    [22.8294]    [ 4]    [23.6793]    [ 6]
    'img2'    [24.3452]    [ 8]    [27.1751]    [12]
    'img2'    [25.0608]    [ 3]    [27.1751]    [12]
    'img2'    [25.2548]    [ 9]    [27.1751]    [12]
    'img2'    [27.1751]    [12]    [27.1751]    [12]
    'img2'    [25.7463]    [ 5]    [27.1751]    [12]
    'img2'    [23.6599]    [ 2]    [27.1751]    [12]
    'img3'    [27.1899]    [ 4]    [28.9362]    [ 3]
    'img3'    [28.0790]    [ 1]    [28.9362]    [ 3]
    'img3'    [27.6633]    [ 2]    [28.9362]    [ 3]
    'img3'    [28.9362]    [ 3]    [28.9362]    [ 3]

Upvotes: 0

Robert Seifert
Robert Seifert

Reputation: 25232

Thats a job for accumarray:

[~,~,u] = unique(data(:,1));
maxima = accumarray(u,[data{:,2}],[],@max)
temp = cell2mat(data(:,[2,3]))
pos = accumarray(u,1:size(data,1),[],@(x) getfield(sortrows( temp(x,:),1),{numel(x),2}) )
output = [data num2cell(maxima(u)) num2cell(pos(u))]

I used @max as you were asking for the maximum, your output actually shows @min. But you could apply any function (@mean, etc.).

Upvotes: 3

Related Questions