Использование bsxfun с одноэлементным расширением с матрицами трех измерений
Я использую bsxfun
векторизовать операцию с одноэлементным расширением между матрицами размеров:
MS: (nms, nls)
KS: (nks, nls)
Операция представляет собой сумму абсолютных разностей между каждым значением MS(m,l)
с m
в 1:nms
а также l
в 1:nls
и каждый KS(k,l)
с k
в 1:nks
,
Я достигаю этого через код:
[~, nls] = size(MS);
MS = reshape(MS',1,nls,[]);
R = sum(abs(bsxfun(@minus,MS,KS)));
R
имеет размер (nls, nms)
,
Я хочу обобщить эту операцию для списка образцов, поэтому новые размеры будут:
MS: (nxs, nls, nms)
KS: (nxs, nls, nks)
Это может быть легко достигнуто с помощью цикла for, который выполняет первый фрагмент кода для каждой 2-мерной матрицы, но я подозреваю, что производительность может быть намного лучше, если обобщить предыдущий код, добавив новое измерение.
R
имеет размер: (nxs, nls, nms)
Я пытался изменить MS
до 4-х измерений без успеха. Может ли это быть сделано с изменением формы и bsxfun
?
1 ответ
Вам может понадобиться это:
% generate small dummy data
nxs = 2;
nls = 3;
nms = 4;
nks = 5;
MS = rand(nxs, nls, nms);
KS = rand(nxs, nls, nks);
R = sum(abs(bsxfun(@minus,MS,permute(KS,[1,2,4,3]))),4)
Это даст матрицу размера [2,3,4]
т.е. [nxs,nls,nms]
, Каждый элемент [k1,k2,k3]
будет соответствовать
R(k1,k2,k3) == sum_k abs(MS(k1,k2,k3) - KS(k1,k2,k))
Например, в моем случайном прогоне
R(2,1,3)
ans =
1.255765020150647
>> sum(abs(MS(2,1,3)-KS(2,1,:)))
ans =
1.255765020150647
Хитрость заключается в том, чтобы ввести одноэлементные размеры с permute
: permute(KS,[1,2,4,3])
имеет размер [nxs,nls,1,nks]
, в то время как MS
размера [nxs,nls,nms]
неявно также имеет размер [nxs,nls,nms,1]
: предполагается, что каждый массив в MATLAB обладает счетным бесконечным числом конечных одноэлементных измерений. Отсюда легко увидеть, как вы можете bsxfun
вместе массивы размера [nxs,nls,nms,1]
а также [nxs,nls,1,nks]
соответственно получить один с размером [nxs,nls,nms,nks]
, Суммирование по измерению 4 запечатывает сделку.
Я отметил в комментарии, что это может быть быстрее permute
индекс суммирования должен быть на первом месте. Оказывается, это само по себе замедляет выполнение кода. Однако за счет изменения размеров массивов в уменьшающиеся размерные размеры общая производительность увеличивается (благодаря оптимальному доступу к памяти). Сравните это:
% generate larger dummy data
nxs = 20;
nls = 30;
nms = 40;
nks = 500;
MS = rand(nxs, nls, nms);
KS = rand(nxs, nls, nks);
MS2 = permute(MS,[4 3 2 1]);
KS2 = permute(KS,[3 4 2 1]);
R3 = permute(squeeze(sum(abs(bsxfun(@minus,MS2,KS2)),1)),[3 2 1]);
То, что я сделал, это положил суммирование nks
Размер на первое место, и упорядочить остальные размеры в порядке убывания. Это может быть сделано автоматически, я просто не хочу переусердствовать с примером. В вашем случае вы, вероятно, в любом случае будете знать величину размеров.
Время выполнения с двумя вышеуказанными кодами: 0, 07028 с для оригинала, 0, 051162 с для переупорядоченного (лучший из 5). Большие примеры, к сожалению, не вписываются в мою память.