find multiple closest values in a vector at once without "going over" (Matlab) - matlab

Let's say I have the following 2 vectors:
a = [1 3 5 7 8 9 10 15 16];
b = [2 4 14];
Is there a function I can use so that, for every element in b, I can find the index of the closest value to that element in a without "going over" the value I'm searching for? The expected output would be:
[1 2 7]
I have found previous answers that address finding the closest value, but not the closest value without exceeding the values being searched for.

Edited: now with a one-liner:
[~,index] = max(repmat(a,numel(b),1) + 0./bsxfun(#le,a,b'), [], 2)
'#% The 0./(0 or 1) creates a NaN mask where the condition
#% isn't met, leaving only the desired values in the matrix
#% max ignores NaNs, conveniently
This isn't a built-in function but it is pretty simple (link on ideone):
a = [1 3 5 7 8 9 10 15 16];
b = [2 4 14];
c = bsxfun(#minus,b',a) #%' transpose b
c(c<0)=nan; #% discard the values in a greater than b
[~,ci] = min(c,[],2) #% min ignores nan
d = a(ci) #% if you want the actual values of a
output:
c =
1 -1 -3 -5 -6 -7 -8 -13 -14
3 1 -1 -3 -4 -5 -6 -11 -12
13 11 9 7 6 5 4 -1 -2
ci =
1
2
7
d =
1 3 10

Related

Construct a matrix from a vector periodically [duplicate]

This question already has answers here:
How to create a symmetric matrix where each row/column is a subset of a known vector [duplicate]
(2 answers)
How do I generate the following matrix and vector from the given input data in MATLAB?
(1 answer)
Closed 5 years ago.
I have a vectorb with 30 entries.
I want to avoid using a for loop to construct a matrix like this:
where b_i is the i-th entry of the vector b.
For example, define the vector
b = [2 6 -7 3 1 -4 -1 1 11 8 -4 9 2 0 2 -1 0 4 4 4 2 -4 2 5 1 3 2 -1 1 -2]
where I tried by using for loop is:
A = zeros(5,5);
for i = 1:5
A(i) = b(i+5);
A(i+5) = b(i+6);
A(i+10) = b(i+7);
A(i+15) = b(i+8);
A(i+20) = b(i+9);
end
The result is
Is there a faster and more general method to generate this matrix?
You can use toeplitz:
A=fliplr(toeplitz(b(10:14),b(10:-1:6))
A =
-4 -1 1 11 8
-1 1 11 8 -4
1 11 8 -4 9
11 8 -4 9 2
8 -4 9 2 0
By the way, the indices here are 6 to 14 as in your example, and not 7 to 15 as in the picture. You can change it to your preferred purpose.
Your instinct is to avoid for loops. This is a good intuition to develop as a MATLAB programmer, but it's not always the quickest option. As can be seen in my answer to a very similar question, a for loop may be the quickest way to generate this type of matrix.
Possibly the shortest method to write would use hankel
A = b(hankel(7:11, 11:15));
Output:
>> ans =
-1 1 11 8 -4
1 11 8 -4 9
11 8 -4 9 2
8 -4 9 2 0
-4 9 2 0 2
Equivalent result (but quicker processing as seen in the previously linked answer)
A = hankel(b(7:11), b(11:15));
As Adiel said, there is a difference between the indices you showed in the image of the matrix and the indices you used to create your example. This uses the former.
Not as nice as using toeplitz, but a bit clearer to see what's going on:
b = [2 6 -7 3 1 -4 -1 1 11 8 -4 9 2 0 2 -1 0 4 4 4 2 -4 2 5 1 3 2 -1 1 -2];
n = 5; % window size
% StartIdx = 4; % Starting index of your window
AddVec = repelem(1:n,5)+StartIdx; % create addition vector
IdxVec = repmat(1:n,1,n); % Initialise index vector
IdxVec = AddVec+IdxVec; % add to let the window "slide"
c = b(IdxVec); % create a new vector
d = reshape(c,n,[]) % Reshape to get the desired matrix
d =
6 -7 3 1 -4
-7 3 1 -4 -1
3 1 -4 -1 1
1 -4 -1 1 11
-4 -1 1 11 8
Note that I didn't use a starting index in my run of the matrix. Adjust that parameter according to your needs.

Matlab : How to make label output from interval data?

So i have this data:
A=
2
4
8
9
4
6
1
3
And 3 interval
B=
1 4
5 8
9 12
How to make an output like this
Output=
1
1
2
3
1
2
1
1
The output is based on the interval
you can solve it in several ways. for example, with arrayfun:
A = [2 4 8 9 4 6 1 3].';
B = [1 4;
5 8;
9 12];
res = arrayfun(#(x) find((x >= B(:,1)) & (x <= B(:,2))),A);
If the interval always has the same length, as in your case 4, you can solve it as follows:
Output=ceil(A/4);
If it is not the case, and if not all numbers necessarily fall between any of the intervals, you can compute it as follows. A zero is outputted if a number does not fall within any of the intervals.
% example entry
A=[2 3 4 8 9 4 6 1 3]';
B=[1 4;5 7;9 12]';
Arep=A(:,ones(size(B,2),1)); % replicate array (alternatively use repmat)
Alog=Arep>=B(1,:)&Arep<=B(2,:); % conditional statements, make logical array
Output=Alog*(1:size(B,2))'; % matrix product with natural array to obtain indices

Convert vector to circular space with different median

I have a vector of 10 random numbers from 1 to 11 (median of the vector 1:11 is 6).
min = 1;
max = 11;
nVector = 10;
VectorRand = randi([min max],1,nVector);
I would like to convert the values in VectorRand to a circular array of 11 values, but with a different median. For example, median 2:
-5(8) -4(9) -3(10) -2(11) -1(1) 0(2) +1(3) +2(4) +3(5) +4(6) +5(7)
Result: 8 9 10 11 1 - 2 - 3 4 5 6 7
in the case of VectorRand = [1 3 8 4 6 8 5 2 6 8 10]
Result: -1 1 -5 2 4 -5 3 0 4 -5 -3
where the median (2 in this case) becomes zero and all the other values are translated in terms of distance from the median (e.g. 8=-5, 9=-4 10=-3 and so on).
The description is confusing, but here's my guess as to what you want:
>> % Set parameters
>> minVal = 1;
>> maxVal = 11;
>> newMedian = 2;
>> % Determine mapping
>> currMedian = median(minVal:maxVal);
>> map = circshift((minVal:maxVal) - currMedian, [0 newMedian - currMedian]);
This is shifting the range of values so that the median is 0 (this will eventually give you the distances from the target median), and then rotating it so the target median is at the center value of the ordering of values. Now, with the mapping built, you can map your vector to this new circulant ring:
>> VectorRand = [1 3 8 4 6 8 5 2 6 8 10];
>> Result = map(VectorRand)
Result =
-1 1 -5 2 4 -5 3 0 4 -5 -3

How to align vectors with asynchronous time stamp in matlab?

I would like to align and count vectors with different time stamps to count the corresponding bins.
Let's assume I have 3 matrix from [N,edges] = histcounts in the following structure. The first row represents the edges, so the bins. The second row represents the values. I would like to sum all values with the same bin.
A = [0 1 2 3 4 5;
5 5 6 7 8 5]
B = [1 2 3 4 5 6;
2 5 7 8 5 4]
C = [2 3 4 5 6 7 8;
1 2 6 7 4 3 2]
Now I want to sum all the same bins. My final result should be:
result = [0 1 2 3 4 5 6 7 8;
5 7 12 16 ...]
I could loop over all numbers, but I would like to have it fast.
You can use accumarray:
H = [A B C].'; %//' Concatenate the histograms and make them column vectors
V = [unique(H(:,1)) accumarray(H(:,1)+1, H(:,2))].'; %//' Find unique values and accumulate
V =
0 1 2 3 4 5 6 7 8
5 7 12 16 22 17 8 3 2
Note: The H(:,1)+1 is to force the bin values to be positive, otherwise MATLAB will complain. We still use the actual bins in the output V. To avoid this, as #Daniel says in the comments, use the third output of unique (See: https://stackoverflow.com/a/27783568/2732801):
H = [A B C].'; %//' stupid syntax highlighting :/
[U, ~, IU] = unique(H(:,1));
V = [U accumarray(IU, H(:,2))].';
If you're only doing it with 3 variables as you've shown then there likely aren't going to be any performance hits with looping it.
But if you are really averse to the looping idea, then you can do it using arrayfun.
rng = 0:8;
output = arrayfun(#(x)sum([A(2,A(1,:) == x), B(2,B(1,:) == x), C(2,C(1,:) == x)]), rng);
output = cat(1, rng, output);
output =
0 1 2 3 4 5 6 7 8
5 7 12 16 22 17 8 3 2
This can be beneficial for particularly large A, B, and C variables as there is no copying of data.

Inserting multiple rows into matrix, shifting existing rows

Question:
I'm looking for the most efficient way to insert multiple rows R into a matrix M before specified rows I, while shifting existing rows down.
Example:
M = [1 1 1 1;
2 2 2 2;
3 3 3 3;
4 4 4 4;
5 5 5 5];
I = [1 3 3 5];
R = [-6 -6 -6 -6;
-7 -7 -7 -7;
-8 -8 -8 -8
-9 -9 -9 -9];
The result should be the matrix:
[-6 -6 -6 -6
1 1 1 1
2 2 2 2
-7 -7 -7 -7
-8 -8 -8 -8
3 3 3 3
4 4 4 4
-9 -9 -9 -9
5 5 5 5]
This functionality is available on the file exchange. What it essentially does is:
ind = [1:size(M, 1) I-1];
[~, ind] = sort(ind);
MR = [M; R];
MR = MR(ind,:);
The following will work, but I think only if the indices in I are sorted in increasing order (if not, you can always add some sorting steps first). In any case, I would test it with "real" data:
[m,n] = size(M);
l = length(I);
MM = zeros(m+l,n);
MM(I+(1:l)-1,:) = R; % I+(1:l)-1 are the row indices of the final matrix in which to insert the the rows of R
MM(~ismember(1:(l+m),I+(1:l)-1),:) = M; % ~ismember(...) are the other row indices (using logical indexing)
This gives:
>> MM
MM =
-6 -6 -6 -6
1 1 1 1
2 2 2 2
-7 -7 -7 -7
-8 -8 -8 -8
3 3 3 3
4 4 4 4
-9 -9 -9 -9
5 5 5 5
This could be one approach -
R_rowind = cumsum([I(1) diff(I)+1]) %// Row indices where rows from R are
%// to be inserted in the output matrix
rowidx_arr = 1:size(M,1)+size(R,1) %// array of [1 : number_of_rows_in_output]
out(setdiff(rowidx_arr,R_rowind),:) = M %// Insert rows from M into output array
out(R_rowind,:) = R %// Insert rows from R into output array
Please note that for inserting rows from M into output array, you can this bsxfun based alternative approach as well -
out(all(bsxfun(#ne,rowidx_arr,R_rowind'),1),:) = M