Get matrix consisting of every subsquare - arrays

I wish to obtain a matrix consisting of every 3x3 square within a matrix. The matrix is a #channels by m by n. The end result is a matrix of size #channels x (size1-2)*(size2-2) x 9I can do this in a non-vector fashion via the following code:
clear
size1 = 10;
size2 = 10;
matrix = 1:size1*size2;
matrix =reshape(matrix,[size1 size2]);
matrix_withdraw = 1:(88*size1*size2);
matrix_withdraw = reshape(matrix_withdraw,[88 size1 size2]);
tic
iter = 1;
for ii = 1:size1-2
for jj = 1:size2-2
locs(ii,jj,:,:) = matrix(ii:ii+2,jj:jj+2);
method1(:,iter,:) = reshape(matrix_withdraw(:,ii:ii+2,jj:jj+2),[88,9]);
iter = iter+1;
end
end
locs = permute(locs,[3 4 1 2]);
This is obviously fairly slow for large sizes of matrices. I'm looking to vectorize this. I managed to obtain a solution which works but it's not very clean
locs2 = ones(size1*(size2-2),1)+(0:size1*(size2-2)-1)';
temp = 1:size1*(size2-2);
temp = mod(temp,size1);
temp = (temp>(size1-2)) | (temp==0);
locs2(temp,:) = [];
locs3 = reshape(locs2,[1 1 (size1-2) (size2-2)]);
locs3(2,1,:,:) = reshape(locs2+1,[1 1 (size1-2) (size2-2)]);
locs3(3,1,:,:) = reshape(locs2+2,[1 1 (size1-2) (size2-2)]);
locs3(:,2,:,:) = locs3(:,1,:,:)+size1;
locs3(:,3,:,:) = locs3(:,2,:,:)+size1;
locs3 = permute(locs3,[1 2 4 3]);
locs3 = vec(locs3);
method2 = matrix_withdraw(:,locs3);
method2 = reshape(method2,[88,9,64]);
method2 = permute(method2,[1 3 2]);
Method1 and method2 are equivalent and render the exact same results. Method2 is also 10x faster than method1. My question is, is there a cleaner way to do this?

If you have the Image Processing Toolbox, you can use im2col to get each block as a column, and then reshape as a 4-D array:
blockSize = [3 3];
locs = reshape(im2col(matrix, blockSize), [blockSize size(matrix)-blockSize+1]);
Or you can do it directly, building a 4-D indexing array using implicit singleton expansion. This doesn't require any toolboxes:
m = 3; n = 3; % block size
[M, N] = size(matrix);
ind = (1:m).'+(0:n-1)*M + reshape(0:M-m, 1, 1, []) + reshape((0:N-n)*M, 1, 1, 1, []);
locs = matrix(ind);

Related

Summing partitions of an array in matlab

Suppose I have an array of length 15
x = randi([0 5], 1,15);
I want to sum every 3 elements of x together and put each sum in a new array called y, as in the following:
y = [y1 y2 y3 y4 y5];
Please help me in doing that in Matlab using for loops.
Here's a vectorized approach that automatically deals with a possible smaller last chunk:
x = randi([0 5], 1, 15); % example data
N = 3; % chunk size
y = accumarray(ceil((1:numel(x))/N).', x(:));
you can use reshape as follows:
y = sum(reshape(x,3,[]))
This reshapes your vector x to an array 3 by whatever is left, then sum along right dimension...
For the case the # of elements you want to sum doesnt add up to the total length of the vector, you can pad with zeros or NaN at the end to make it work. Here's I chose adding zeros:
x = randi([0 5], 1,15);
n = 4 ; % sum every n elements (which is the number of rows in the reshape)
try
y = sum(reshape(x, n, []));
catch
disp('added trailing zeros!')
x(numel(x) + (n - mod(numel(x), n))) = 0;
y = sum(reshape(x, n, []));
end
(you can do this with an if condition instead, I just like try catch more here)
Using for loops:
y = zeros(1,5);
for i = 1:5
idx = (i-1)*3 + 1:(i-1)*3 + 3;
y(i) = sum(x(idx));
end
Using a reference variable Target that is used to indicate the start position of each partition the loop below can be achieved. If you would only like to use only loops an alternative inner loop can be done. This method works almost on the same premise as windowing.
Method 1: Single For-Loop with Indexing
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Index = 1;
for Target = 1: +3: 15
Partition = x(Target:Target+2);
y(1,Index) = sum(Partition);
Index = Index + 1;
end
Method 2: Outer and Inner For-Loops
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Partition = zeros(1,3);
Index = 1;
for Target = 1: +3: 15
for Column = 1: +1: 3
Partition(1,Column) = x(1,Target+Column-1);
end
y(1,Index) = sum(Partition);
Index = Index + 1;
end

Accessing multiple elements in a 3d matrix in matlab

val(:,:,1) =
0.1068 0.7150 0.6987 0.5000
0.6538 0.9037 0.1978 0.4799
0.4942 0.8909 0.0305 0.9047
0.7791 0.3342 0.7441 0.6099
val(:,:,2) =
0.6177 0.1829 0.4899 0.5005
0.8594 0.2399 0.1679 0.4711
0.8055 0.8865 0.9787 0.0596
0.5767 0.0287 0.7127 0.6820
val(:,:,3) =
0.0424 0.8181 0.6596 0.8003
0.0714 0.8175 0.5186 0.4538
0.5216 0.7224 0.9730 0.4324
0.0967 0.1499 0.6490 0.8253
Row Col
4 1
1 2
3 3
Hi, i want to take multiple points from a 3d array, but i don't know any efficient methods without using loops. I've tried messing around with sub2ind but it doesn't seem very effective to do each 2d matrix separately.
If you want all values of the data in the third dimension you can easily acess it with:
Rows = [4, 1, 3]
Col = [1, 2 ,3]
val(Rows,Col,:)
Output:
>> val([4, 1, 3],[1, 2, 3],:)
ans(:,:,1) =
0.7791 0.3342 0.7441
0.1068 0.7150 0.6987
0.4942 0.8909 0.0305
ans(:,:,2) =
0.5767 0.0287 0.7127
0.6177 0.1829 0.4899
0.8055 0.8865 0.9787
ans(:,:,3) =
0.0967 0.1499 0.6490
0.0424 0.8181 0.6596
0.5216 0.7224 0.9730
You can obtain the desired output with:
x1 = [4,1,3];
x2 = [1,2,3];
x = val(x1,x2,:);
for ii = 1:size(val,3)
res(:,:,ii) = diag(x(:,:,ii)).';
end
Output:
res =
ans(:,:,1) =
0.779100 0.715000 0.030500
ans(:,:,2) =
0.57670 0.18290 0.97870
ans(:,:,3) =
0.096700 0.818100 0.973000
Or with sub2ind, and implicit expansion (matlab 2016b and newer)
x1 = [4,1,3];
x2 = [1,2,3];
len = length(x1);
ind = sub2ind(size(val),x1+zeros(len,1),x2+zeros(len,1),[1:size(val,3)].'+zeros(1,len));
res = A(ind)
%if you want to preserve the third dimension add: permute(res,[1,3,2])
%we can do that because an array in matlab has an infinite number of singleton dimension
Output:
res =
0.779100 0.715000 0.030500
0.576700 0.182900 0.978700
0.096700 0.818100 0.973000

Change diagonals of an array of matrices

I have an application with an array of matrices. I have to manipulate the diagonals several times. The other elements are unchanged. I want to do things like:
for j=1:nj
for i=1:n
g(i,i,j) = gd(i,j)
end
end
I have seen how to do this with a single matrix using logical(eye(n)) as a single index, but this does not work with an array of matrices. Surely there is a way around this problem. Thanks
Use a linear index as follows:
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
s = size(g); % size of g
ind = bsxfun(#plus, 1:s(1)+1:s(1)*s(2), (0:s(3)-1).'*s(1)*s(2)); % linear index
g(ind) = gd.'; % write values
Result:
>> g
g(:,:,1) =
1.000000000000000 0.483437118939645 0.814179952862505
0.154841697368116 2.000000000000000 0.989922194103104
0.195709075365218 0.356349047562417 3.000000000000000
g(:,:,2) =
4.000000000000000 0.585604389346560 0.279862618046844
0.802492555607293 5.000000000000000 0.610960767605581
0.272602365429990 0.551583664885735 6.000000000000000
Based on Luis Mendo's answer, a version that may perhaps be more easy to modify depending on one's specific purposes. No doubt his version will be more computationally efficient though.
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
sz = size(g); % Get size of data
sub = find(eye(sz(1))); % Find indices for 2d matrix
% Add number depending on location in third dimension.
sub = repmat(sub,sz(3),1); %
dim3 = repmat(0:sz(1)^2:prod(sz)-1, sz(1),1);
idx = sub + dim3(:);
% Replace elements.
g(idx) = gd;
Are we already playing code golf yet? Another slightly smaller and more readable solution
g = rand(3,3,2);
gd = [1 4; 2 5; 3 6];
s = size(g);
g(find(repmat(eye(s(1)),1,1,s(3))))=gd(:)
g =
ans(:,:,1) =
1.00000 0.35565 0.69742
0.85690 2.00000 0.71275
0.87536 0.13130 3.00000
ans(:,:,2) =
4.00000 0.63031 0.32666
0.33063 5.00000 0.28597
0.80829 0.52401 6.00000

Matlab array multiplication after linear indexing

I have 2 matrices defined as follows:
A=[1 2;3 4]
B=[1 4; 5 3]
Then I define Aensem, Bensem and Gensem like this:
Arow=A(:);
Brow=B(:);
Aensem=repmat(Arow,1,10);
Bensem=repmat(Brow,1,10);
G=A*B;
Grow=G(:);
Gensem=repmat(Grow,1,10);
I need to create a function that can calculate any Gensem-like arrays directly from Aensem and Bensem. I only have knowledge of Aensem and Bensem. I tried the following method, but it's not working:
function ret = mat_mult(v1, v2, r)
ret = zeros(size(v1));
for i = 1:2*r.c.M
for j = 1:2*r.c.M
sum = 0;
for k = 1:2*r.c.M
sum = sum + ...
v1(idx1(i,k,r),:) .* v2(idx1(k,j,r),:);
ret(idx1(i,j,r),:)=sum;
end
end
end
end
If I understand your question correctly, you want to calculate Gensem directly from Aensem and Bensem. This can be done as follows:
A_ = reshape(Aensem(:, 1), 2, 2); % extract A from Aensem
B_ = reshape(Bensem(:, 1), 2, 2); % extract B from Bensem
G_ = A_*B_; % calculate G based on the extracted A and B
Gensem_ = repmat(G_(:),1,10); % build Gensem

Insert elements to the matrix using index

I would like to add the elements to the array using their index.
array_in = [1 5 6 8 9];
index = [2 4];
newElements = [25 67];
index = index + (0:length(index)-1);
expected output:
array_out = [1 25 5 6 67 8 9];
1.using for loop:
tmp = array_in;
for idx = 1:length(index)
array_out = cat(2,tmp(1:index(idx)-1),newElements(idx),tmp(index(idx):end));
tmp = array_out;
end
2.The code for Calling a Function Using Its Handle:
fcn_Insert = #(a, x, n) cat(2, x(1:n-1), a, x(n:end));
array_out = fcn_Insert(newElements,array_in,index)
The above code.2 (function) is not working? can any one suggest the solution. Are there any other better solutions?
Here's a sort-based approach:
array_out = [array_in newElements]; %// append new to old
[~, ind] = sort([1:numel(array_in) index-.5]); %// determine new order needed
array_out = array_out(ind); %// apply that order
I was working on this before Luis's answer was accepted. It ran over twice as fast, if people are interested.
function array_out = Insert(array_in, index, values)
array_out = zeros(length(array_in)+length(index), 1);
oldIndex = ~ismember(1:length(array_out), index);
array_out(index) = values;
array_out(oldIndex) = array_in;
end

Resources