2011-10-28 8 views
1

J'ai une matrice qui a plusieurs lignes par ID. Je dois extraire seulement. DERNIERES lignes par ID. ID se compose de 2 Col. Col.:Extrait "n" lignes pour chaque ID dans une matrice - MATLAB

mat = [ ... 
     2222 1 734771 0.11 
     2222 1 734772 0.22 
     2222 1 734773 0.33 
     2222 1 734774 0.44 
     2222 1 734775 0.55 
     5555 3 734771 0.11 
     5555 1 734772 0.12 
     5555 1 734773 0.13 
     ] ; 

réponse -> % [2222 1 734 772 0,22; 2222 1 734773 0,33; 2222 1 734774 0,44; 2222 1 734775 0,55]

J'apprécierai une approche vectorisée. Merci et joyeux Halloween!

Répondre

2

Voici une solution efficace en utilisant UNIQUE et ACCUMARRAY fonctions:

%# sample matrix, sorted by first column 
mat = [randi([1 5],[20 1]) (1:20)' rand(20,1)]; %' 
mat = sortrows(mat); 

%# extract last N rows from each unique ID 
N = 4; 
[~,~,subs] = unique(mat(:,1));     %# index into unique values 
fcn = @(x){ x(max(1,end-N+1):end) };    %# at most last N values 
ind = accumarray(subs, 1:numel(subs), [max(subs) 1], fcn); 
ind = cell2mat(ind(cellfun(@numel,ind) >= 4)); %# keep only those with 4+ count 
result = mat(ind,:) 

en utilisant la troisième sortie de la fonction UNIQUE, nous obtenons pour chaque ligne l'index dans la liste des identifiants uniques. Ensuite, nous distribuons les indices de ligne dans des groupes (tableau de cellules) basés sur ces indices. Nous prenons les quatre derniers indices de chaque bin, et filtrons ceux avec moins de 4 occurrences. Nous les combinons ensuite en un seul vecteur d'indices de ligne. Enfin, nous l'utilisons pour obtenir les lignes correspondantes de la matrice d'origine.

En utilisant l'exemple ci-dessus, nous générons la matrice suivante:

mat = 
      1   2  0.70199 
      1   6  0.46313 
      1   7  0.98821 
      1   12  0.15645 
      1   13  0.67037 
      1   16  0.86966 
      2   8  0.63491 
      2   9  0.076501 
      2   15  0.55076 
      2   17  0.44727 
      2   19  0.30587 
      3   5  0.91502 
      3   10  0.97322 
      3   20  0.48231 
      4   3  0.45633 
      4   4  0.12363 
      4   11  0.18319 
      4   14  0.36045 
      5   1  0.36708 
      5   18  0.63084 

et le résultat était:

result = 
      1   7  0.98821 
      1   12  0.15645 
      1   13  0.67037 
      1   16  0.86966 
      2   9  0.076501 
      2   15  0.55076 
      2   17  0.44727 
      2   19  0.30587 
      4   3  0.45633 
      4   4  0.12363 
      4   11  0.18319 
      4   14  0.36045 
+0

Merci Amro. Pour une raison quelconque, votre code ne fonctionne pas correctement pour l'exemple ci-dessus. J'ai modifié fnc unique pour utiliser 2 cols au lieu d'un, mais il continue à ramasser 5555 (qui devrait être abandonné car il a compté <4 lignes). Je vais apprécier. – Maddy

+0

@Maddy: Eh bien, il n'était pas clair à partir de votre description initiale si vous vouliez garder des lignes avec moins de 4 occurrences d'ID uniques .. De toute façon, voir mon édition récente pour le correctif – Amro

+0

merci! J'ai besoin de mieux comprendre accumarray. J'ai conçu une nouvelle approche en utilisant reshape() etc mais votre solution est beaucoup plus courte et élégante. – Maddy

0

Pas la mise en œuvre la plus efficace que vous avez vu, mais il fonctionne:

a = [1 1 1 1 2 2 2 2 3 3 ]; 
b = unique(a); 

for i = 1:length(b) 
    c = b(i); 
    ind = find(a==c); 
    last4 = ind(end-1:end) %adjust how many elements you want 
end 

De toute évidence, si vous avez une matrice que vous auriez à ajouter l'index de colonne si les choses que vous se soucient de

0
X=[]; 
id=ones(size(mat,1),1)==1; 
for n=1:4 
    [~, m, ~]=unique(mat(id,1:2),'rows'); 
    X=[X; mat(m,:)]; 
    id(m)=0; 
end 
return X 

(Dans votre exemple réponse que vous avez retourné aucun des 5555 lignes, mais je suppose que vous voulez.)

+0

Salut Cyborg, merci pour la réponse. J'ai omis '5555' parce qu'il avait <4 lignes comme désiré dans l'exemple! – Maddy

Questions connexes