Group and sum elements that are the same within a vector - matlab

Let's say I have a vector that looks as so (the numbers will always be > 0)...
[1, 2, 1, 4, 1, 2, 4, 3]
I need a vectorized implementation that sums the numbers together and uses the original number as the index to store the number. So if I run it I would get...
% step 1
[1+1+1, 2+2, 3, 4+4]
% step 2
[3, 4, 3, 8]
I have already implemented this using for loops, but I feel like there is a vectorized way to achieve this. I am still quite new at vectorizing functions so any help is appreciated.

This sounds like a job for accumarray:
v = [1, 2, 1, 4, 1, 2, 4, 3];
result = accumarray(v(:), v(:)).'
result =
3 4 3 8

Other approaches:
Using histcounts:
x = [1, 2, 1, 4, 1, 2, 4, 3];
u = unique(x);
result = u.*histcounts(x, [u inf]);
Using bsxfun (may be more memory-intensive):
x = [1, 2, 1, 4, 1, 2, 4, 3];
u = unique(x);
result = u .* sum(bsxfun(#eq, x(:), u(:).' ), 1);

Related

Octave function to get groups of consecutive columns in matrix

I am trying to find an efficient way of extracting groups of n consecutive columns in a matrix. Example:
A = [0, 1, 2, 3, 4; 0, 1, 2, 3, 4; 0, 1, 2, 3, 4];
n = 3;
should produce an output similar to this:
answer = cat(3, ...
[0, 1, 2; 0, 1, 2; 0, 1, 2], ...
[1, 2, 3; 1, 2, 3; 1, 2, 3], ...
[2, 3, 4; 2, 3, 4; 2, 3, 4]);
I know this is possible using a for loop, such as the following code snippet:
answer = zeros([3, 3, 3]);
for i=1:3
answer(:, :, i) = A(:, i:i+2);
endfor
However, I am trying to avoid using a for loop in this case - is there any possibility to vectorize this operation as well (using indexed expressions)?
Using just indexing
ind = reshape(1:size(A,1)*n, [], n) + reshape((0:size(A,2)-n)*size(A,1), 1, 1, []);
result = A(ind);
The index ind is built using linear indexing and implicit expansion.
Using the Image Package / Image Processing Toolbox
result = reshape(im2col(A, [size(A,1) n], 'sliding'), size(A,1), n, []);
Most of the work here is done by the im2col function with the 'sliding' option.

Basic Matlab for loop

A=2;
for x=0:2:4
A=[A, A*x];
end
A
I'd appreciate any help! The for loop condition as well as the 3rd line and how they work together I can't quite piece together
So, here comes the walktrough.
A = 2;
A is an array of length 1, with 2 as the only element.
for x = 0:2:4
Have a look at the Examples section of the for help. You create an "iteration variable" x, which iterates through an array with the values [0, 2, 4]. See also the Examples section of the : operator help.
A = [A, A*x];
Concatenate array A with the value of A*x (multiplying an array with a scalar results in an array of the same length, in which each element is multiplied by the given scalar), and re-assign the result to A. See also the help on Concatenating Matrices.
Initially, A = [2].
For x = 0: A = [[2], [2] * 0], i.e. A = [2, 0].
For x = 2: A = [[2, 0], [2, 0] * 2], i.e. A = [2, 0, 4, 0].
For x = 4: A = [[2, 0, 4, 0], [2, 0, 4, 0] * 4], i.e. A = [2, 0, 4, 0, 8, 0, 16, 0].
end
End of for loop.
A
Output content of A by implicitly calling the display function by omitting the semicolon at the end of the line, see here for explanation.

Octave/MATLAB: Using a matrix to access elements in a matrix without loops

Consider, the two matrices:
>> columns = [1,3,2,4]
and
>> WhichSet =
[2, 2, 1, 2;
1, 1, 2, 1;
1, 2, 1, 2;
2, 1, 2, 2]
My intent is to do the following:
>> result = [WhichSet(1,columns(1)), WhichSet(2,columns(2)), WhichSet(3, columns(3)) and WhichSet(4, columns(4))]
result = [2,2,2,2]
without any loops.
Because how indexing works, you can not just plug them as they are now, unless you use linear indexing
Your desired linear indices are:
ind=sub2ind(size(WhichSet),1:size(whichSet,1),columns);
Then
out=WhichSet(ind);

Transform matrix without loop

I have oldMat which is a ranking of equity tickers. The column number represents the respective rank, e.g. first column equals highest rank, second column represents second highest rank and so on. The integers within oldMatrepresent the number of the individual equity ticker. The number 3 in oldMat(3,2,1)means, that the third equity ticker is ranked second in the third period (rows represent different periods).
Now, I need to transform oldMat in the following way: The column numbers now represent the individual equity tickers. The integers now represent the rank that individual equity tickers hold at specific periods. For example, the number 2 in newMat(3,3,1) means, that the third equity ticker is ranked second in the third period.
I used a for-loop in order to solve that problem, but I am pretty sure there exists a more efficient way to achieve this result. Here's my code:
% Define oldMat
oldMat(:,:,1) = ...
[NaN, NaN, NaN, NaN, NaN, NaN; ...
1, 3, 4, 6, 2, 5; ...
6, 3, 4, 1, 2, 5; ...
2, 3, 6, 1, 4, 5; ...
5, 4, 6, 2, 3, 1; ...
5, 1, 2, 3, 6, 4; ...
4, 5, 1, 3, 6, 2; ...
4, 1, 6, 5, 2, 3];
oldMat(:,:,2) = ...
[NaN, NaN, NaN, NaN, NaN, NaN; ...
NaN, NaN, NaN, NaN, NaN, NaN; ...
1, 6, 3, 4, 2, 5; ...
6, 3, 2, 1, 4, 5; ...
2, 6, 3, 4, 1, 5; ...
5, 2, 1, 6, 3, 4; ...
5, 1, 3, 6, 2, 4; ...
4, 1, 5, 6, 3, 2];
% Pre-allocate newMat
newMat = nan(size(oldMat));
% Transform oldMat to newMat
for runNum = 1 : size(newMat,3)
for colNum = 1 : size(newMat,2)
for rowNum = 1 : size(newMat,1)
if ~isnan(oldMat(rowNum, colNum, runNum))
newMat(rowNum,oldMat(rowNum, colNum, runNum), runNum) = colNum;
end
end
end
end
Looks like a classic case of sub2ind. You want to create a set of linear indices to access the second dimension of the new matrix and set those equal to the column number. First create a grid of 3D coordinates with meshgrid, then use the oldMat matrix as an index into the second column of the output and set this equal to the column number. Make sure that you don't copy over any NaN values or sub2ind will complain. You can use isnan to help filter these values out for you:
% Initialize new matrix
newMat = nan(size(oldMat));
% Generate a grid of coordinates
[X,Y,Z] = meshgrid(1:size(newMat,2), 1:size(newMat,1), 1:size(newMat,3));
% Find elements that are NaN and remove
mask = isnan(oldMat);
X(mask) = []; Y(mask) = []; Z(mask) = [];
% Set the values now
newMat(sub2ind(size(oldMat), Y, oldMat(~isnan(oldMat)).', Z)) = X;

Average of neighbouring pairs 'interpolation'

I have two vectors, for example
A = [1, 3, 6, 7]
B = [2.0, 5.1, 2.2, 1]
I want to create a vector C and C1 so it would create the missing elements and assign to each of them the average of the corresponding surrounding elements in B.
C = [1, 2, 3, 4, 5, 6, 7]
C1 = [2.0, 3.55, 5.1, 3.65, 3.65, 2.2, 1]
What is the best way to that?
In order to use interp1 you would need
Ci = [1, 2, 3, 4.5, 4.5, 6, 7]
and then
C1 = interp1(A,B,Ci)
However generating that Ci is about as difficult as generating C1. I think in this case your best bet is to loop:
%// Assuming A is sorted
C = min(A):max(A);
C1 = zeros(size(C));
Acounter = 1;
for ii = 1:numel(C)
if C(ii)==A(Acounter)
C1(ii) = B(Acounter);
Acounter = Acounter + 1;
else
C1(ii) = (B(Acounter) + B(Acounter-1))/2;
end
end