【问题标题】:Return the maximum value of every subgroup in a table返回表中每个子组的最大值
【发布时间】:2019-10-18 04:30:28
【问题描述】:

我有一段这样的表格:

X     Y    Value
__   __    __   
1    2     6.9   
1    3     6.8   
1    4     8.1 
2    1     7.2 
2    3     11.7
2    4     16
3    1     22.6
3    2     20.5
3    3     18.1
… … …

对于具有相同X 的每组行,我只需要选择具有最大Value 的行。如何生成这样的表格?

X     Y    Value 
__   __    __   
1   4      8.1 
2   4      16
3   1      22.6

到目前为止我的代码只产生一行:

X = [1; 1; 1; 2; 2; 2; 3; 3; 3];
Y = [2; 3; 4; 1; 3; 4; 1; 2; 4];
Value = [6.9; 6.8; 8.1;7.2;11.7;16;22.6;20.5;18.1];
T = table(X,Y,Value);
[~,maxidx] = max(Value);
T(maxidx,:)
%{
ans =
  1×3 table
    X    Y    Value
    _    _    _____
    3    1    22.6 
%}

【问题讨论】:

  • 也许尝试创建一个二维矩阵,而不是将 Y 作为向量。每一行(或你喜欢的每一列)都对应于 X 中的一个值。然后你可以尝试在这个矩阵上使用 max()。
  • edit 功能并不是要讨论您对建议的解决方案所遇到的问题。请为此使用 cmets,或提出新问题。

标签: matlab max grouping subset matlab-table


【解决方案1】:

如果您使用的是 R2015b 或更新版本,您可以使用splitapply

function T2 = q56413455()
% Define some example inputs:
X = [1; 1; 1; 2; 2; 2; 3; 3; 3];
Y = [2; 3; 4; 1; 3; 4; 1; 2; 4];
Value = [6.9; 6.8; 8.1;7.2;11.7;16;22.6;20.5;18.1];
T = table(X,Y,Value);
% Call the "business logic" and assign the output:
T2 = getMaxRows(T);

function out = getMaxRows(T)
GROUPING_VAR = 1; % We assume that the 1st column contains the grouping variable
varnames = T.Properties.VariableNames;
tmp = splitapply(@maxrow, T, T.(varnames{ GROUPING_VAR }));
out = array2table(tmp, 'VariableNames', varnames );

function outrow = maxrow(varargin)
COL_WITH_MAX = 3; % We assume that the 3rd columns is the one that matters for max()
subArr = cell2mat(varargin);
[~,I] = max(subArr(:,COL_WITH_MAX));
outrow = subArr(I,:);

调用它会产生所需的结果:

>> q56413455
ans =
  3×3 table
    X    Y    Value
    _    _    _____
    1    4     8.1 
    2    4      16 
    3    1    22.6 

另一个变体使用splitapply(@max, ...) 的第二个nd 输出,它是组内最大值的索引。然后我们需要将先前组中的元素数量添加到此(使用diff 完成):

X = [1; 1; 1; 2; 2; 2; 3; 3; 3];
Y = [2; 3; 4; 1; 3; 4; 1; 2; 4];
Value = [6.9; 6.8; 8.1;7.2;11.7;16;22.6;20.5;18.1];
T = table(X,Y,Value);

% Get the position of the maximum Value in every group
[~,I] = splitapply(@max, T.Value, T.X); % I == [3; 3; 1]

% Get beginnings of every group
lastGroupEnd = find([1; diff(X)])-1; % lastGroupEnd == [0; 3; 6]

% Offset the maximum positions by group starts to get row indices in the original table
T2 = T(I + lastGroupEnd, :);

【讨论】:

  • 是否可以在不使用 out 和 outrow 的功能的情况下做到这一点?此外,我将 splitapply 行更改为 out = splitapply(@maxrow, [T.X T.Y T.Value], T.X);并在其上方添加 T = table(X,Y,Value);。如何将 ans 存储为新表?
  • 谢谢。在 out =.. 行之后添加行 T2 = array2table(out, 'VariableNames',{'X','Y','Value'}) 。它没有将其存储在工作区中,为什么?
  • 我不知道你到底在做什么,所以几乎不可能知道。如果您使用函数来执行此操作,例如在我的示例中,并且函数的输出是 out(而不是 T2),那么除非您重新定义 out 或设置,否则您不会得到不同的输出输出为T2。如果这不能解决您的问题并且您想更详细地解释它,请随时通过chat与我联系。
  • 谢谢。我更新了问题中的代码。或许你能看出哪里出了问题。
【解决方案2】:

您可以对唯一的 X 值使用循环:

X = [1; 1; 1; 2; 2; 2; 3; 3; 3];
Y = [2; 3; 4; 1; 3; 4; 1; 2; 4];
Value = [6.9; 6.8; 8.1;7.2;11.7;16;22.6;20.5;18.1];

uniqueX = unique(X); % Get 'X' unique values
maxidx = zeros(size(uniqueX));
for i = 1:length(uniqueX)
    xind = find(X == uniqueX(i)); % Find all indices of a unique 'X' value
    [~,vind] = max(Value(xind)); % Find index of max Value in 'xind' group
    maxidx(i) = xind(vind); % Get the index in the original group 
end

T(maxidx,:)

输出:

ans =
  3×3 table
    X    Y    Value
    _    _    _____
    1    4     8.1 
    2    4      16 
    3    1    22.6 

【讨论】:

  • 不幸的是,您看不到我对现在已删除的答案的评论,所以我会再写一遍:使用find 是有问题的,因为如果我们在不同的组中有重复值,这种方法就会失败,其中一组的最大值也存在于之前的某个组中,但它不是那里的最大值(即用Value = [16;17;18;7.2;11.7;16;22.6;20.5;18.1]; 试试这个,看看会发生什么)。这正是我决定返回整行而不是行索引的原因。此解决方案适用于大多数(但不是全部)输入,这使得以后调试变得危险且困难。
  • @Dev-iL:是的,你是对的,我从我的回答中删除了splitapply
【解决方案3】:

使用unique和cumsum

X = [1; 1; 1; 2; 2; 2; 3; 3; 3];
Y = [2; 3; 4; 1; 3; 4; 1; 2; 4];
values = [6.9; 6.8; 8.1;7.2;11.7;16;22.6;20.5;18.1];

T = table(X,Y,values);

% Find number of categorical X and corresponding category X 
[count,category]=hist(X,unique(X));

% Starting offset index of each category, X = 2, offset is 3, X = 3, offset is 6
location = cumsum(count);

maxidx = zeros(1,numel(category));

for i = 1:numel(category)
    [~,maxidx(i)] = max(T(T.X == category(i) , :).values);
if i == 1
    % First category, no need offset 
else
    % Locate max index in whole table by adding offset to the initial index
    maxidx(i) = maxidx(i) + location(i-1);
end
end
T(maxidx,:)
%{
ans =

  3×3 table

    X    Y    values
    _    _    ______

    1    4      8.1 
    2    4       16 
    3    1     22.6 
    %}

【讨论】:

  • 我也考虑过这个,但是如果我们在不同的组中有重复值,这种方法会失败,其中一个组中的最大值也存在于前一组中,但它不是那里的最大值(即用Value = [16;17;18;7.2;11.7;16;22.6;20.5;18.1]; 试试这个,看看会发生什么)。这正是我决定返回整行而不是行索引的原因。此解决方案适用于大多数(但不是所有)输入,这使得以后调试变得危险且困难。
  • @Dev-iL 检查这个,认为它可能完美运行
  • 这个想法似乎是正确的。几个 cmets/建议:1)您似乎已经重新实现了 splitapply - 如果您想了解如何使用 splitapply 完成此操作,请查看我的(现已更新)答案。 2)如果您像这样预先计算,您可以摆脱iflocation = location-location(1);(第一个元素的偏移量将为0)。 3) hist 已弃用,应使用 histcountsdiscretize 代替。
猜你喜欢
  • 1970-01-01
  • 2021-06-09
  • 2018-10-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-13
  • 1970-01-01
  • 2019-10-21
  • 1970-01-01
相关资源
最近更新 更多