GROUP BY in MATLAB - matlab

I want to do what SQL's GROUP BY does in MATLAB. For example,
M = [
1, 5;
2, 5;
3, 5;
1, 6;
2, 6;
1,7 ]
SQL: SELECT MAX(c1), c2 FROM M(c1, c2) GROUP BY 2
Result = [
3, 5;
2, 6;
1, 7]
How can I do this in Matlab?

grpstats in the Statistics Toolbox can do this:
>> [grpstats(M(:,1), M(:,2), {'max'}) unique(M(:,2))]
ans =
3 5
2 6
1 7

If you don't mind doing some preprocessing to get the order (or if the first column is nicely built from 1 to n), you can do it like this:
accumarray([1 2 3 1]',[11 12 13 14]',[],#max)
This will give:
14
12
13
Or in your case:
accumarray(M(:,1),M(:,2),[],#max)
Note the order. The second number for example, will correspond to M(:,1) == 2

I think there is a simple solution to this.
Here is what I tested on Matlab and it worked:
>> M = [
1, 5;
2, 5;
3, 5;
1, 6;
2, 6;
1,7 ];
>> grpstats(M,M(:,2),{'max'})
ans =
3 5
2 6
1 7

Related

Convert a matrix to a vector using a spiral path

I have seen until now that when someone wants to read the elements of a matrix using a spiral one by one they always mean starting outwards and slowly moving to the center using an MxM matrix. How would we do the opposite, start from a random point inside the matrix and using a "spiraling" path to read all the elements.
I am doing the tests using Matlab.
Example.
mat=
1 2 3
4 5 6
7 8 9
If lets say we were to start from mat(3,1) spiraling clockwise then we would have
vec=
7, 8, 4, 5, 6, 9, 1, 2, 3
and if we started from mat(2,2) then
vec=
5, 6, 9, 8, 7, 4, 1, 2, 3
One possible approach:
mat = [1 2 3; 4 5 6; 7 8 9];
M = length(mat); % Assuming mat is always MxM
r = 3;
c = 1;
temp = spiral(2 * M - 1);
temp = temp(M - r + 1:end - r + 1, M - c + 1:end - c + 1);
[~, idx] = sort(temp(:));
vec = mat(idx).'
Result running with r = 3 and c = 1
vec =
7 8 4 5 6 9 1 2 3
Result running with r = 2 and c = 2
vec =
5 6 9 8 7 4 1 2 3

Concatenate two arrays in MATLAB

I have a 5000x2 array as:
A = [1, 3; 2, 4; 1, 6; 2, 4; 1, 7];
I have another array of size 100x2 which looks as:
B = [1, 14; 2, 15];
How can I create a third array where I am going to use column 2 of vector B as follows to modify matrix A:
C = [1, 3, 14; 2, 4, 15; 1, 6, 14; 2, 4, 15; 1, 7, 14];
I am just trying to use column 1 of B as keys which would be same as contents of column 1 of A.
Assuming the first column on B is indices = 1 2 3 4 ..., the following should work:
A =
1 3
2 4
2 6
2 4
1 7
2 8
C(:,3) = B(A(:,1),2)
C =
1 3 14
2 4 15
2 6 15
2 4 15
1 7 14
2 8 15
or if you just want 14 15 14 15:
C = [A repmat(B(:,2),size(A,1)/size(B,1),1)]
Do the following:
A(A(:,1)== key ,3) = B(B(:,1)== key ,2);
where key takes the values of 1 and 2 (and any other possible key).
What does this line do? A(:,1)== key will be true on the rows where the first column values are equal to key, and then sets the third column of A equal to the values of B that have the same key.
You should execute this line for key=1 and key=2 in order to get what you need.

Outer product in Matlab?

How to turn two vectors into a matrix of all combinations of their elements?
For example, vectors
>> A=[1;2;3]
A =
1
2
3
>> B=[4;5;6]
B =
4
5
6
Should be turned to
[1, 4; 1, 5; 1, 6; 2, 4; 2, 5; 2, 6; 3, 4; 3, 5; 3, 6]
ans =
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
I'm sure there is a simpler way of doing this but... meshgrid will get you close and you just need to perform some array manipulation to get your result:
[BA, BB] = meshgrid(A,B);
[BA(:) BB(:)]
An order of magnitude slower than meshgrid, but just to show you a different method:
[kron(A,ones(numel(B),1)), kron(ones(numel(A),1), B)];

Expand a matrix with polynomials

Say I have a matrix A with 3 columns c1, c2 and c3.
1 2 9
3 0 7
3 1 4
And I want a new matrix of dimension (3x3n) in which the first column is c1, the second column is c1^2, the n column is c1^n, the n+1 column is c2, the n+2 column is c2^2 and so on. Is there a quickly way to do this in MATLAB?
Combining PERMUTE, BSXFUN, and RESHAPE, you can do this quite easily such that it works for any size of A. I have separated the instructions for clarity, you can combine them into one line if you want.
n = 2;
A = [1 2 9; 3 0 7; 3 1 4];
[r,c] = size(A);
%# reshape A into a r-by-1-by-c array
A = permute(A,[1 3 2]);
%# create a r-by-n-by-c array with the powers
A = bsxfun(#power,A,1:n);
%# reshape such that we get a r-by-n*c array
A = reshape(A,r,[])
A =
1 1 2 4 9 81
3 9 0 0 7 49
3 9 1 1 4 16
Try the following (don't have access to Matlab right now), it should work
A = [1 2 9; 3 0 7; 3 1 4];
B = [];
for i=1:n
B = [B A.^i];
end
B = [B(:,1:3:end) B(:,2:3:end) B(:,3:3:end)];
More memory efficient routine:
A = [1 2 9; 3 0 7; 3 1 4];
B = zeros(3,3*n);
for i=1:n
B(3*(i-1)+1:3*(i-1)+3,:) = A.^i;
end
B = [B(:,1:3:end) B(:,2:3:end) B(:,3:3:end)];
Here is one solution:
n = 4;
A = [1 2 9; 3 0 7; 3 1 4];
Soln = [repmat(A(:, 1), 1, n).^(repmat(1:n, 3, 1)), ...
repmat(A(:, 2), 1, n).^(repmat(1:n, 3, 1)), ...
repmat(A(:, 3), 1, n).^(repmat(1:n, 3, 1))];

addition of one column with certain condition in another colum, like sumifs of excel

I have a matrix like this
A=[ 1 2; 2 3; 3 4; 4 5; 5 6; 6 8; 7 9; 8 5; 9 4]
Now I want to add a second column the condition is that if limit=0, and interval=3 and limit=limit+interval, or in other words, I have to sum column 2 when values of column 1, ranges like 0 to 3, 3 to 6, 6 to 9, and 9 to 12, and i want sum of corresponding values of column 2.
my solution will be like that
range- sum
0 to 3 9
3 to 6 19
6 to 9 18
like that I have a matrix of around 7000x2. In place of range just serial no may also be given.
This is just an example.
This is a job for ACCUMARRAY. First, you construct an array of indices of the values that should be added together, then you call accumarray:
%# create test data
A=[ 1 2; 2 3; 3 4; 4 5; 5 6; 6 8; 7 9; 8 5; 9 4];
%# create indices from first column
%# if you have indices already, you can use them directly
%# or you can convert them to consecutive indices via grp2idx
groupIdx = ceil(A(:,1)/3); %# 0+ to 3 is group 1, 3+ to 6 is group 2, etc
%# sum
result = accumarray(groupIdx,A(:,2),[],#sum)
result =
9
19
18
EDIT
If you need instead to count entries within the ranges, it is still a job for accumarray, only that you don't accumulate into a sum, but into a histogram.
%# use test data, groupIdx from above
A=[ 1 2; 2 3; 3 4; 4 5; 5 6; 6 8; 7 9; 8 5; 9 4];
groupIdx = ceil(A(:,1)/3); %# 0+ to 3 is group 1, 3+ to 6 is group 2, etc
%# find values to count
values2count = unique(A(:,2));
%# count the values
countsPerRange = accumarray(groupIdx,A(:,2),[],#(x){hist(x,values2count)})
%# inspect the counts for range #1
[values2count,countsPerRange{1}']
ans =
2 1
3 1
4 1
5 0
6 0
8 0
9 0