Je souhaite diviser ma variable data
en différentes variables a
b
et c
, et appliquer signifie
aux bacs (1ère dimension). Existe-t-il un moyen d'améliorer considérablement (par exemple, un ordre de grandeur 1x) ce code en termes de vitesse? Commentaires généraux bienvenus
data=rand(20,1000); %generate data bins=[5 10 5]; %given size of bins start_bins=cumsum([1 bins(1:end-1)]); end_bins=cumsum([bins]); %split the data into 3 cell arrays and apply mean in 1st dimension binned_data=cellfun(@(x,y) mean(data(x:y,:),1),num2cell(start_bins),num2cell(end_bins),'uni',0); %data (explicitly) has be stored into different variables [a,b,c]=deal(binned_data{:}); whos a b c Name Size Bytes Class Attributes a 1x1000 8000 double b 1x1000 8000 double c 1x1000 8000 double
4 Réponses :
La moyenne peut être appliquée avant le fractionnement, ce qui réduit les données à un vecteur, puis accumarray
peut être utilisé:
ind_rows = repmat(repelem((1:numel(bins)).', bins), 1, size(data,2)); ind_cols = repmat(1:size(data,2), size(data,1), 1); binned_data = sparse(ind_rows, ind_cols, data); binned_data = bsxfun(@rdivide, binned_data, bins(:)); binned_data = num2cell(binned_data, 2).';
mea culpa, j'ai foiré, j'en avais besoin dans la 1ère dimension, mais si je lis correctement votre code (élégant), c'est facilement adaptable. code d'exemple mis à jour
Je dois partir maintenant. Je jetterai un coup d'oeil plus tard
Veuillez voir modifier. En attendant, vous avez également obtenu de très bonnes réponses :-)
Vous pouvez utiliser la multiplication matricielle:
0.386079 seconds
Si vous voulez la sortie sous forme de cellule:
0.0398011 seconds
Pour les grandes matrices, il est préférable de utiliser une matrice creuse:
0.806947 seconds sparse: 0.2331 seconds
Remarque: dans les versions précédentes de MATLAB, vous devriez utiliser bsxfun
:
0.00718904 seconds
Voici le résultat du timing de trois méthodes proposées dans Octave:
Multiplication matricielle:
0.00465298 seconds
Accumarray:
0.00197697 seconds
Cellfun:
result = bsxfun(@times,bsxfun(@eq, r.',repelem(r,bins)) * data , (1./bins(:)))
MODIFIER: Pour une matrice 200 x 100000:
Multiplication de la matrice:
result = sparse(r.' == repelem(r,bins)) * data .* (1./bins(:));
Accumarray:
result = num2cell(result,2);
Cellfun :
r = 1:numel(bins); result = (r.' == repelem(r,bins)) * data .* (1./bins(:));
Vous pourriez avoir besoin d'un exemple plus grand, les résultats de synchronisation seront plus représentatifs si le code prend des secondes pour se terminer (je ne doute pas que la multiplication simple soit la plus rapide).
OK, le résultat est différent pour différentes tailles.
c'est encore au moins un indicateur d'effets temporels très importants. Souhaitez-vous exécuter num2cell
pour convertir les lignes en tableau de cellules et la sortie dans les différentes variables?
répondu à la question principale, il se trouve que c'est une exigence circonstancielle pour moi
quelqu'un peut-il m'aider à faire ce travail dans matlab2016a? Remplacez simplement tous les opérateurs implicites par bsxfun?
Vous pouvez utiliser splitapply
(le petit frère un peu plus sympathique de accumarray
):
% Your example data = rand(20,1000); % generate data bins = [5 10 5]; % given size of bins % Calculation bins = repelem(1:numel(bins), bins).'; % Bin sizes to group labels binned_data = splitapply( @mean, data, bins ); % splitapply for calculation
Les lignes de binned_data
sont votre a
, b
et c
.
Je ne savais pas que splittapply
pouvait fonctionner en ligne (ou en colonne) avec une matrice de données. Bonne trouvaille!
@Luis tant que le vecteur de regroupement (ligne ou colonne en conséquence) correspond à la taille, cela fonctionne en quelque sorte comme votre expansion implicite préférée!
Je vois. J'aime ça :-D
a choisi cette réponse car dans mon cas d'utilisation réel (taille de données 400x50000 et 50x bins) était en fait toujours un peu plus rapide que la solution de multiplication matricielle.
Vous pouvez également utiliser une simple boucle for, je ne vois pas comment une autre fonction peut être plus rapide dans ce cas. La fonction mean
doit en tout cas lire chaque valeur donc ...
for ii = 1:numel(start_bins) res{ii} = mean(data(start_bins(ii):end_bins(ii),:),1); end
Je ne vais pas diviser la cellule en plusieurs variables puisqu'une cellule est destinée exactement pour ça.
@LuisMendo Yups en effet j'ai raté la dimension, merci. Je vais comparer les différentes solutions ce soir pour voir, dans ce cas précis, à quel point une boucle for est mauvaise par rapport à une solution vectorisée.
Y a-t-il une raison pour laquelle vous le souhaitez dans des variables séparées, plutôt qu'une seule matrice avec des lignes différentes ou en le laissant dans le tableau de cellules?
oui ... j'essaie de m'améliorer et d'en apprendre davantage sur une bibliothèque qui nécessite que la matrice soit divisée en un champ de structure. Les superbes extraits de code postés ci-dessous m'illustrent que le choix de conception sur ce point n'était peut-être pas exactement optimal. Le reste de la bibliothèque nécessite ces champs de structure
Une simple boucle for n'est pas une option?
Si vous souhaitez diviser un tableau de cellules en champs d'une structure, vous pouvez utiliser
cell2struct
et ainsi éviter les variables intermédiaires@obchardon une boucle est implémentée, mais je veux emprunter le chemin des démons et optimiser en vectorisant @luis_mendo: désolé de ne pas être clair. C'est un tableau de cellules dans un champ struct. Les variables ci-dessus
a, b, c
représentent de tels champs, je suppose que c'est plus facile à attribuer - soit en boucle - soit en utilisantdeal
mise à jour, bon à savoir: l'utilisation de
sparse ()
ne semble pas fonctionner pour les types de variables int16, ce qui pour moi est le cas et exclut certaines réponsesmise à jour supplémentaire / FYI: apparemment l'utilisation de bsxfun (@times) n'est pas implémentée dans matlab2016a avec int16
Vous pouvez envelopper vos données avec
double ()
lors du passage dans la fonction d'accumulation pour éviter les problèmes de type int16 ... étant donné que vous calculez la moyenne que vous allez finir avec des doubles de toute façon?@Wolfie vous avez absolument raison et c'est un excellent point
@Wolfie J'ai réfléchi à cela, je réduis les données brutes d'origine en extraits de code avant la conversion (vous en inférez correctement que la RAM est la raison pour laquelle le type de données est int16 en premier lieu), cela devrait fonctionner