Fastest way to compute sum of a submatrix - matlab

What's the fastest way of computing the sum of a submatrix? Currently I'm going about this in the following way:
x = [1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4]
>> y = x(1:2, 1:2);
>> y
y =
1 2
1 2
>> sum(y(:))
ans =
6
>>
How can I compute the sum of all submatrices of x?
Edit
At each point (x,y) in the matrix, I want to compute the sum for a window of size 2M+1. For example, if I have a 4x4 matrix I want to compute the sums for 3x3 windows at each point where this is possible (e.g. it wouldn't be possible for the edges because the window would spill off the matrix)
for i = M:ncols-M
for j=M:nrows-M
end
end
Example
For a 4x4 matrix with a window size of 3, I would want these sums:
sum centered at (2,2)
+ + + x
+ + + x
+ + + x
x x x x
sum centered at (3,2)
x + + +
x + + +
x + + +
x x x x
sum centered at (2,3)
x x x x
+ + + x
+ + + x
+ + + x
sum centered at (3,3)
x x x x
x + + +
x + + +
x + + +

You're looking at computing a summed area table. Simply put, each element at row i and column j - (i,j) - in the output is the sum of the submatrix bounded from the top-left corner to the point of interest (i,j) which serves as the bottom right corner.
It's very simple with cumsum:
y = cumsum(cumsum(x, 1), 2);
Example:
>> x = repmat(1:4, 4, 1); %// your example
>> x
x =
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
... and the output:
>> y = cumsum(cumsum(x, 1), 2)
y =
1 3 6 10
2 6 12 20
3 9 18 30
4 12 24 40
As such, for your example, the submatrix sum from the top left corner to (i,j) = (2,2) is equal to 6.

If by "submatrix" you mean a sliding 2D-window of fixed size, that's essentially a 2D-convolution:
x = [1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4];
m = 2; n = 2; %// submatrix size
result = conv2(x, ones(m, n), 'valid');
In this example,
result =
6 10 14
6 10 14
6 10 14

This answer was created before clarifications by the OP. It assumes the submatrices do not overlap!
If you have the Image Processing Toolbox you can use blockproc:
A = blockproc(x,[2 2],#(k) sum(k.data(:)))
If you don't have it you can use accumarray:
n = 2; %// number of elements per block in row direction
N = size(x,1)/n; %// number of blocks in row direction
m = 2; %// number of elements per block in column direction
M = size(x,2)/m; %// number of blocks in column direction
idx = repelem( reshape(1:(N*M),N,M),n,m)
B = reshape( accumarray(x(:),idx(:)), N, M )
Some heavy reshaping will work too:
C = reshape(sum(reshape(permute(sum(reshape(x,n,m,[])),[2,3,1]).',n,[])),n,[]).'
A = B = C =
6 14
6 14

x = repmat(1:4, 4, 1); %// your example
[A,B] = size(x);
kk=1;
for ii = 1:2:A
for jj = 1:2:B
subsummed(kk) = sum(sum(x(ii:ii+1,jj:jj+1)));
kk = kk+1;
end
end
My looped version, in case you mean a "submatrix" in case of the four 2x2 matrices that make up your full one.

Related

How to merge elements in a matrix

x = [1 2 3 4 5
1 2 3 0 0];
I want to get off all zeros and merge the two rows
newx = x(:,1) + x(:,2)(nonzeros)
= [1 2 3 4 5 1 2 3];
nonzeros will give you nonzeros in a column vector, you just have to orient the original matrix properly and transpose into a row vector (if that's what you want):
>> newx = nonzeros(x.').'
newx =
1 2 3 4 5 1 2 3
newx=x.'; %Taking Transpose
% Converting the given matrix into a column vector and then taking transpose again
% (since you require answer as a row vector)
newx=newx(:).' ;
newx(newx==0)=[] %Removing zeros
or using reshape:
newx = reshape(x.',1,[])
newx(newx==0)=[] %Removing zeros
Result:
newx =
1 2 3 4 5 1 2 3

Classifying multivariate data and giving new coordinate

Consider a below data set
Obs y x z
1 3 10 1
2 0 12-1
3 4 9 3
4 2 15 0
y is a dependent variable and the others are explanatory variables
I want to give total 4 observations a new coordinates based on some conditions, for example,
If y is in [0,3) give 1 to that y,
or if y is in [3,6), give 2 to that y.
Likewise,
If x is in [9,12), give 1 to that x,
or if x is in [12,16), give 2 to that x,
And do the similar for z.
As a result,
Obs y x z coordinate
1 3 10 1 (1,1,1)
2 0 12 1 (1,1,1)
3 4 9 3 (2,1,2)
4 2 15 0 (1,2,1)
I need these new coordinates as vectors for 4 observations.
I might be able to do this by 'loop' command, but it is too time consuming.
So I need to do this without 'loop' but with some commands related to vector.
Does anybody know how to do this?
While Dan's answer will work well if you only have two values for each coordinate based on a logical expression, if you have more complex logical requirements then I think that you want something similar to the following (which can easily be extended to cover more cases):
y = [ 3 0 4 2 ]';
x = [ 10 12 9 15 ]';
z = [ 1 1 3 0 ]';
coordinate = zeros(length(x), 3);
coordinate(y >= 0 & y < 3, 1) = 1;
coordinate(y >= 3 & y < 6, 1) = 2;
coordinate(x >= 9 & x < 12, 2) = 1;
coordinate(x >= 12 & x < 16, 2) = 2;
coordinate(z >= 0 & z < 3, 3) = 1;
coordinate(z >= 3 & z < 6, 3) = 2;
coordinate
results in
coordinate =
2 1 1
1 2 1
2 1 2
1 2 1
Where you can read each row off, for example coordinate(1, :) to get the first set of coordinates.
This also has the advantage that you can see where none of your rules match because the element in the coordinate matrix will be 0. You could alternatively use nan instead of zeros to create the coordinate matrix.
You can do this easily using logical indexing, in fact, this is pretty much answered here: Change elements of matrix based on condition
n=4;
coordinate = zeros(n,3);
%// y
coordinate(:,1) = (y > 3) + 1
%// x
coordinate(:,2) = (x > 12) + 1
etc...

creating diagonal matrix with selected elements

I have a 4x5 matrix called A from which I want to select randomly 3 rows, then 4 random columns and then select those elements which coincide in those selected rows and columns so that I have 12 selected elements.Then I want to create a diagonal matrix called B which will have entries either 1 or 0 so that multiplication of that B matrix with reshaped A matrix (20x1) will give me those selected 12 elements of A.
How can I create that B matrix? Here is my code:
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
It's a little hard to understand what you want and your code isn't much help but I think I've a solution for you.
I create a matrix (vector) of zeros of the same size as A and then use bsxfun to determine the indexes in this vector (which will be the diagonal of B) that should be 1.
>> A = reshape(1:20, 4, 5);
>> R = [1 2 3]; % Random rows
>> C = [2 3 4 5]; % Random columns
>> B = zeros(size(A));
>> B(bsxfun(#plus, C, size(A, 1)*(R-1).')) = 1;
>> B = diag(B(:));
>> V = B*A(:);
>> V = V(V ~= 0)
V =
2
3
4
5
6
7
8
9
10
11
12
13
Note: There is no need for B = diag(B(:)); we could have simply used element by element multiplication in Matlab.
>> V = B(:).*A(:);
>> V = V(V ~= 0)
Note: This may be overly complex or very poorly put together and there is probably a better way of doing it. It's my first real attempt at using bsxfun on my own.
Here is a hack but since you are creating y2 you might as well just use it instead of creating the useless B matrix. The bsxfun answer is much better.
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
idx = reshape(y2 ~= 0, numel(y2), []);
B = diag(idx);
% "diagonal" matrix 12x20
B = B(all(B==0,2),:) = [];
output = B * A(:)
output =
1
3
4
9
11
12
13
15
16
17
19
20
y2 from example.
y2 =
1 0 9 13 17
0 0 0 0 0
3 0 11 15 19
4 0 12 16 20

Rotate the rows of a matrix (like GAUSS function rotater)

I'm currently bringing some GAUSS code over to Matlab and I'm stuck trying to use the GAUSS "rotater" function.
The command reference entry for rotater says:
Purpose Rotates the rows of a matrix
Format y = rotater(x,r)
Input x: N x K matrix to be rotated. r: N x 1 or 1 x 1 matrix specifying the amount of rotation.
Output y: N x K rotated matrix.
Remarks The rotation is performed horizontally within each row of the matrix. A positive rotation value will cause the elements to move
to the right. A negative rotation will cause the elements to move to
the left. In either case, the elements that are pushed off the end of
the row will wrap around to the opposite end of the same row. If the rotation value is greater than or equal to the number of columns in x, then the rotation value will be calculated using (r % cols(x)).
Example 1
(I'm following Matlab's notation here, with straight brackets for matrices and a semicolon for a new ro)
If x = [1 2 3; 4 5 6], and r = [1; -1],then y = [3 1 2; 5 6 4]
Example 1
If x = [1 2 3; 4 5 6; 7 8 9; 10, 11, 12], and r = [0; 1; 2; 3], then y = [1 2 3; 6 4 5; 8 9 7; 10 11 12]
Maybe someone has found a function like that somewhere or can give me advice how to write it?
This can be done using bsxfun twice:
Compute rotated row indices by subtracting r with bsxfun and using mod. As usual, mod needs indices starting at 0, not 1. The rotated row indices are left as 0-based, because that's more convenient for step 2.
Get a linear index from columns and rotated rows, again using bsxfun. This linear index applied to x gives y:
Code:
[s1 s2] = size(x);
rows = mod(bsxfun(#plus, 1:s2, -r(:))-1, s2); % // step 1
y = x(bsxfun(#plus, rows*s1, (1:s1).')); %'// step 2
circshift is pretty close to what you're looking for except that 1) it works on columns rather than rows, and 2) it shifts the entire matrix by the same offset.
The first one is easy to fix, we just transpose. For the second one I haven't been able to find a vectorized approach, but in the meantime, here's a version with a for loop:
x = [1 2 3; 4 5 6; 7 8 9; 10 11 12]
r = [0 1 2 3]
B = x'
C = zeros(size(B));
for ii = 1:size(B,2)
C(:,ii) = circshift(B(:,ii),r(ii));
end
y = C'
The output is:
x =
1 2 3
4 5 6
7 8 9
10 11 12
r =
0 1 2 3
B =
1 4 7 10
2 5 8 11
3 6 9 12
y =
1 2 3
6 4 5
8 9 7
10 11 12
This can be done using a simple for loop to iterate over each row, and a function called 'circshift' from matlab.
I created a function the goes through each row and applies the appropriate shift to it. There may be more efficient ways to implement this, but this way works with your examples. I created a function
function rotated_arr = GaussRotate(input_array, rotation_vector)
[N,K] = size(input_array)
%creates array for return values
rotated_arr = zeros(N,K);
%if the rotation vector is a scalar
if (length(rotation_vector) == 1)
%replicate the value once for each row
rotation_vector = repmat(rotation_vector, [1,N]);
end
%if the rotation vector doesn't have as many entries as there are rows
%in the input array
if (length(rotation_vector) ~= N)
disp('ERROR GaussRotate: rotation_vector is the wrong size')
disp('if input_Array is NxK, rotation_vector must be Nx1 or 1x1')
return
end
%for each row
for idx=1:size(input_array,1)
%shift the row by the appropriate number of columns
%we use [0,shift] because we want to shift the columns, the row
%stays where it is (even though this is a 1xN at this point we
%still specify rows vs columns)
rotated_arr(idx,:) = circshift(input_array(idx,:),[0,rotation_vector(idx)]);
end
end
then simply called it with your examples
x = [1 2 3; 4 5 6];
r = [1; -1];
y = GaussRotate(x,r)
%produces [3 1 2; 5 6 4]
%I also made it support the 1x1 case
r = [-1]
%this will shift all elements one column to the left
y = GaussRotate(x,r)
%produces [2 3 1; 5 6 4]
x = [1 2 3; 4 5 6; 7 8 9; 10, 11, 12]
r = [0; 1; 2; 3]
y = GaussRotate(x,r)
%produces [1 2 3; 6 4 5; 8 9 7; 10 11 12]

Creating Matrix with a loop in Matlab

I want to create a matrix of the following form
Y = [1 x x.^2 x.^3 x.^4 x.^5 ... x.^100]
Let x be a column vector.
or even some more variants such as
Y = [1 x1 x2 x3 (x1).^2 (x2).^2 (x3).^2 (x1.x2) (x2.x3) (x3.x1)]
Let x1,x2 and x3 be column vectors
Let us consider the first one. I tried using something like
Y = [1 : x : x.^100]
But this also didn't work because it means take Y = [1 x 2.*x 3.*x ... x.^100] ?
(ie all values between 1 to x.^100 with difference x)
So, this also cannot be used to generate such a matrix.
Please consider x = [1; 2; 3; 4];
and suggest a way to generate this matrix
Y = [1 1 1 1 1;
1 2 4 8 16;
1 3 9 27 81;
1 4 16 64 256];
without manually having to write
Y = [ones(size(x,1)) x x.^2 x.^3 x.^4]
Use this bsxfun technique -
N = 5; %// Number of columns needed in output
x = [1; 2; 3; 4]; %// or [1:4]'
Y = bsxfun(#power,x,[0:N-1])
Output -
Y =
1 1 1 1 1
1 2 4 8 16
1 3 9 27 81
1 4 16 64 256
If you have x = [1 2; 3 4; 5 6] and you want Y = [1 1 1 2 4; 1 3 9 4 16; 1 5 25 6 36] i.e. Y = [ 1 x1 x1.^2 x2 x2.^2 ] for column vectors x1, x2 ..., you can use this one-liner -
[ones(size(x,1),1) reshape(bsxfun(#power,permute(x,[1 3 2]),1:2),size(x,1),[])]
Using an adapted Version of the code found in Matlabs vander()-Function (which is also to be found in the polyfit-function) one can get a significant speedup compared to Divakars nice and short solution if you use something like this:
N = 5;
x = [1:4]';
V(:,n+1) = ones(length(x),1);
for j = n:-1:1
V(:,j) = x.*V(:,j+1);
end
V = V(:,end:-1:1);
It is about twice as fast for the example given and it gets about 20 times as fast if i set N=50 and x = [1:40]'. Although I state that is not easy to compare the times, just as an option if speed is an issue, you might have a look at this solution.
in octave, broadcasting allows to write
N=5;
x = [1; 2; 3; 4];
y = x.^(0:N-1)
output -
y =
1 1 1 1 1
1 2 4 8 16
1 3 9 27 81
1 4 16 64 256