Matlab random number range - matlab

I am struggling generating a random number within the range of x.
So say x is 4 the range would be -2 to 2 and if it was 6 then -3 to 3.
I know it is
rand() * something + somethingelse

You have to take out the mean of rand*x, that is x/2:
x = [1 2 3 4 5 6 7]
rand(1,numel(x)).*x-x/2
ans =
0.4172 -0.4283 0.7716 1.0149 -0.5978 0.4069 -2.9690

From where you left it is not hard to find the solution:
rand() * something + somethingelse
From left to right:
rand() : From 0 to 1
We want to make the range 4 times as wide, so we do:
rand()*4 : From 0 to 4
Now the width is correct, we just need to give it the correct location:
rand()*4-2: From -2 to 2

Related

Matlab: Using a matrix as a mask to perform elementwise operations

In Matlab, I have two matrices: one with integers,x, and one with booleans, y:
x =
2 4 2
3 3 1
4 1 5
y =
0 0 1
1 1 0
1 0 1
What I now want to do is to assign some elements of x to 5, and I want to use y as a mask to determine which elements should be set to 5. So elements with a corresponding value of 0 in y should remain as they are in x, but those with a corresponding value of 1 in y should be set to 5. Therefore, the output should be:
2 4 5
5 5 1
5 1 5
I have tried the following:
x(y) = 5
Which gives me the error:
Subscript indices must either be real positive integers or logicals.
And I have also tried:
y(x) = 5
Which gives me the following:
5 5 1
5 1 0
5 0 1
Can somebody please explain what is going on here, and what I need to do to get my desired result?
The error you've got is due to the fact that, apparently, y is of type double while, in this case, it should be of type logical
You could try:
x(logical(y))=5
Hope this helps
Its not a fancy solution but will solve your problem
>> x = [ 2 4 2;3 3 1;4 1 5];
y = logical([ 0 0 1;1 1 0;1 0 1]);
f = x(:);
f(y(:)) = 5;
x = reshape(f,size(x))
x =
2 4 5
5 5 1
5 1 5
>>
x(find(y)) = 5; should work fine.

Vector-defined cross product application matrix and vectorization in Matlab

I ran into an operation I cannot seem to achieve via vectorization.
Let's say I want to find the matrix of the application defined by
h: X -> cross(V,X)
where V is a predetermined vector (both X and V are 3-by-1 vectors).
In Matlab, I would do something like
M= cross(repmat(V,1,3),eye(3,3))
to get this matrix. For instance, V=[1;2;3] yields
M =
0 -3 2
3 0 -1
-2 1 0
Let's now suppose that I have a 3-by-N matrix
V=[V_1,V_2...V_N]
with each column defining its own cross-product operation. For N=2, here's a naive try to find the two cross-product matrices that V's columns define
V=[1,2,3;4,5,6]'
M=cross(repmat(V,1,3),repmat(eye(3,3),1,2))
results in
V =
1 4
2 5
3 6
M =
0 -6 2 0 -3 5
3 0 -1 6 0 -4
-2 4 0 -5 1 0
while I was expecting
M =
0 -3 2 0 -6 5
3 0 -1 6 0 -4
-2 1 0 -5 4 0
2 columns are inverted.
Is there a way to achieve this without for loops?
Thanks!
First, make sure you read the documentation of cross very carefully when dealing with matrices:
It says:
C = cross(A,B,DIM), where A and B are N-D arrays, returns the cross
product of vectors in the dimension DIM of A and B. A and B must
have the same size, and both SIZE(A,DIM) and SIZE(B,DIM) must be 3.
Bear in mind that if you don't specify DIM, it's automatically assumed to be 1, so you're operating along the columns. In your first case, you specified both the inputs A and B to be 3 x 3 matrices. Therefore, the output will be the cross product of each column independently due to the assumption that DIM=1. As such, you expect that the i'th column of the output contains the cross product of the i'th column of A and the i'th column of B and the number of rows is expected to be 3 and the number of columns needs to match between A and B.
You're getting what you expect because the first input A has [1;2;3] duplicated correctly over the columns three times. From your second piece of code, what you're expecting for V as the first input (A) looks like this:
V =
1 1 1 4 4 4
2 2 2 5 5 5
3 3 3 6 6 6
However, when you do repmat, you are in fact alternating between each column. In fact, you are getting this:
V =
1 4 1 4 1 4
2 5 2 5 2 5
3 6 3 6 3 6
repmat tile matrices together and you specified that you wanted to tile V horizontally three times. That's obviously not correct. This explains why the columns are swapped because the second, fourth and sixth columns of V actually should appear at the last three columns instead. As such, the ordering of your input columns is the reason why the output appears swapped.
As such, you need to re-order V so that the first three vectors are [1;2;3], followed by the next three vectors as [4;5;6] after. Therefore, you can generate your original V matrix first, then create a new matrix such that the odd column comes first in a group of three, followed by the even column in a group of three after:
>> V = [1,2,3;4,5,6].';
>> V = V(:, [1 1 1 2 2 2])
V =
1 1 1 4 4 4
2 2 2 5 5 5
3 3 3 6 6 6
Now use V with cross and maintain the same second input:
>> M = cross(V, repmat(eye(3), 1, 2))
M =
0 -3 2 0 -6 5
3 0 -1 6 0 -4
-2 1 0 -5 4 0
Looks good to me!

Find Value at a given Orientation in Matrix

In Matlab I've matrix where, in a previous stage of my code, an specific element was chosen. From this point of the matrix I would like to find a maximum, not just the maximum value between all its surounding neighbours for a given radius, but the maximum value at a given angle of orientation. Let me explain this with an example:
This is matrix A:
A =
0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1
The element chosen in the first stage is the 4 in A(2,4), and the next element should be the maximum value with, for example, a 315 degrees angle of orientation, that is the 7 in A(5,7).
What I've done is, depending on the angle, subdivide matrix A in different quadrants and make a new matrix (an A's submatrix) with only the values of that quadrant.
So, for this example, the submatrix will be A's 4th quadrant:
q_A =
4 3 2 8 1 0
3 3 2 2 1 0
3 2 2 2 1 0
3 3 2 7 2 1
2 3 2 3 2 1
And now, here is my question, how can I extract the 7?
The only thing I've been able to do (and it works) is to find all the values over a threshold value and then calculate how those points are orientated. Then, saving all the values that have a similar orientation to the given one (315 degrees in this example) and finally finding the maximum among them. It works but I guess there could be a much faster and "cleaner" solution.
This is my theory, but I don't have the image processing toolbox to test it. Maybe someone who does can comment?
%make (r,c) the center by padding with zeros
if r > size(A,1)/2
At = padarray(A, [size(A,1) - r], 0, 'pre');
else
At = padarray(A, [r-1], 0 'post');
if c > size(A,2)/2
At = padarray(At, [size(A,2) - c], 0, 'pre');
else
At = padarray(At, [c-1], 0 'post');
%rotate by your angle (maybe this should be -angle or else 360-angle or 2*pi-angle, I'm not sure
Ar = imrotate(At,angle, 'nearest', 'loose'); %though I think nearest and loose are defaults
%find the max
max(Ar(size(Ar,1)/2, size(Ar,2)/2:end); %Obviously you must adjust this to handle the case of odd dimension sizes.
Also depending on your array requirements, padding with -inf might be better than 0
The following is a relatively inexpensive solution to the problem, although I found wrapping my head around the matrix coordinate system a real pain, and there is probably room to tidy it up somewhat. It simply traces all matrix entries along a line around the starting point at the supplied angle (all coordinates and angles are of course based on matrix index units):
A = [ 0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1 ];
alph = 315;
r = 2;
c = 4;
% generate a line through point (r,c) with angle alph
[nr nc] = size(A);
x = [1:0.1:nc]; % overkill
m = tan(alph);
b = r-m*c;
y = m*x + b;
crd = unique(round([y(:) x(:)]),'rows');
iok = find((crd(:,1)>0) & (crd(:,1)<=nr) & (crd(:,2)>0) & (crd(:,2)<=nc));
crd = crd(iok,:);
indx=sub2ind([nr,nc],crd(:,1),crd(:,2));
% find max and position of max
[val iv]=max(A(indx)); % <-- val is the value of the max
crd(iv,:) % <-- matrix coordinates (row, column) of max value
Result:
val =
7
iv =
8
ans =
5 7

sort in matlab and assign ranking

Hi I need to sort a vector and assign a ranking for the corresponding sorting order. I'm using sort function [sortedValue_X , X_Ranked] = sort(X,'descend');
but the problem is it assigns different ranks for the same values (zeros).
i.e. x = [ 13 15 5 5 0 0 0 1 0 3] and I want zeros to take the same last rank which is 6 and fives needs to share the 3rd rank etc..
any suggestions?
The syntax [sortedValues, sortedIndexes] = sort(x, 'descend') does not return rank as you describe it. It returns the indexes of the sorted values. This is really useful if you want to use the sort order from one array to rearrange another array.
As suggested by #user1860611, unique seems to do what you want, using the third output as follows:
x = [ 13 15 5 5 0 0 0 1 0 3];
[~, ~, forwardRank] = unique(x);
%Returns
%forwardRank =
% 5 6 4 4 1 1 1 2 1 3
To get the order you want (decending) you'll need to reverse the order, like this:
reverseRank = max(forwardRank) - forwardRank + 1
%Returns
%reverseRank =
% 2 1 3 3 6 6 6 5 6 4
You may be done at this point. But you may want to sort these into the into an acsending order. This is a reorder of the reverseRank vector which keeping it in sync with the original x vector, which is exactly what the 2nd argument of sort is desined to help with. So we can do something like this:
[xSorted, ixsSort] = sort(x, 'descend'); %Perform a sort on x
reverseRankSorted = reverseRank(ixsSort); %Apply that sort to reverseRank
Which generates:
xSorted = 15 13 5 5 3 1 0 0 0 0
reverseRankSorted = 1 2 3 3 4 5 6 6 6 6
tiedrank.m might be the thing you are looking for.
>> x = round(rand(1,5)*10)
x =
8 7 3 10 0
>> tiedrank(x)
ans =
4 3 2 5 1

Permutation with duplicates

I'm using MATLAB and trying to run a randomized block of different conditions for a psych experiment. I have 'levels' I want to try out, 3 times each. So I would like to basically permute three copies of the vector, all together. So far I have:
levels = [0 0.25 0.5 0.75 1]
permutationIndices = randperm(length(levels)*3)
... and then here the natural solution to me would be a mapping function that maps whatever is in levels to the corresponding location using mod 5, so for instance in permutationIndices, wherever there is a 1, 6, or 11, the number 0 would be slotted in. How can I do this (or, is there a neater way?) Thanks.
mod(randperm(15)-1,5)+1
This outputs
5 4 5 1 3 2 1 1 4 3 3 2 4 5 2
or another run:
3 4 4 2 2 5 3 2 4 1 3 1 5 5 1
which you can use to get the corresponding elements from the levels vector:
output = levels(mod(randperm(15)-1,5)+1)
Try:
%# three copies of levels
x = repmat(1:numel(levels),1,3)
%# random permuation
[~,ord] = sort(rand(size(x)));
output = x(ord)
For example:
output =
3 2 4 2 5 4 5 2 3 1 5 3 1 1 4
The mod approach is probably the simplest. I would generate permuted indices for an array of length nlevels * ntimes, and then mod those indices to index into an array of your actual level values.
level_values = [0 0.25 0.5 0.75 1];
nlevels = numel(level_values);
ntimes = 3;
lv_inds = 1 + mod( randperm(ntimes*nlevels - 1), nlevels);
levels = level_values( lv_inds );
The odd 1 + and - 1 offsets are necessary to make the mod call work with Matlab's array indexing (which starts at 1).