2011-11-06 4 views
3

J'ai deux tableaux de la forme suivante:Matlab: sommation conditionnelle

v1 = [ 1 2 3 4 5 6 7 8 9 ... ] 
c2 = { 'a' 'a' 'a' 'b' 'b' 'c' 'c' 'c' 'c' ... } 

(toutes les valeurs sont que des exemples, aucun modèle ne peut être pris en charge dans les données réelles v1 et c2 ont la même taille.)

Je veux obtenir un vecteur contenant la sommation des composantes de v1 correspondant à des valeurs égales en c2. Dans l'exemple ci-dessus, le premier composant du vecteur résultant serait 1+2+3, le second 4+5, et ainsi de suite.

Je sais que je peux le faire dans une boucle de la forme:

uni_c2 = unique(c2); 
result = zeros(size(uni_c2)); 
for i = 1:numel(uni_c2) 
    result(i) = sum(v1(strcmp(uni_c2(i),c2))); 
end 

Y at-il une seule commande ou d'une manière vectorisée de faire la même opération?

+0

"la sommation des composantes de v1 correspondant à des valeurs égales en c2". valeurs consécutives en c2?Par exemple: v1 = [1 2 3 4], c2 = ['a', 'b', 'a', 'a'], quel est le résultat attendu dans cet exemple? –

Répondre

3

Vous pouvez le faire en deux lignes:

[b, m, n] = unique(c2) 
result = accumarray(n', v1) 

Les éléments de résultat correspondent aux chaînes de la matrice de cellules b.

+0

Super, @Carl, exactement ce que je cherchais. Le seul problème est que, selon mon test, 'n' doit être transposé dans un vecteur de colonne:' result = accumarray (n ', v1) '. Merci pour l'aide. – foglerit

+0

Merci pour le correctif. Je n'ai pas Matlab à la maison, donc je n'ai pas pu tester mon code. Je vais éditer ma réponse –

1

Ceci est vectorisé mais une mauvaise idée pour les très grands vecteurs. Pour certains problèmes, une solution "vectorisée" est pire qu'une boucle for.

>> v1 = [ 1 2 3 4 5 6 7 8 9]; 
>> c2 = 'aaabbcccc'-'a' 
c2 = 
    0 0 0 1 1 2 2 2 2 
>> N = repmat(c2',1,max(c2)-min(c2)+1) == repmat([min(c2):max(c2)],size(c2,2),1); 
>> v1*N 
ans = 
    6 9 30 
+0

Très ingénieux, @stardt! C'est à cause de ces pierres précieuses que j'aime les questions de vectorisation sur le web. Mais votre solution utilise l'hypothèse que c2 suit le modèle dans la question, alors que ce n'était qu'un exemple simplifié (comme indiqué). En réalité, le contenu de ma cellule est beaucoup plus complexe et imprévisible. Des idées sur la façon de généraliser votre approche? – foglerit

+0

@jonnat Le code ne dépend pas du motif. Cela fonctionne avec, par exemple, 'c2 = 'abacaccbc' ​​- 'a'' Quelle supposition votre vrai problème contredit-il? – stardt

+0

mon c2, en fait, contient des éléments qui ne sont pas des lettres simples. J'ai choisi d'utiliser des éléments à lettre unique comme une simplification. – foglerit

0

Je pense qu'un très général (et vectorisé) solution est quelque chose comme ceci:

v1 = [ 1 2 3 4 5 6 7 8 9 ] 
c2 = { 'a' 'a' 'a' 'b' 'b' 'c' 'c' 'c' 'c' } 
uniqueValuesInC2 = unique(c2); 
conditionalSumOfV1 = @(x)(sum(v1(strcmp(c2, x)))); 
result = cellfun(conditionalSumOfV1, uniqueValuesInC2) 

Peut-être que ma solution a besoin d'un peu d'une explication à l'œil non averti:

Alors d'abord vous en fait besoin de calculer les différentes valeurs possibles dans c2, ce qui est fait par unique.

La fonction conditionalSumOfV1 prend un argument x, il compare chaque élément de c2 avec x et sélectionne les éléments correspondants dans v1 et les sommes.

Enfin cellfun est comparable à une foreach construction dans d'autres langues: la fonction conditionalSum est évaluée pour chaque valeur dans le réseau de cellules que vous fournissez (dans ce cas: chaque valeur unique dans c2) et le stocke dans le tableau de sortie. Pour les autres types de variables de conteneur (tableaux, structures), MATLAB a des constructions équivalentes foreach: arrayfun, structfun.

Ceci fonctionnera pour les contenus de c2 qui sont plus longs qu'un seul caractère et qui ne nécessite pas une grande opération repmat comme solution de stardt. J'ai cependant mes doutes quand il s'agit de tableaux longs où c2 a seulement quelques valeurs en double, mais je suppose que ce sera un cas difficile pour la plupart des algorithmes. Si vous êtes dans un tel cas, vous devrez peut-être regarder les sorties supplémentaires de unique ou écrire votre propre alternative à unique (c'est-à-dire écrire des boucles for, de préférence dans une langue compilée/MEX).