Populating all the values of a vector with values from another vector at places from still another vector [duplicate] - matlab

This question already has answers here:
Element-wise array replication according to a count [duplicate]
(4 answers)
Closed 8 years ago.
I have two vectors representing the edges and levels some continuous data (this is simulated at the moment).
edges = [50, 120, 170, 200, 220, 224, 250]
levels = [24,3,30,0,36,0]
How would I create a vector that has 24 for the first 50 entries, 3 for the subsequent 120 etc?
I've tried
psd = zeros(1,250)
psd(edges) = levels
but this just puts a single value at the relevant position - it's not quite what I want.

Your description and code don't quite match. Do you want psd to have 250 elements or 1234 (sum(edges), as your use of subsequent would imply)? If it's the latter, you can simply modify edges = cumsum(edges). Secondly, you don't have enough levels for the given edges.
Here's a solution without the use of loops.
edges = [50, 120, 170, 200, 220, 224, 250];
levels = [24,3,30,0,36,0,nan]; % padded with nan for equal length
idxs = length(levels) - sum(bsxfun(#(x,y) x < y,1:max(edges),edges'+1))+1;
pds = levels(idxs)
Or even simpler:
idxs = zeros(1,max(edges));
idxs(edges(1:end-1)+1) = 1;
psd = levels(cumsum(idxs)+1)

Here is a little solution, if edges is really the edges of your intervals:
Expand the vector levels with 0 as a first element:
levels = [0, 24, 3, 30, 0, 36, 0];
Then you can do the following:
psd = zeros(1,250);
psd(edges(2:end)-1) = diff(levels);
psd = cumsum(psd);
You put at the interval limits the offset betwwen two consecutive values, 0 elsewhere. When summing up, you have what you expect.

Involves a loop but it should be OK. Also, your edges and levels need to be of equal length.
v = [];
for i = 1:numel(edges)
v = [v;level(i)*ones(edges(i),1)];
end
Here's a more efficient version which takes care of the allocation overhead.
v = zeros(sum(edges),1);
c = [0 cumsum(edges)];
for i = 1:numel(edges)-1
v( c(i)+1:c(i+1) ) = levels(i)*ones(edges(i),1);
end
Just remember ; loops aren't always evil in today's MATLAB. Sometimes, it's the simplest (and clearest) solution.

You can use this run-length decoding utility FEX rude(), which implements the approach showed by Bentoy13:
% example inputs
edges = [2, 3, 1];
levels = [24,3,30];
the result
rude(edges, levels)
ans =
24 24 3 3 3 30

Inspired by an answer by gnovice you can use this:
psd = levels(cumsum(sparse(1,cumsum([1 edges(1:end-1)]),1,1,sum(edges))));
To make it easier you can create an anonymous function and resuse it instead:
populate = #(L,E) L(cumsum(sparse(1,cumsum([1 E(1:end-1)]),1,1,sum(E))));
psd = popultae(levels, edges);

Related

Bitwise or over an array in Matlab?

I have a large array of binary numbers, and I want to do a bitwise OR over one dimension of the array:
X = [ 192, 96, 96, 2, 3
12, 12, 128, 49, 14
....
];
union_of_bits_on_dim2 = [
bitor(X(:,1), bitor(X(:,2), bitor(X(:,3), ... )))
];
ans =
[ 227
191
... ]
Is there a simple way of doing this? I'm actually working on an n-dimensional array. I tried bi2de but it flattens out my array and so the subscripting becomes complicated.
I could do it easily if matlab had a fold function but I don't think it does.
OK #Divakar asked for runnable code so to make it clear here is a long-winded version that might work for a 2D array.
function U=union_of_bits_on_dim2(X)
U=zeros(size(X,1),1);
for i=1:size(X,2)
U=bitor(U,X(:,i));
end
Surely it be done without looping? I was of course hoping that bitor could take arbitrary numbers of arguments. Then it could have been done with mat2cell.
One vectorized approach -
[m,n] = size(X) %// Get size of input array
bd = dec2bin(X)-'0' %// Get binary digits
%// Get cumulative "OR-ed" version with ANY(..,1)
cum_or = reshape(any(permute(reshape(bd,m,n,[]),[2 3 1]),1),8,[])
%// Finally convert to decimals
U = 2.^(7: -1:0)*cum_or
I don't know any function that can do that automatically. However you can loop over the dimension you are interested in:
function result = bitor2d(A)
result = A(1,:);
for i=2:size(A,1)
result = bitor(result,A(i,:));
end
end
If your array has more than 2 dimensions, then you need to prepare it to have only 2.
function result = bitornd(A,whichdimension)
B = shiftdim(A,whichdimension-1); % change dimensions order
s = size(B);
B = reshape(B,s(1),[]); % back to the original shape
result = bitor2d(B);
s(1) = 1;
result = reshape(result,s); % back to the original shape
result = shiftdim(result,1-whichdimension); % back to the original dimension order
end

how to find intersection points when lines are created from an array [duplicate]

This question already has answers here:
Finding where plots may cross with octave / matlab
(2 answers)
Closed 8 years ago.
The intersections functioned recommended below worked great for arrays up to 8000 values but if I had an array of 100000 values or more I would run out of memory, (and I have 16gig of ram) this most likely was caused due to the repmat command with the intersections function.
I'm trying to find the intersection points of lines created from an array. but keep getting an error "fzero: not a valid initial bracketing" I'm using octave 3.8.1 (which is an open source version of matlab) The image below is what I'm trying to get with the black circles at the intersection points.
Do I need to have the fzero in a for loop to loop through the array of x and y values?
clear all,clf, clc,tic
%freq array here
x1=[20,30,40,50,60,70,80]';
x2=[20,30,40,50,60,70,80]';
y1=[2,4,3,7,1,8,4]';
y2=abs(y1-max(y1)); %used to switch high and low amplitude of freq
%fit linear polynomial
p1 = polyfit(x1,y1,1);
p2 = polyfit(x2,y2,1);
%calculate intersection
x_intersect = fzero(#(x) polyval(p1-p2,x),3);
y_intersect = polyval(p1,x_intersect);
line(x1,y1);
hold on;
line(x2,y2);
plot(x_intersect,y_intersect,'r*')
The intersections functioned recommended below worked great for arrays up to 8000 values but if I had an array of 100000 values or more I would run out of memory, (and I have 16gig of ram) this most likely was caused due to the repmat command with the intersections function.
So now I'm trying to:
1) cycle though each row in the array which represents a line
linea1-6 xvalues = 20 to 30, 30 to 40, 40 to 50, 50 to 60, 60 to 70, 70 to 80
linea1-6 yvalues =2 to 4, 4 to 3, 3 to 7, 7 to 1, 1 to 8, 8 to 4
lineb1-6 xvalues = 20 to 30, 30 to 40, 40 to 50, 50 to 60, 60 to 70, 70 to 80
lineb1-6 yvalues =6 to 4, 4 to 5, 5 to 1, 1 to 7, 7 to 0, 0 to 4
**I'm having problems coding the for loop to work with polyfit and fzero**
2) store the intersection values found for each line into an array.
This should solve running out of memory issues when using large arrays
I am not sure why you are not using the solution given to your previous question Finding where plots may cross with octave / matlab
But here's what's happening here (from the doc):
If X0 is a single scalar then several nearby and distant values are
probed in an attempt to obtain a valid bracketing. If this is not
successful, the function fails.
So what's happening is that your initial guess of 3 is too far away from the solution. Try that instead:
>> x_intersect = fzero(#(x) polyval(p1-p2,x),30)
x_intersect = 46.667
However, I am not sure what you are trying to do by fitting a first degree polynomial to your data, it doesn't make sense to me...

Random numbers with constant sum in MATLAB [duplicate]

[I'm splitting a population number into different matrices and want to test my code using random numbers for now.]
Quick question guys and thanks for your help in advance -
If I use;
100*rand(9,1)
What is the best way to make these 9 numbers add to 100?
I'd like 9 random numbers between 0 and 100 that add up to 100.
Is there an inbuilt command that does this because I can't seem to find it.
I see the mistake so often, the suggestion that to generate random numbers with a given sum, one just uses a uniform random set, and just scale them. But is the result truly uniformly random if you do it that way?
Try this simple test in two dimensions. Generate a huge random sample, then scale them to sum to 1. I'll use bsxfun to do the scaling.
xy = rand(10000000,2);
xy = bsxfun(#times,xy,1./sum(xy,2));
hist(xy(:,1),100)
If they were truly uniformly random, then the x coordinate would be uniform, as would the y coordinate. Any value would be equally likely to happen. In effect, for two points to sum to 1 they must lie along the line that connects the two points (0,1), (1,0) in the (x,y) plane. For the points to be uniform, any point along that line must be equally likely.
Clearly uniformity fails when I use the scaling solution. Any point on that line is NOT equally likely. We can see the same thing happening in 3-dimensions. See that in the 3-d figure here, the points in the center of the triangular region are more densely packed. This is a reflection of non-uniformity.
xyz = rand(10000,3);
xyz = bsxfun(#times,xyz,1./sum(xyz,2));
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
view(70,35)
box on
grid on
Again, the simple scaling solution fails. It simply does NOT produce truly uniform results over the domain of interest.
Can we do better? Well, yes. A simple solution in 2-d is to generate a single random number that designates the distance along the line connecting the points (0,1) and 1,0).
t = rand(10000000,1);
xy = t*[0 1] + (1-t)*[1 0];
hist(xy(:,1),100)
It can be shown that ANY point along the line defined by the equation x+y = 1, in the unit square, is now equally likely to have been chosen. This is reflected by the nice, flat histogram.
Does the sort trick suggested by David Schwartz work in n-dimensions? Clearly it does so in 2-d, and the figure below suggests that it does so in 3-dimensions. Without deep thought on the matter, I believe that it will work for this basic case in question, in n-dimensions.
n = 10000;
uv = [zeros(n,1),sort(rand(n,2),2),ones(n,1)];
xyz = diff(uv,[],2);
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
box on
grid on
view(70,35)
One can also download the function randfixedsum from the file exchange, Roger Stafford's contribution. This is a more general solution to generate truly uniform random sets in the unit hyper-cube, with any given fixed sum. Thus, to generate random sets of points that lie in the unit 3-cube, subject to the constraint they sum to 1.25...
xyz = randfixedsum(3,10000,1.25,0,1)';
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
view(70,35)
box on
grid on
One simple way is to pick 8 random numbers between 0 and 100. Add 0 and 100 to the list to give 10 numbers. Sort them. Then output the difference between each successive pair of numbers. For example, here's 8 random numbers between 0 and 100:
96, 38, 95, 5, 13, 57, 13, 20
So add 0 and 100 and sort.
0, 5, 13, 13, 20, 38, 57, 95, 96, 100
Now subtract:
5-0 = 5
13-5 = 8
13-13 = 0
20-13 = 7
38-20 = 18
57-38 = 19
95-57 = 38
96-95 = 1
100-96 = 4
And there you have it, nine numbers that sum to 100: 0, 1, 4, 5, 7, 8, 18, 19, 38. That I got a zero and a one was just a strange bit of luck.
It is not too late to give the right answer
Let's talk about sampling X1...XN in the range [0...1] such that Sum(X1, ..., XN) is equal to 1. Then you could rescale it to 100
This is called Dirichlet distribution, and below is the code to sample from it. Simplest case is when all parameters are equal to 1, then all marginal distributions for X1, ..., XN would be U(0,1). In general case, with parameters different from 1s, marginal distributions might have peaks.
----------------- taken from here ---------------------
The Dirichlet is a vector of unit-scale gamma random variables, normalized by their sum. So, with no error checking, this will get you that:
a = [1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0]; // 9 numbers to sample
n = 10000;
r = drchrnd(a,n)
function r = drchrnd(a,n)
p = length(a);
r = gamrnd(repmat(a,n,1),1,n,p);
r = r ./ repmat(sum(r,2),1,p);
Take a list of N - 1 numbers, create a list of N + 1 numbers by inserting 0 and 100, sort the list, and diff them down to a total of N numbers.

Creating a vector with random sampling of two vectors in matlab

How does one create a vector that is composed of a random sampling of two other vectors?
For example
Vector 1 [1, 3, 4, 7], Vector 2 [2, 5, 6, 8]
Random Vector [random draw from vector 1 or 2 (value 1 or 2), random draw from vector 1 or 2 (value 3 or 5)... etc]
Finally, how can one ask matlab to repeat this process n times to draw a distribution of results?
Thank you,
There are many ways you could do this. One possibility is:
tmp=round(rand(size(vector1)))
res = tmp.*vector1 + (1-tmp).*vector2
To get one mixed sample, you may use the idea of the following code snippet (not the optimal one, but maybe clear enough):
a = [1, 3, 4, 7];
b = [2, 5, 6, 8];
selector = randn(size(a));
sample = a.*(selector>0) + b.*(selector<=0);
For n samples put the above code in a for loop:
for k=1:n
% Sample code (without initial "samplee" assignments)
% Here do stuff with the sample
end;
More generally, if X is a matrix and for each row you want to take a sample from a column chosen at random, you can do this with a loop:
y = zeros(size(X,1),1);
for ii = 1:size(X,1)
y(ii) = X(ii,ceil(rand*size(X,2)));
end
You can avoid the loop using clever indexing via sub2ind:
idx_n = ceil(rand(size(X,1),1)*size(X,2));
idx = sub2ind(size(X),(1:size(X,1))',idx_n);
y = X(idx);
If I understand your question, you are choosing two random numbers. First you decide whether to select vector 1 or vector 2; next you pick an element from the chosen vector.
The following code takes advantage of the fact that vector1 and vector2 are the same length:
N = 1000;
sampleMatrix = [vector1 vector2];
M = numel(sampleMatrix);
randIndex = ceil(rand(1,N)*M); % N random numbers from 1 to M
randomNumbers = sampleMatrix(randIndex); % sample N times from the matrix
You can then display the result with, for instance
figure; hist(randomNumbers); % draw a histogram of numbers drawn
When vector1 and vector2 have different elements, you run into a problem. If you concatenate them, you will end up picking elements from the longer vector more often. One way around this is to create random samplings from both arrays, then choose between them:
M1 = numel(vector1);
M2 = numel(vector2);
r1 = ceil(rand(1,N)*M1);
r2 = ceil(rand(1,N)*M2);
randMat = [vector1(r1(:)) vector2(r2(:))]; % two columns, now pick one or the other
randPick = ceil(rand(1,N)*2);
randomNumbers = [randMat(randPick==1, 1); randMat(randPick==2, 2)];
On re-reading, maybe you just want to pick "element 1 from either 1 or 2", then "element 2 from either 1 or 2", etc for all the elements of the vector. In that case, do
N=numel(vector1);
randPick = ceil(rand(1,N)*2);
randMat=[vector1(:) vector2(:)];
randomNumbers = [randMat(randPick==1, 1); randMat(randPick==2, 2)];
This problem can be solved using the function datasample.
Combine both vectors into one and apply the function. I like this approach more than the handcrafted versions in the other answers. It gives you much more flexibility in choosing what you actually want, while being a one-liner.

Random numbers that add to 100: Matlab

[I'm splitting a population number into different matrices and want to test my code using random numbers for now.]
Quick question guys and thanks for your help in advance -
If I use;
100*rand(9,1)
What is the best way to make these 9 numbers add to 100?
I'd like 9 random numbers between 0 and 100 that add up to 100.
Is there an inbuilt command that does this because I can't seem to find it.
I see the mistake so often, the suggestion that to generate random numbers with a given sum, one just uses a uniform random set, and just scale them. But is the result truly uniformly random if you do it that way?
Try this simple test in two dimensions. Generate a huge random sample, then scale them to sum to 1. I'll use bsxfun to do the scaling.
xy = rand(10000000,2);
xy = bsxfun(#times,xy,1./sum(xy,2));
hist(xy(:,1),100)
If they were truly uniformly random, then the x coordinate would be uniform, as would the y coordinate. Any value would be equally likely to happen. In effect, for two points to sum to 1 they must lie along the line that connects the two points (0,1), (1,0) in the (x,y) plane. For the points to be uniform, any point along that line must be equally likely.
Clearly uniformity fails when I use the scaling solution. Any point on that line is NOT equally likely. We can see the same thing happening in 3-dimensions. See that in the 3-d figure here, the points in the center of the triangular region are more densely packed. This is a reflection of non-uniformity.
xyz = rand(10000,3);
xyz = bsxfun(#times,xyz,1./sum(xyz,2));
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
view(70,35)
box on
grid on
Again, the simple scaling solution fails. It simply does NOT produce truly uniform results over the domain of interest.
Can we do better? Well, yes. A simple solution in 2-d is to generate a single random number that designates the distance along the line connecting the points (0,1) and 1,0).
t = rand(10000000,1);
xy = t*[0 1] + (1-t)*[1 0];
hist(xy(:,1),100)
It can be shown that ANY point along the line defined by the equation x+y = 1, in the unit square, is now equally likely to have been chosen. This is reflected by the nice, flat histogram.
Does the sort trick suggested by David Schwartz work in n-dimensions? Clearly it does so in 2-d, and the figure below suggests that it does so in 3-dimensions. Without deep thought on the matter, I believe that it will work for this basic case in question, in n-dimensions.
n = 10000;
uv = [zeros(n,1),sort(rand(n,2),2),ones(n,1)];
xyz = diff(uv,[],2);
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
box on
grid on
view(70,35)
One can also download the function randfixedsum from the file exchange, Roger Stafford's contribution. This is a more general solution to generate truly uniform random sets in the unit hyper-cube, with any given fixed sum. Thus, to generate random sets of points that lie in the unit 3-cube, subject to the constraint they sum to 1.25...
xyz = randfixedsum(3,10000,1.25,0,1)';
plot3(xyz(:,1),xyz(:,2),xyz(:,3),'.')
view(70,35)
box on
grid on
One simple way is to pick 8 random numbers between 0 and 100. Add 0 and 100 to the list to give 10 numbers. Sort them. Then output the difference between each successive pair of numbers. For example, here's 8 random numbers between 0 and 100:
96, 38, 95, 5, 13, 57, 13, 20
So add 0 and 100 and sort.
0, 5, 13, 13, 20, 38, 57, 95, 96, 100
Now subtract:
5-0 = 5
13-5 = 8
13-13 = 0
20-13 = 7
38-20 = 18
57-38 = 19
95-57 = 38
96-95 = 1
100-96 = 4
And there you have it, nine numbers that sum to 100: 0, 1, 4, 5, 7, 8, 18, 19, 38. That I got a zero and a one was just a strange bit of luck.
It is not too late to give the right answer
Let's talk about sampling X1...XN in the range [0...1] such that Sum(X1, ..., XN) is equal to 1. Then you could rescale it to 100
This is called Dirichlet distribution, and below is the code to sample from it. Simplest case is when all parameters are equal to 1, then all marginal distributions for X1, ..., XN would be U(0,1). In general case, with parameters different from 1s, marginal distributions might have peaks.
----------------- taken from here ---------------------
The Dirichlet is a vector of unit-scale gamma random variables, normalized by their sum. So, with no error checking, this will get you that:
a = [1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0]; // 9 numbers to sample
n = 10000;
r = drchrnd(a,n)
function r = drchrnd(a,n)
p = length(a);
r = gamrnd(repmat(a,n,1),1,n,p);
r = r ./ repmat(sum(r,2),1,p);
Take a list of N - 1 numbers, create a list of N + 1 numbers by inserting 0 and 100, sort the list, and diff them down to a total of N numbers.