J'ai une liste de coordonnées (x, y) et j'ai besoin de trouver l'index de la première et de la dernière occurrence de chaque coordonnée de la liste. Exemple (dans mon use-cast j'ai ~ 30M de coordonnées):
ind = sub2ind(size(Mlast),y-y1+1, x-x1+1); t = (1:length(x))'; Mlast(ind) = t; Mfirst = ???
Je l'ai implémenté en utilisant une matrice et une boucle, ça ressemble à ça, mais c'est lent:
XXX
J'ai essayé de vectoriser tout le processus, mais je ne réussis qu'avec Mlast:
x1 = min(x);
y1 = min(y);
x2 = max(x);
y2 = max(y);
tic
Mlast = zeros(y2-y1+1, x2-x1+1);
Mfirst = Mlast;
ind = sub2ind(size(Mlast),y-y1+1, x-x1+1);
for i1=1:length(ind)
first = Mfirst(ind(i1));
if first == 0
first = i1;
end
Mlast(ind(i1)) = i1;
Mfirst(ind(i1)) = first;
end
Y a-t-il un moyen d'obtenir cela pour le premier occurrence aussi?
3 Réponses :
La fonction unique peut faire que:
[~, b, c] = unique([x(:) y(:)], 'rows', 'first'); first = b(c).'; [~, b, c] = unique([x(:) y(:)], 'rows', 'last'); last = b(c).';
En effet cela fonctionne, mais c'est env. 20x plus lent que la version avec la boucle
Bizarre; Je me serais attendu à ce que unique soit assez rapide
@LuisMendo: Oui, mon mal.
En supposant que les coordonnées sont des entiers positifs et en particulier lorsque la plage de coordonnées est petite, vous pouvez utiliser accumarray :
ifirst = accumarray(ind(:), 1:numel(ind), [], @min,[],1); ilast = accumarray(ind(:), 1:numel(ind), [], @max,[],1);
Pour les plages plus élevées, vous pouvez utiliser l'option sparse:
x1 = min(x); y1 = min(y); x2 = max(x); y2 = max(y); sz = [y2-y1+1, x2-x1+1]; ind = sub2ind(sz,y-y1+1, x-x1+1); ifirst = accumarray(ind(:), 1:numel(ind), [], @min); ilast = accumarray(ind(:), 1:numel(ind), [], @max); Mfirst = ifirst(ind); Mlast = ilast(ind);
Ça marche! Mais c'est environ 30 fois plus lent que l'implémentation avec la boucle. Mais intéressant à apprendre sur accumarray ...
Si vous avez 30M points, vous n'avez probablement pas assez de mémoire pour cette méthode ... mais c'est assez rapide pour les petits tableaux
x = [1 3 7 1 3];
y = [5 1 6 5 1];
g = findgroups( x, y );
first = zeros( size( x ) );
last = first;
for ii = 1:max(g)
idx = (ii==g);
first( idx ) = find( idx, 1, 'first' );
last( idx ) = find( idx, 1, 'last' );
end
Modifier Vous pouvez également le faire avec findgroups , et en boucle sur les coordonnées uniques au lieu de chaque coordonnée pour avoir une boucle potentiellement beaucoup plus courte ...
x = [1 3 7 1 3]; y = [5 1 6 5 1]; xy = cat( 3, x, y ); chk = all( xy == permute( xy, [2 1 3] ), 3 ); [~,first] = max( chk ); [~,last] = max( flipud( chk ) ); last = size(chk,1) - last + 1;
find (x == y, 1, 'first')trouve la première entrée confirmant le critère. D'autres fonctions utiles peuvent êtreunique ()