Nested double sort in Matlab - matlab

Suppose I have 3 vectors, vector A which is (n x 1), vector B which is (n x 1) and vector C which is (n x 1).
I want to sort the elements of A, into 5 groups, and then within those groups I want to sort the respective elements of B into 5 groups as well. And then take the average of the elements in C. So I will have 25 averages.
In other words:
Sort the elements of A into 5 quintiles;
Pick the first
group of elements in A, get the corresponding values in B;
Sort the picked elements of B into 5 groups.
Take the average of each group from C.
Pick the second group of elements in A, get the corresponding
values in B;
Sort the picked elements of B into 5 groups.
Take the average of each group from C.
And so on and so forth.
Here's my dummy code for this:
minimum = 50;
maximum = 100;
A = (maximum-minimum).*rand(1000,1) + minimum;
B = (maximum-minimum).*rand(1000,1) + minimum;
C = (maximum-minimum).*rand(1000,1) + minimum;
nbins1 = 5;
nbins2 = 5;
bins1 = ceil(nbins1 * tiedrank(A) / length(A));
for i=1:nbins1
B1 = B(bins1==i);
C1 = C(bins1==i);
bins2 = ceil(nbins1 * tiedrank(B1) / length(B1));
for j=1:nbins2
C2 = C1(bins2==j);
output(i,j) = mean(C2);
clearvars C2
end
clearvars B1 C1
end
The issue is that, this does not seem very elegant or efficient at all. Is there any other way of doing this? For people in Finance, this problem is analogous to the Fama-French (1993) double sorting of portfolios.

First of all, sort everything by column A:
sortedByA = sortrows([A,B,C], 1);
Create a dummy vector representing indices of each group in A (from 1 to nbins1):
groupsA = repmat(1:nbins1, 1000/nbins1, 1); groupsA = groupsA(:);
Then re-sort again (by first two columns), but replacing actual column A with group indices, which would in effect sort B within each group of values in A:
sorted = sortrows([groupsA, sortedByA(:,[2,3])], [1,2]);
Create indices for groups in column C (from 1 to nbins1*nbins2):
groupsC = repmat(1:(nbins1*nbins2), 1000/(nbins1*nbins2), 1); groupsC = groupsC(:);
Finally, compute mean within each group:
averages = accumarray(groupsC, sorted(:,3), [], #mean);

Related

Sum of top 2 numbers in matlab

I have three vectors in a function, each of size 2. I want to get the sum of the top 2 numbers at each index. What I mean is this:
A = [4,4];
B = [3,5];
C = [5,6];
Sum = [9,11];
I would like to skip if-else statements if possible.
A simple way is to
concatenate the row vectors into a matrix: [A; B; C]
apply maxk along the first dimension: maxk(..., 2, 1), where 2 is the desired number of elements
then apply sum along the first dimension: sum(..., 1.
Thus:
result = sum(maxk([A; B; C], 2, 1), 1);
First, you concatenate your three vectors through
vectors = [A; B; C];
Then, you sort it through
s = sort(vectors, 'descent');
Finally, you sum the two bigger element of each column through
SUM = s(1, :) + s(2, :)

Comparing matrices of different size in matlab and storing values that are close

I have two matrices A and B. A(:,1) corresponds to an x-coordinate, A(:,2) corresponds to a y-coordinate, and A(:,3) corresponds to a certain radius. All three values in a row describe the same circle. Now let's say...
A =
[1,4,3]
[8,8,7]
[3,6,3]
B =
[1,3,3]
[1, 92,3]
[4,57,8]
[5,62,1]
[3,4,6]
[9,8,7]
What I need is to be able to loop through matrix A and determine if there are any rows in matrix B that are "similar" as in the x value is within a range (-2,2) of the x value of A (Likewise with the y-coordinate and radius).If it satisfies all three of these conditions, it will be added to a new matrix with the values that were in A. So for example I would need the above data to return...
ans =
[1,4,3]
[8,8,7]
Please help and thank you in advance to anyone willing to take the time!
You can use ismembertol.
result = A(ismembertol(A,B,2,'ByRows',1,'DataScale',1),:)
Manual method
A = [1,4,3;
8,8,7;
3,6,3];
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]; % example matrices
t = 2; % desired threshold
m = any(all(abs(bsxfun(#minus, A, permute(B, [3 2 1])))<=t, 2), 3);
result = A(m,:);
The key is using permute to move the first dimension of B to the third dimension. Then bsxfun computes the element-wise differences for all pairs of rows in the original matrices. A row of A should be selected if all the absolute differences with respect to any column of B are less than the desired threshold t. The resulting variable m is a logical index which is used for selecting those rows.
Using pdist2 (Statistics and Machine Learning Toolbox)
m = any(pdist2(A, B, 'chebychev')<=t, 2);
result = A(m,:);
Ths pdist2 function with the chebychev option computes the maximum coordinate difference (Chebychev distance, or L∞ metric) between pairs of rows.
With for loop
It should work:
A = [1,4,3;
8,8,7;
3,6,3]
B = [1,3,3;
1,92,3;
4,57,8;
5,62,1;
3,4,6;
9,8,7]
index = 1;
for i = 1:size(A,1)
C = abs(B - A(i,:));
if any(max(C,[],2)<=2)
out(index,:) = A(i,:);
index = index + 1
end
end
For each row of A, computes the absolute difference between B and that row, then checks if there exists a row in which the maximum is less than 2.
Without for loop
ind = any(max(abs(B - permute(A,[3 2 1])),[],2)<=2);
out = A(ind(:),:);

Shifting repeating rows to a new column in a matrix

I am working with a n x 1 matrix, A, that has repeating values inside it:
A = [0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4]
which correspond to an n x 1 matrix of B values:
B = [2;4;6;8;10; 3;5;7;9;11; 4;6;8;10;12; 5;7;9;11;13]
I am attempting to produce a generalised code to place each repetition into a separate column and store it into Aa and Bb, e.g.:
Aa = [0 0 0 0 Bb = [2 3 4 5
1 1 1 1 4 5 6 7
2 2 2 2 6 7 8 9
3 3 3 3 8 9 10 11
4 4 4 4] 10 11 12 13]
Essentially, each repetition from A and B needs to be copied into the next column and then deleted from the first column
So far I have managed to identify how many repetitions there are and copy the entire column over to the next column and then the next for the amount of repetitions there are but my method doesn't shift the matrix rows to columns as such.
clc;clf;close all
A = [0;1;2;3;4;0;1;2;3;4;0;1;2;3;4;0;1;2;3;4];
B = [2;4;6;8;10;3;5;7;9;11;4;6;8;10;12;5;7;9;11;13];
desiredCol = 1; %next column to go to
destinationCol = 0; %column to start on
n = length(A);
for i = 2:1:n-1
if A == 0;
A = [ A(:, 1:destinationCol)...
A(:, desiredCol+1:destinationCol)...
A(:, desiredCol)...
A(:, destinationCol+1:end) ];
end
end
A = [...] retrieved from Move a set of N-rows to another column in MATLAB
Any hints would be much appreciated. If you need further explanation, let me know!
Thanks!
Given our discussion in the comments, all you need is to use reshape which converts a matrix of known dimensions into an output matrix with specified dimensions provided that the number of elements match. You wish to transform a vector which has a set amount of repeating patterns into a matrix where each column has one of these repeating instances. reshape creates a matrix in column-major order where values are sampled column-wise and the matrix is populated this way. This is perfect for your situation.
Assuming that you already know how many "repeats" you're expecting, we call this An, you simply need to reshape your vector so that it has T = n / An rows where n is the length of the vector. Something like this will work.
n = numel(A); T = n / An;
Aa = reshape(A, T, []);
Bb = reshape(B, T, []);
The third parameter has empty braces and this tells MATLAB to infer how many columns there will be given that there are T rows. Technically, this would simply be An columns but it's nice to show you how flexible MATLAB can be.
If you say you already know the repeated subvector, and the number of times it repeats then it is relatively straight forward:
First make your new A matrix with the repmat function.
Then remap your B vector to the same size as you new A matrix
% Given that you already have the repeated subvector Asub, and the number
% of times it repeats; An:
Asub = [0;1;2;3;4];
An = 4;
lengthAsub = length(Asub);
Anew = repmat(Asub, [1,An]);
% If you can assume that the number of elements in B is equal to the number
% of elements in A:
numberColumns = size(Anew, 2);
newB = zeros(size(Anew));
for i = 1:numberColumns
indexStart = (i-1) * lengthAsub + 1;
indexEnd = indexStart + An;
newB(:,i) = B(indexStart:indexEnd);
end
If you don't know what is in your original A vector, but you do know it is repetitive, if you assume that the pattern has no repeats you can use the find function to find when the first element is repeated:
lengthAsub = find(A(2:end) == A(1), 1);
Asub = A(1:lengthAsub);
An = length(A) / lengthAsub
Hopefully this fits in with your data: the only reason it would not is if your subvector within A is a pattern which does not have unique numbers, such as:
A = [0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0;]
It is worth noting that from the above intuitively you would have lengthAsub = find(A(2:end) == A(1), 1) - 1;, But this is not necessary because you are already effectively taking the one off by only looking in the matrix A(2:end).

Shuffle a vector of repeated numbers so the numbers do not repeat in MATLAB

Okay, so I have a script that will produce my vector of repeated integers of a certain interval, but now theres a particular instance where I need to make sure that once it is shuffled, the numbers do not repeat. So for example, I produced a vector of repeating 1-5, 36 times, shuffled. How do I ensure that there are no repeated numbers after shuffling? And to make things even more complex, I need to produce two such vectors that do not ever have the same value at the same index. For example, lets say 1:5 was repeated twice for these vectors, so then this would be what I'm looking for:
v1 v2
4 2
2 4
3 2
5 3
4 5
1 4
5 1
1 5
3 1
2 3
I made that right now by taking an example of 1 vector and just shifting it off by 1 to create another vector that will satisfy the requirements, but in my situation, that wont actually work because I can't have them be systematically dependent like that.
So I tried a recursive technique to make the script start over if the vectors did not make the cut and as expected, that did not go over so well. I hit my maximum recursive iterations and I've realized this is clearly not the way to go. Is there some other alternative?
EDIT:
So I found a way to satisfy some of the conditions I needed above in the following code:
a = nchoosek(1:5,2);
b = horzcat(a(:,2),a(:,1));
c = vertcat(a,b);
cols = repmat(c,9,1);
cols = cols(randperm(180),:);
I just need to find a way to shuffle cols that will also enforce no repeating numbers in columns, such that cols(i,1) ~= cols(i+1,1) and cols(i,2) ~= cols(i+1,2)
This works, but it probably is not very efficient for a large array:
a = nchoosek(1:5, 2);
while (any(a(1: end - 1, 1) == a(2: end, 1)) ...
|| any(a(1: end - 1, 2) == a(2: end, 2)))
random_indices = randperm(size(a, 1));
a = a(random_indices, :);
end
a
If you want something faster, the trick is to logically insert each row in a place where your conditions are satisfied, rather than randomly re-shuffling. For example:
n1 = 5;
n2 = 9;
a = nchoosek(1:n1, 2);
b = horzcat(a(:,2), a(:,1));
c = vertcat(a, b);
d = repmat(c, n2, 1);
d = d(randperm(n1 * n2), :);
% Perform an "insertion shuffle"
for k = 2: n1 * n2
% Grab row k from array d. Walk down the rows until a position is
% found where row k does not repeat with its upstairs or downstairs
% neighbors.
m = 1;
while (any(d(k,:) == d(m,:)) || any(d(k,:) == d(m+1,:)))
m = m + 1;
end
% Insert row k in the proper position.
if (m < k)
ind = [ 1: m k m+1: k-1 k+1: n1 * n2 ];
else
ind = [ 1: k-1 k+1: m k m+1: n1 * n2 ];
end
d = d(ind,:);
end
d
One way to solve this problem is to think both vectors as being created as follows:
For every row of arrays v1 and v2
Shuffle the array [1 2 3 4 5]
Set the values of v1 and v2 at the current row with the first and second value of the shuffle. Both values will always be different.
Code:
s = [1 2 3 4 5];
Nrows = 36;
solution = zeros(Nrows,2);
for k=1:Nrows
% obtain indexes j for shuffling array s
[x,j] = sort(rand(1,5));
%row k takes the first two values of shuffled array s
solution(k,1:2) = s(j(1:2));
end
v1 = solution(:,1);
v2 = solution(:,2);
Main edit: random => rand,
With this method there is no time wasted in re-rolling repeated numbers because the first and second value of shuffling [1 2 3 4 5] will always be different.
Should you need more than two arrays with different numbers the changes are simple.

Using elements of a vector to set elements of a matrix

I have a vector whose elements identify the indices (per column) that I need to set in a different matrix. Specifically, I have:
A = 7
1
2
and I need to create a matrix B with some number of rows of zeros, except for the elements identified by A. In other words, I want B:
B = zeros(10, 3); % number of rows is known; num columns = size(A)
B(A(1), 1) = 1
B(A(2), 2) = 1
B(A(3), 3) = 1
I would like to do this without having to write a loop.
Any pointers would be appreciated.
Thanks.
Use linear indexing:
B = zeros(10, 3);
B(A(:).'+ (0:numel(A)-1)*size(B,1)) = 1;
The second line can be written equivalently with sub2ind (may be a little slower):
B(sub2ind(size(B), A(:).', 1:numel(A))) = 1;