Matlab column aggregation - matlab

Is there a function in MATLAB which allows to aggregate (or we can say sum) columns in a matrix per defined number of columns?
For example I have:
A =
1 2 3 4 5 6
9 10 11 12 13 14
17 18 19 20 21 22
I wish to aggregate every 2 columns, like this: col1+col2, and then col3+col4, and then col5+col6, so my output is:
A_agg =
3 7 11
19 23 27
35 39 43
I couldn't find a built-in function and was trying to write a for loop but I couldn't manage to do it since I am quite new to programming. Do you have any suggestions/solutions how this could be solved with a for loop, or maybe there is a built-in function?

Since sum operates down columns in a matrix, I first reshape A so that it has 2 rows and 9 columns, then sum down each column. Then reshape back to the desired output matrix A_agg.
A=[1 2 3 4 5 6
9 10 11 12 13 14
17 18 19 20 21 22]
[m,n]=size(A);
A_agg=reshape(sum(reshape(A',2,[])),m,[])'

You can use a combination of mat2cell and cellfun. You can use mat2cell to split up your matrices into individual 2 column chunks. Each chunk would be stored as a cell in a cell array. You can then use cellfun to take each cell and sum row-wise. After you're done, you can use cell2mat to convert back.
Using your example:
A = [1:6;9:14;17:22];
B = mat2cell(A, 3, [2 2 2]);
C = cellfun(#(x) sum(x,2), B, 'UniformOutput', false);
A_agg = cell2mat(C);
A_agg should thus give you:
A_agg =
3 7 11
19 23 27
35 39 43
Let's walk through the code slowly:
A is defined as we had before. B will be a cell array, and will segment your matrix into matrices of 2 columns per cell. The first parameter is the matrix you want to decompose (in our case A). The second parameter tells you how many rows each segment should have. Because we want all of the matrices to have the same number of rows, we thus supply one number which is 3. After, you specify the number of columns you want per matrix. Because there are 6 columns, we need 3 matrices, and so you'd specify a vector of [2 2 2].
C is the output of cellfun, where cellfun applies a function to every single element in a cell matrix. What you want to do here is for each cell (essentially each matrix), you want to sum row-wise. The first parameter is an anonymous function that takes in a matrix from each cell, and sums row-wise. The second parameter is the cell array we just created. You'll notice that we have an additional flag to set: UniformOutput. The reason why you have to set UniformOutput = false is because if you apply cellfun without that flag, the expected result at the end of the function you apply to each cell is scalar. Because we are outputting a column vector instead, we have to set this flag to false.
A_agg will thus aggregate all of your cells back to matrix form.
If you want to do this for any size matrix, bear in mind that there has to be an even amount of columns for this work. What I mean by even is that the number of columns has to be evenly divisible by 2. You would thus re-run the code like so:
B = mat2cell(A, size(A,1), 2*ones(1, size(A,2)/2));
C = cellfun(#(x) sum(x,2), B, 'UniformOutput', false);
A_agg = cell2mat(C);

Another possibility, if you have the Image Processing Toolbox, is to use blockproc. Let n denote the number of columns to be aggregated (2 in your example). Then:
A_agg = blockproc(A, [size(A,1) n], #(x) sum(x.data, 2));

Related

How to get unique pairs of numbers in matlab where both numbers have not repeated in the matrix before

I have an input matrix as below
all = [12 16;12 13;8 14;14 19;3 6;8 6;13 25;25 14;7 2];
I need the following output
output = [12 16;8 14;3 6;13 25;7 2];
The explanation for the output is as follows.
First row of input i.e. 12 16 is the first row in output as both the numbers have never been repeated before in the output matrix (obviously).
Second row of input i.e 12 13 is not needed as the number 12 is present in first row of output i.e repeated
Third row of input i.e 8 14 is second row of output as both the numbers have never been repeated before in the output matrix.
Fourth row of input i.e 14 19 is not needed as the number 14 is present in output i.e repeated
On similar lines
3 6 needed as both are not repeated,
8 6 not needed as both 8 and 6 are repeated,
13 25 needed as both are not repeated
25 14 not needed as both are repeated
7 2 needed as both are not repeated
I am not able to get any ideas to start. Any help will be appreciated.
Thanks!
One Liner Solution
res = all(arrayfun(#(ii) isempty(intersect(all(1:ii-1,:),all(ii,:))),1:size(all,1)),:);
Result
res =
12 16
8 14
3 6
7 2
Explanation
let's divide the one-liner into a more detailed and documented chunk of code:
%defines a function which validates for each index wheter the row is
%completely unique are not.
uniqueRowIndicator = #(ii) isempty(intersect(all(1:ii-1,:),all(ii,:)));
%finds all the unique row in the matrix
inds = arrayfun(uniqueRowIndicator,1:size(all,1));
%extracts the result from the returned indices
res = all(inds,:);
This assumes that if a row contains two equal values they count as repeated and thus the row should be removed.
Don't use all as a variable name, because that shadows a function:
A = [12 16;12 13;8 14;14 19;3 6;8 6;13 25;25 14;7 2]; % input matrix
[~, u] = unique(A.', 'first'); % unique values of linearized transposed A.
% In recent Matlab versions you an remove 'first '
M = false(flip(size(A))); % initiallize mask of values to be kept
M(u) = true; % fill values
output = A(all(M,1),:); % keep rows that only have non-repeated values
This gives
output =
12 16
8 14
3 6
7 2

Size of cell elements

I have five classes of data stored in a cell,
DataCell =
[74035x14 single] [8063x14 single] [7244x14 single] [6895x14 single] [2510x14 single]
I want to get the prior probabilities of each class,
So want it's pretty simple,
SumData = 74032 + 8063 + 7244 + 6895 + 2510;
prior = [74035 8063 7244 6895 2510] / SumData;
I was wondering if there is a way to avoid loop and get the answer.
Thanks,
Store the counts of the DataCell into an array, then "nomalize" it:
data_counts = cellfun(#(x) size(x,1), DataCell);
prior = data_counts / sum(data_counts(:));
The data_counts(:) is just a funny way of summing all the elements of data_counts, no matter what shape they're in.
To add to CST-Link's answer, cellfun has a special flag where if you specify 'size', you can determine the size of of elements inside each cell in a cell array. You simply specify which dimension you're measuring the size of and it'll return an array of elements that denote the size of each cell in a particular dimension. It will also respect whatever the shape of your cell array was before you call cellfun. For example, if you have a 2 x 2 cell array, after using 'size', it will return a 2 x 2 numeric matrix where each element is the size of the dimension you specified.
Therefore, do this:
data_counts = cellfun('size', DataCell, 1);
prior = data_counts / sum(data_counts(:));
To add to the data_counts(:) statement, this is MATLAB's way of unrolling a matrix. What this will do is that it transforms data_counts into a single vector, where it is composed of columns of A stacked on top of each other. For example, if you had a matrix like so:
A =
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
Doing A(:) would give you:
1
5
9
13
2
6
10
14
3
7
11
15
4
8
12
16
However, if A is already a 1D array, then this has no effect at all. The only thing that it will do is that if your array was a row vector, this will transform the array so that it's column vector. If it was already a column vector, then this won't change anything. This is a neat trick to ensure that a 1D array is always a column vector.

Create vector from elements other than diagonal ones

I would like to create a column vector from the elements of a matrix A of size (3,3) that are not on the diagonal. Thus, I would have 6 elements in that output vector. How can I do this?
Use eye and logical negation, although this is no better than Divakar's original answer, and possibly significantly slower for very large matrices.
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> A(~eye(size(A)))
ans =
5
9
4
2
7
14
3
10
15
13
8
12
Use this to get such a column vector, assuming A is the input matrix -
column_vector = A(eye(size(A))==0)
If you don't care about the order of the elements in the output, you can also use a combination of setdiff and diag -
column_vector = setdiff(A,diag(A))
You can also use linear indexing to access the diagonal elements and null them. This will automatically reshape itself to a single vector:
A(1:size(A,1)+1:end) = [];
Bear in mind that this will mutate the original matrix A. If you don't want this to happen, make a copy of your matrix then perform the above operation on that copy. In other words:
Acopy = A;
Acopy(1:size(A,1)+1:end) = [];
Acopy will contain the final result. You need to create a vector starting from 1 and going to the end in increments of the rows of the matrix A added with 1 due to the fact that linear indices are column-major, so the linear indices used to access a matrix progress down each row first for a particular column. size(A,1) will allow us to offset by each column and we add 1 each time to ensure we get the diagonal coefficient for each column in the matrix.
Assuming that the matrix is square,
v = A(mod(0:numel(A)-1, size(A,1)+1) > 0).';

matrix get min values of a matrix before max values occurred

I was trying to get the min values of a matrix before the max values of the matrix occurred. I have two matrices: matrix data and matrix a. Matrix a is a subset of matrix data and is composed of the max values of matrix data. I have the following code but obviously doing something wrong.
edit:
Matrix a are the max values of matrix data. I derived it from:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
matrix b code:
for x=1:size(data,1)
b(x)=min(data(x,(x<data==a)));
end
b=b'
clear x
matrix data matrix a matrix b
1 2 3 4 4 1
6 5 4 7 7 4
9 6 12 5 12 6
I need all the min values that occurred before to matrix a occurred in matrix data
Short and simple:
[a,idxmax] = max(data,[],2);
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
which is the same as
b=NaN(1,size(data,1)); % preallocation!
for ii=1:size(data,1)
b(ii) = min(data(ii,1:idxmax(ii)));
end
Ignore maximum itself
If you want minimum of everything really before (and not including the maximum), it's possible that the maximum is the first number, and you try taking minimum of an empty matrix. Solution then is to use cell output, which can be empty:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii)-1)), 1:size(data,1),'uni',false);
Replace empty cells with NaN
If you want to replace empty cells to Nan and then back to a matrix use this:
b(cellfun(#isempty,b))={NaN};
b=cell2mat(b);
or simply use the earlier version and replace b(ii) with NaN when it is equal to a(ii) same outcome:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
b(b'==a) = NaN
Example:
data=magic(4)
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
outputs:
a' = 16 11 12 15
b =
16 5 6 4
and
b =[1x0 double] [5] [6] [4]
for the 2nd solution using cell output and ignoring the maximum itself also.
And btw:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
can be replaced with
a=max(data,[],2);
It's not pretty but this is the only way I found so far of doing this kind of thing without a loop.
If loops are ok I would recommend Gunther Struyf answer as the most compact use of matlab's in-built array looping function, arrayfun.
Some of the transposition etc may be superfluous if you're wanting column mins instead of row...
[mx, imx] = max(data');
inds = repmat(1:size(data,2), [size(data,1),1]);
imx2 = repmat(imx', [1, size(data,2)]);
data2 = data;
data2(inds >= imx2) = inf;
min(data2');
NOTE: if data is not needed we can remove the additional data2 variable, and reduce the line count.
So to demonstrate what this does, (and see if I understood the question correctly):
for input
>> data = [1,3,-1; 5,2,1]
I get minima:
>> min(data2')
ans = [1, inf]
I.e. it only found the min values before the max values for each row, and anything else was set to inf.
In words:
For each row get index of maximum
Generate matrix of column indices
Use repmat to generate a matrix, same size as data where each row is index of maximum
Set data to infinity where column index > max_index matrix
find min as usual.

Create third matrix in MATLAB from combination of two other matrices

I have two expressions in MATLAB that represent a 365x24 matrix. The first expression has 10, 365x24 matrices and is therefore
PV_power_output(:,:,K)
and the second expression which is again 365x24 but with three possible matrices therefore is
WT_energy_supply(:,:,M);ode here
Now, I am looking to create a third matrix that adds the elements in the same position above and thus form a 365x24 matrix. However I want a set of matrix with all possible combinations of the two expressions shown above (therefore this matrix must be 365x24x30.
How do I go about this?
What about the bsxfun function in MATLAB?
Expand the original matrices (which for clarity I name a and b) with repmat and then just add them, bsxfun is not needed.
repmat(a,[1 1 size(b,3)]) + repmat(b,[1 1 size(a,3)]))
Update
>> size(a)
ans =
364 24 10
>> size(b)
ans =
364 24 3
>> c=repmat(a,[1 1 size(b,3)])+repmat(b,[1 1 size(a,3)]);
>> size(c)
ans =
364 24 30
It looks fine to me. Of course you'll have to replace my variables a and b with your variables PV_power_output and WT_energy_supply.