Is there a way to group matrix elements in matlab? - matlab

I am working on a problem which requires me to group array elements and average each group. For example consider the 4 x 4 matrix M,
M = [ 1 0 0 1;
1 0 1 1;
1 1 0 1;
0 0 0 1;]
I want to group this into a 2 x 2 matrix, taking an average of the elements so we would get
M1 = [0.5 0.75;
0.5 0.5;]
does anyone know a way to do this?
Many thanks

You can do this using conv2, and a little indexing, like so:
>> A = conv2(M,ones(2), 'same');
>> A(1:2:end,1:2:end)/4
ans =
0.5000 0.7500
0.5000 0.5000

I think the way to go is to first split your matrix in parts using mat2cell, then apply your functions to each part and merge them to a new matrix:
>> M = [ 1 0 0 1;
1 0 1 1;
1 1 0 1;
0 0 0 1;]
M =
1 0 0 1
1 0 1 1
1 1 0 1
0 0 0 1
>> T=mat2cell(M, [2 2], [2 2])
T =
2×2 cell array
{2×2 double} {2×2 double}
{2×2 double} {2×2 double}
>> M1 = cellfun(#mean, cellfun(#mean, T, 'UniformOutput', false))
M1 =
0.5000 0.7500
0.5000 0.5000
>>

You can do something like this for any rectangle, where x and y denote the size of domains that you want to average.
function M1 = get_means(M, x, y)
[rows, cols] = size(M);
if mod(rows, x) == 0 && mod(cols, y) == 0
for i = 1:y:cols
for j = 1:x:rows
M1((j+x-1)/x, (i+y-1)/y) = sum(M(j:j+x-1,i:i+y-1),'all')/(x*y);
end
end
else
error('The matrix doesn''t have compatible dimensions.')
end
end

You can loop over the grouped matrices and then calculate the mean
M = [ 1 0 0 1;
1 0 1 1;
1 1 0 1;
0 0 0 1;];
n=2; % 2x2 mat
% create new matrix with means
MM = zeros(n,n);
% row counter for mean-Matrix
r=1;
% loop over matrix groups
for row=1:n:size(M,1)
c = 1; % column counter for mean-Matrix
for col=1:n:size(M,2)
MM(r,c) = mean(mean(M(row:row+n-1, col:col+n-1)));
c = c+1;
end
r = r+1;
end
Output:
MM =
0.5000 0.7500
0.5000 0.5000

Related

How to improve this code for replacing elements of a matrix from another matrices in MATLAB?

So I have these 4 same-sized matrices:
k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];
And I want to put the elements from each matrix into the diagonal of zero matrix, so it would create something like this:
f = 0.5 2.0 0 0 0 0 0 0
1.7 2.5 0 0 0 0 0 0
0 0 1.0 4.0 0 0 0 0
0 0 3.0 5.0 0 0 0 0
0 0 0 0 1.5 6.0 0 0
0 0 0 0 4.5 7.5 0 0
0 0 0 0 0 0 2.0 8.0
0 0 0 0 0 0 6.0 10.0
This is what I've come up with:
f = zeros(8,8);
k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];
for i = 1:2:8 %odd index number
for j = 2:2:8 %even index number
f(i,i) = k(1,i)
f(i,j) = l(1,i)
f(j,i) = m(1,i)
f(j,j) = n(i,1)
end;
end;
disp(f)
But the result put the elements not into the diagonal of matrix f=zero(8,8), and I've always got the error of Index exceeds matrix dimensions.
Can I get some pointers on how to fix this code?
I think you intended to write this:
for i = 2:2:8
f(i-1,i-1) = k(i/2)
f(i-1,i) = l(i/2)
f(i,i-1) = m(i/2)
f(i,i) = n(i/2)
end
Though there must be simpler ways to accomplish the same thing. For example, check out the function blkdiag!
If you're looking for compact, then here's an interesting solution using eye, repelem, and logical indexing:
>> f = zeros(8);
>> f(repelem(eye(4, 'logical'), 2, 2)) = [k m l n].';
f =
0.5000 2.0000 0 0 0 0 0 0
1.7000 2.5000 0 0 0 0 0 0
0 0 1.0000 4.0000 0 0 0 0
0 0 3.0000 5.0000 0 0 0 0
0 0 0 0 1.5000 6.0000 0 0
0 0 0 0 4.5000 7.5000 0 0
0 0 0 0 0 0 2.0000 8.0000
0 0 0 0 0 0 6.0000 10.0000
Cris Luengo's answer is more flexible and read much better than what I have here, but this is something you probably want to learn if you will do a lot of work using MATLAB.
(This method generally performs better because it removes the need of a for loop.)
k = [0.5;1.0;1.5;2.0];
l = [2;4;6;8];
m = [1.7;3.0;4.5;6.0];
n = [2.5;5.0;7.5;10.0];
f = zeros(8); % to create a square matrix, you only need to specify the size with one value
f(1:18:end) = k;
f(9:18:end) = l;
f(2:18:end) = m;
f(10:18:end) = n;
Here, f is used as if it is a vector (array) even though it is a 8*8 matrix. In Matlab, you can access a single element from a matrix by either specifying the row and column number or linear indexing.
More details can be found here: https://uk.mathworks.com/help/matlab/math/matrix-indexing.html (see Linear Indexing)
Another option is to construct the diagonal, and the two off-diagonal vectors first and then use diag to create the matrices and then adding them together.
%Data
k = [0.5;1.0;1.5;2.0];
l = [2;4;6;8];
m = [1.7;3.0;4.5;6.0];
n = [2.5;5.0;7.5;10.0];
%Construct vectors
diagonal = [1;0].*k.' + [0;1].*n.'; %Diagonal of the matrix
offu = [1;0].*l'; %Upper diagonal
offl = [1;0].*m'; %Lower diagonal
A = diag(diagonal(:)) + diag(offu(1:end-1),1) + diag(offl(1:end-1),-1);
I have no idea whether it is faster than the other answers, but I like its transparency of what is happening.
NOTE: In order to construct the variables diagonal, offu and offl I am using implicit expansion which requires Matlab R2016b or newer. If you are using an older version, you can instead use bsxfun.
A sorry, I missed the everything is shifted. My former answer was completely incorrect...
I still would go for something with blkdiag, you just need to redistribute your matrices:
k = [0.5; 1.0; 1.5; 2.0];
l = [2; 4; 6; 8];
m = [1.7; 3.0; 4.5; 6.0];
n = [2.5; 5.0; 7.5; 10.0];
Y = arrayfun(#(i) [k(i), l(i); m(i), n(i)], 1:4, 'UniformOutput', false);
blkdiag(Y{:})
X = cat(3, [k, m], [l, n]);
blkdiag(squeeze(X(1,:,:)), squeeze(X(2,:,:)), squeeze(X(3,:,:)), squeeze(X(4,:,:)))

Remove column mean from nonzero entries of a column

Given an sparse matrix A in MATLAB and the mean for the nonzero elements in its columns m, is there anyway to subtract the nonzero elements in each column from the mean of each column and avoid looping over columns?
I am looking for efficient solutions. Using 'bsxfun' could be one solution if it is possible to use.
Thanks
You can use the second output of find to get the column indices; use those to index into m to do the subtraction; and put the results back into A using logical indexing:
A = sparse([0 0 0 0; 1 0 3 2; 2 1 0 5]); %// example data
m = [1.5 1 3 3.5]; %// vector of mean of nonzero elements of each column
m = m(:);
[~, jj, vv] = find(A);
A(logical(A)) = vv - m(jj);
Original A:
>> full(A)
ans =
0 0 0 0
1 0 3 2
2 1 0 5
Final A:
>> full(A)
ans =
0 0 0 0
-0.5000 0 0 -1.5000
0.5000 0 0 1.5000

Solve a system of equations with a constraint to x in Matlab

I have given a nxn upper triangular matrix R and I want to solve the system of equations Rx=0 where x is a vector of size n. Moreover the lowermost diagonalement of R is 0 (R(n,n)=0). Therefore I want to set x(n)=1.
I tried some loops but I do not know how to solve it.
Thank you for your help.
It's guaranteed that R has a zero eigenvalue, and the solution you want is a multiple of the eigenvector corresponding to that eigenvalue. Let's create some matrix R first:
>> R = triu(rand(3, 3));
>> R(3, 3) = 0;
>> R
R =
0.8147 0.9134 0.2785
0 0.6324 0.5469
0 0 0
Now let's get the eigenvalues and eigenvectors:
>> [V, E] = eig(R)
V =
1.0000 -0.9806 0.4289
0 0.1958 -0.5909
0 0 0.6833
E =
0.8147 0 0
0 0.6324 0
0 0 0
The eigenvectors are the diagonal elements of E
>> E = diag(E);
>> index = find(abs(E) < 1e-16); %// NB don't use find(E==0) because of fp problems...
Now pull out the correct eigenvector
>> v = V(:, index);
and make sure its final element is equal to 1
>> v = v / v(end)
v =
0.6277
-0.8648
1.0000
You can check that this is the solution you want
>> R * v
ans =
0
0
0

Check neighbour pixels Matlab

I have a A which is 640x1 cell. where the value of each cell A(i,1) varies from row to row, for example A(1,1) =[], while A(2,1)=[1] and A(3,1)=[1,2,3].
There is another matrix B of size 480x640, where the row_index (i) of vector A corresponds to the col_index of matrix B. While the cell value of each row in vector A corresponds to the row_index in matrix B. For example, A(2,1)=[1] this means col_2 row_1 in matrix B, while A(3,1)=[1,2,3] means col_3 rows 1,2&3 in matrix B.
What I'm trying to do is to for each non-zero value in matrix B that are referenced from vector A, I want to check whether there are at least 4 other neighbors that are also referenced from vector A. The number neighbors of each value are determined by a value N.
For example, this is a part of matrix B where all the zeros"just to clarify, as in fact they may be non-zeros" are the neighbors of pixel X when N=3:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 X 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
As shown, because N=3, all these zeros are pixel X's neighbors. So if more than 4 neighbor pixels are found in vector A then do something e.g G=1 if not then G=0;
So if anyone could please advise. And please let me know if any more clarification is needed.
The first thing I would do is to convert your cell of indices A to a logic matrix Amat. This makes it easier to check how many neighbours are included in A.
Here is a solution that uses this conversion. I hope the comments are enough to make it understandable.
clear all
clc
nCols = 7;
nRows = 6;
N = 3; %// Number of neighbours
M = 4; %// Minimum number of wanted connections
%// Create cell of indices A
A = cell(nCols,1);
A{1} = [];
A{2} = 1;
A{3} = [1 2 3];
A{4} = [2 5];
A{5} = 3;
A{6} = [3 5];
A{7} = [1 4 6];
%// Generate radom data B
%// (There is a 50% probability for each element of B to be zero)
Bmax = 17;
B = (randi(2,nRows,nCols)-1).*(randi(Bmax,nRows,nCols));
%// Convert the cell A to a logic matrix Amat
Amat = zeros(size(B));
for ii = 1:nCols
Amat(A{ii},ii) = 1;
end
A
B
Amat
for ii = 1:nCols
for jj = A{ii}
if B(jj,ii)>0
%// Calculate neighbour indices with a lower bound of 1
%// and an upper bound of nCols or nRows
col_lim_low = max(1,ii-N);
col_lim_high = min(nCols,ii+N);
row_lim_low = max(1,jj-N);
row_lim_high = min(nRows,jj+N);
%// Get the corresponding neighbouring-matrix from Amat
A_neighbours = ...
Amat(row_lim_low:row_lim_high,col_lim_low:col_lim_high);
%// Check the number of neighbours against the wanted number M
if sum(A_neighbours(:)) > 1 + M
%# do something
fprintf('We should do something here at (%d,%d)\n',jj,ii)
end
end
end
end
The following is a printout from one run of the code.
A =
[]
[ 1]
[1x3 double]
[1x2 double]
[ 3]
[1x2 double]
[1x3 double]
B =
1 5 0 0 11 0 16
0 13 13 0 0 0 9
0 0 0 5 0 0 0
3 8 16 16 0 2 12
0 0 5 0 9 9 0
12 13 0 6 0 15 0
Amat =
0 1 1 0 0 0 1
0 0 1 1 0 0 0
0 0 1 0 1 1 0
0 0 0 0 0 0 1
0 0 0 1 0 1 0
0 0 0 0 0 0 1
We should do something here at (1,2)
We should do something here at (2,3)
We should do something here at (5,6)
We should do something here at (4,7)
Since you have a one-to-one correspondence between A and B, there is no need to work on A. B is a logical matrix (0 if not referenced in A, 1 if referenced). You can therefore apply a simple filter2 function counting the number of active neighbors within the 8 closest elements.
Here is the code
B = rand(10,10); %generate binary matrix
h = [1 1 1;1 0 1;1 1 1]; %filter to be applied
filter2(h,B,'same')>=4 & B>0 %apply filter on B, count minimum of 4 neighbors, if only B>1
EDIT
To transform a cell array B into binary presence (0=empty, 1=not empty), use of cellfunis straightforward
B = ~cellfun(#isempty,B);
And see Armo's response to your previous question for how to create B based on A.

Probability of a number occurring in a matrix

I'm using Matlab and well it's straightforward to find the probability of an element in a matrix, but I a little unsure of how to find probability of an element in a row or column.
e.g this matrix:
X = [
1 2 4 1 8;
5 3 6 9 2;
6 2 2 3 2
];
How would I find the probability of "2" occurring in each row and column of this random matrix.
You could do the following:
X_unique = unique(X);
p_row = zeros(size(X,1),numel(X_unique));
p_col = zeros(size(X,2),numel(X_unique));
for ii = 1:size(X,1)
p_row(ii,:) = hist(X(ii,:),X_unique);
p_row(ii,:) = p_row(ii,:)/sum(p_row(ii,:));
end
for ii = 1:size(X,2)
p_col(ii,:) = hist(X(:,ii),X_unique);
p_col(ii,:) = p_col(ii,:)/sum(p_col(ii,:));
end
Now, each row of p_row contains the probability distribution of the elements of unique(X) in the corresponding row of X and each row of p_col contains the probability distribution of the elements of unique(X) in the corresponding column of X.
For example, for the given example,
X_unique =
1
2
3
4
5
6
8
9
Thus,
p_row =
0.4000 0.2000 0 0.2000 0 0 0.2000 0
0 0.2000 0.2000 0 0.2000 0.2000 0 0.2000
0 0.6000 0.2000 0 0 0.2000 0 0
p_col =
0.3333 0 0 0 0.3333 0.3333 0 0
0 0.6667 0.3333 0 0 0 0 0
0 0.3333 0 0.3333 0 0.3333 0 0
0.3333 0 0.3333 0 0 0 0 0.3333
0 0.6667 0 0 0 0 0.3333 0
Here's a simple, not-quite-Matlab-ish solution that works on non-empty bi-dimensional matrices, looking for elements with the value "2", and returning probabilities by column:
a = [1 2 4 1 8; 5 3 6 9 2; 6 2 2 3 2];
nrows = size(a,1);
ncols = size(a,2);
pc = zeros(1, ncols); % Prob. by column
% Iterate trough columns
for k = 1:ncols
n = sum(a(:,k) == 2);
pc(k) = n/nrows;
end;
You can adapt it to compute "probabilities" by row, or by other dimensions, or look for other values.