2
votes

Renvoie la valeur maximale de chaque sous-groupe dans une table

J'ai une section de tableau comme celle-ci:

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 
%}

Pour chaque groupe de lignes ayant le même X , je dois sélectionner uniquement la ligne ayant le plus grand Valeur . Comment puis-je générer une table comme celle-ci?

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

Le code que j'ai jusqu'à présent ne produit qu'une seule ligne:

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
… … …

p >


2 commentaires

Au lieu d'avoir Y comme vecteur, essayez peut-être de créer une matrice 2D. Chaque ligne (ou colonne si vous le souhaitez) correspondrait à une valeur de X. Ensuite, vous pouvez essayer d'utiliser max () sur cette matrice.


La fonctionnalité modifier n'est pas destinée à discuter des problèmes que vous rencontrez avec les solutions proposées. Veuillez utiliser les commentaires à cet effet ou poser une nouvelle question.


3 Réponses :


3
votes

Si vous utilisez R2015b ou une version plus récente, vous pouvez utiliser splitapply :

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, :);

L'appel de ceci produit le résultat souhaité:

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

Une autre variante utilise le 2 nd sortie de splitapply (@max, ...) qui est l'indice du maximum au sein du groupe. Nous devons ensuite ajouter la quantité d'éléments des groupes précédents à cela (cela se fait en utilisant diff):

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,:);


7 commentaires

Est-il possible de le faire sans utiliser les fonctions out and outrow ?. De plus, j'ai changé la ligne splitapply en out = splitapply (@maxrow, [T.X T.Y T.Value], T.X); et ajoutez au-dessus T = table (X, Y, Value) ;. Comment stocker les ans en tant que nouvelle table?


Quelque chose comme ceci: T2 = array2table (out, 'VariableNames', T .Properties.VariableNames) .


Merci. Ajoutez la ligne T2 = array2table (out, 'VariableNames', {'X', 'Y', 'Value'}) après la ligne out = ... Il ne le stocke pas dans l'espace de travail, pourquoi?


Je ne sais pas ce que vous faites exactement, donc c'est presque impossible de le savoir. Si vous utilisez une fonction pour faire cela, comme dans mon exemple, et que la sortie de la fonction est out (et non T2 ), vous n'obtiendrez pas un sortie différente sauf si vous redéfinissez out ou définissez la sortie sur T2 . Si cela ne résout pas votre problème et que vous souhaitez l'expliquer plus en détail, n'hésitez pas à me contacter via chat .


Merci. J'ai mis à jour le code dans la question. Peut-être que vous pouvez voir ce qui ne va pas.


@jane c'est exactement comme je l'ai dit. Vous avez une mauvaise sortie de la fonction. Une façon de le résoudre est de changer T2 = array2table ... en out = array2table ... . Mais plus important encore, vous devriez lire comment les fonctions fonctionnent dans MATLAB ( entrées, sorties, etc.) parce que votre dernière question indique que vous avez du mal avec certains concepts généraux.


continuons cette discussion dans le chat .



0
votes

Utilisez unique et cum

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 
    %}


3 commentaires

J'ai réfléchi à cela aussi, mais une telle approche échoue si nous avons des valeurs répétitives dans différents groupes, où un maximum dans un groupe existe également dans un groupe précédent, mais ce n'est pas le maximum là-bas (c'est-à-dire essayez ceci avec Value = [16; 17; 18; 7.2; 11.7; 16; 22.6; 20.5; 18.1]; et voyez ce qui se passe). C'est exactement pourquoi j'ai décidé de renvoyer des lignes entières et non des index de lignes. Cette solution fonctionne pour la plupart (mais pas toutes ) les entrées, ce qui rend le débogage dangereux et difficile par la suite.


@ Dev-iL vérifiez celui-ci, pensez que cela peut fonctionner parfaitement


L'idée semble correcte. Plusieurs commentaires / suggestions: 1) Vous semblez avoir ré-implémenté splitapply - si vous voulez voir comment cela peut être fait en utilisant splitapply , consultez mon (maintenant mis à jour) répondre. 2) Vous pouvez vous débarrasser du if si vous précalculez comme ceci: location = location-location (1); (le décalage du premier élément sera 0 ). 3) hist est obsolète, il faut utiliser à la place histcounts ou discretize .



0
votes

Vous pouvez le faire en utilisant une boucle sur des valeurs X uniques:

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

Sortie:

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,:)

p >


2 commentaires

Malheureusement, vous ne pouvez pas voir mon commentaire sur la réponse maintenant supprimée, je vais donc l'écrire à nouveau: l'utilisation de find est problématique car une telle approche échoue si nous avons des valeurs répétées dans différents groupes, où le maximum de un groupe existe également dans un groupe précédent, mais ce n'est pas le maximum ici (c'est-à-dire essayez ceci avec Value = [16; 17; 18; 7.2; 11.7; 16; 22.6; 20.5; 18.1]; et voyez ce qui se passe). C'est exactement pourquoi j'ai décidé de renvoyer des lignes entières et non des index de lignes. Cette solution fonctionne pour la plupart des entrées (mais pas toutes), ce qui rend le débogage dangereux et difficile par la suite.


@ Dev-iL: Oui, vous avez raison, j'ai supprimé splitapply de ma réponse.