Like the title suggests, I am facing difficulty in understanding how we generate two correlated uniform [0,1] random variables. I am new to the idea of copulas.
I am struggling to write a MATLAB code wherein I am required to generate two correlated uniform [0,1] random variables.
Generating correlated uniform random variables with Gaussian Copula
rho = .75; % Desired target correlation
N = 1000; % Number of samples
Z = mvnrnd([0 0],[1 rho; rho 1], N);
U = normcdf(Z); % Correlated U(0,1) random variables
scatterhist(U(:,1),U(:,2),'Direction','out') % Visualize (change `rho` to see impact)
Note: Method not guaranteed to hit target correlation exactly but should be close enough for many applications.
This can be very useful to quickly generate correlated distributions using the inverse transform method (either analytically or numerically). Both use cases illustrated below.
Analytical approach
lambda = 2; alpha = 2; beta = 3;
rho = -.35; N = 1000;
Z = mvnrnd([0 0],[1 rho; rho 1], N);
U = normcdf(Z);
X = (-1/lambda)*log(U(:,1)); % Inverse Transform for Exponential
Y = beta*(-log(U(:,2))).^(1/alpha); % Inverse Transform for Weibull
corr(X,Y)
scatterhist(X,Y,'Direction','out')
Numerical approach
% Parameters
alpha = 6.7; lambda = 3;
mu = 0.1; sigma = 0.5;
rho = 0.75; N = 1000;
% Make distributions
pd_X = makedist('Gamma',alpha,lambda);
pd_Y = makedist('Lognormal',mu,sigma);
Z = mvnrnd([0 0],[1 rho; rho 1], N);
U = normcdf(Z);
% Use Inverse Transform for marginal distributions (numerically)
X = icdf(pd_X,U(:,1)); % Inverse CDF for X
Y = icdf(pd_Y,U(:,2)); % Inverse CDF for Y
corr(X,Y)
scatterhist(X,Y,'Direction','out')
References:
Inverse Transform
Copulas
Gaussian copula:
Ross, Sheldon. (2013). Simulation. Academic Press, San Diego, CA, 5th edition. 103--105.
Modified related answer from here.
Related
I'm having problems in curve fitting my randomized data for the function
Here is my code
N = 100;
mu = 5; stdev = 2;
x = mu+stdev*randn(N,1);
bin=mu-6*stdev:0.5:mu+6*stdev;
f=hist(x,bin);
plot(bin,f,'bo'); hold on;
x_ = x(1):0.1:x(end);
y_ = (1./sqrt(8.*pi)).*exp(-((x_-mu).^2)./8);
plot(x_,y_,'b-'); hold on;
It seems like I'm having vector size problems since it is giving me the error
Error using plot
Vectors must be the same length.
Note that I simplified y_ since mu and the standard deviation is known.
Plot:
Well first of all some adjustments to your question:
You are not trying to do curve fitting. What you are trying to do (in my opinion) is to overlay a probability density function on an histogram obtained by taking random points from the same distribution (A normal distribution with parameters (mu,sigma)). These two curve should indeed overlay, as they represent the same thing, only one is analytical and the other one is obtained numerically.
As seen in the hist documentation, hist is not recommended and you should use histogram instead
First step: Generating your random data
Knowing the distribution is the Normal distribution, we can use MATLAB's random function to do that :
N = 150;
rng('default') % For reproducibility
mu = 5;
sigma = 2;
r = random('Normal',mu,sigma,N,1);
Second step: Plot the histogram
Because we don't just want a count of the elements in each bin, but a feel of the probability density function, we can use the 'Normalization' 'pdf' arguments
Nbins = 25;
f=histogram(r,Nbins,'Normalization','pdf');
hold on
Here I'd rather specify a number of bins than specifying the bins themselves, because you never know in advance how far from the mean your data is going to be.
Last step: overlay the probability density function over the histogram
The histogram being already consistent with a probability density function, it is sufficient to just overlay the density function:
x_ = linspace(min(r),max(r),100);
y_ = (1./sqrt(2*sigma^2*pi)).*exp(-((x_-mu).^2)./(2*sigma^2));
plot(x_,y_,'b-');
With N = 150
With N = 1500
With N = 150.000 and Nbins = 50
If for some obscure reason you want to use old hist() function
The old hist() function can't handle normalization, so you'll have to do it by hand, by normalizing your density function to fit your histogram:
N = 1500;
% rng('default') % For reproducibility
mu = 5;
sigma = 2;
r = random('Normal',mu,sigma,1,N);
Nbins = 50;
[~,centers]=hist(r,Nbins);
hist(r,Nbins); hold on
% Width of bins
Widths = diff(centers);
x_ = linspace(min(r),max(r),100);
y_ = N*mean(Widths)*(1./sqrt(2*sigma^2*pi)).*exp(-((x_-mu).^2)./(2*sigma^2));
plot(x_,y_,'r-');
I want to generate random numbers from a mixture of Gumbel distributions in Matlab differing for location and scale. Could you advise on how to do that?
What I know (very little)
1) In Matlab there is a pre-built package to draw from a mixture of Gaussians. For example,
clear
rng default
m=-3;
mu_a = [m, m, m];
sigma_a = [1 0.1 0.5; 0.1 10 0.9; 0.5 0.9 20];
mu_b = -mu_a;
sigma_b= sigma_a;
MU = [mu_a;mu_b];
SIGMA = cat(3,sigma_a,sigma_b);
w = [1/2 1/2]; %equal weight 0.5
obj = gmdistribution(MU,SIGMA,w);
N = 10^4; %number draws
values = random(obj,N);
2) In Matlab there is a pre-built package to draw from Gumbel. See here
In general, I couldn't find any Matlab code to draw from a custom mixture in Matlab.
Say you have a mixture of 3 Gumbel distributions, each with their own mu and sigma, and relative weight (weights sum to 1), such that the total distribution is:
weight(1) * Gumbel(mu(1),sigma(1)) + weight(2) * Gumbel(mu(2),sigma(2)) + weight(3) * Gumbel(mu(3),sigma(3))
Then drawing a random value from this distribution is a two-step process:
Randomly select one of the 3 distributions to draw a number from.
Randomly select a value from the given distribution.
You can implement that this way:
mu = [1, 2, 3];
sigma = [0.9, 1.5, 2.1];
weight = [1, 2, 1.5]; weight = weight/sum(weight);
k = rand; % a random value in the range [0, 1];
k = find(k < cumsum(weight), 1, 'first');
random_value = evrnd(mu(k), sigma(k)); % Random value from the Gumbel distribution
The above generalizes to any number of distributions, and any type of distribution.
You can vectorize the above to draw N random values using:
N = 100;
k = rand(N,1); % a random value in the range [0, 1];
[~, k] = max(k < cumsum(weight), [], 2); % find doesn't vectorize nicely, this is an ugly workaround...
random_value = evrnd(mu(k), sigma(k)); % N random values from the Gumbel distributions
I am varying the signal strength for synthetic images. I need the signal to vary between 0 and 0.1, but I need to do this with a gamma distribution so that more of them fall around the .01/.02 range. The problem is that I am using the 2010 version of Matlab without the Statistics Toolbox that doesn't have the gamrnd function a part of its library.
Any and all help is greatly appreciated.
You can use the Inverse transform sampling method to convert a uniform distribution to any other distribution:
P = rand(1000);
X = gaminv(P(:),2,2); % with k = 2 and theta = 2
Here is a litle demonstration:
for k = [1 3 9]
for theta = [0.5 1 2]
X = gaminv(P(:),k,theta);
histogram(X,50)
hold on
end
end
Which gives:
Edit:
Without the statistics toolbox, you can use the Marsaglia's simple transformation-rejection method to generate random numbers from gamma distribution with rand and randn:
N = 10000; % no. of tries
% distribution parameters:
a = 0.5;
b = 0.1;
% Marsaglia's simple transformation-rejection:
d = a - 1/3;
x = randn(N,1);
U = rand(N,1);
v = (1+x./sqrt(9*d)).^3;
accept = log(U)<(0.5*x.^2+d-d*v+d*log(v));
Y = d*(v(accept)).*b;
Now Y is distributed like gamma(a,b). We can test the result using the gamrnd function:
n = size(Y,1);
X = gamrnd(a,b,n,1);
And the histograms of Y, and X are:
However, keep in mind that gamma distribution might not fit your needs because it has no specific upper bound (i.e. goes to infinity). So you may want to use another (bounded) distribution, like beta divided by 10.
How can I generate integer random number within [a,b] with below distribution in MATLAB:
p(x)= x^(-a)
I want the distribution to be normalized.
For continuous distributions: Generate random values given a PDF
For discrete distributions, as later it was specified in the OP:
The same rationale can be used as for continuous distributions: inverse transform sampling.
So from mathematical point of view there is no difference, the Matlab implementation however is different. Here is a simple solution with your distribution function:
% for reproducibility
rng(333)
% OPTIONS
% interval endpoints
a = 4;
b = 20;
% number of required random draws
n = 1e4;
% CALCULATION
x = a:b;
% normalization constant
nc = sum(x.^(-a));
% if a and b are finite it is more convinient to have the pdf and cdf as vectors
pmf = 1/nc*x.^(-a);
% create cdf
cdf = cumsum(pmf);
% generate uniformly distributed random numbers from [0,1]
r = rand(n,1);
% use the cdf to get the x value to rs
R = nan(n,1);
for ii = 1:n
rr = r(ii);
if rr == 1
R(ii) = b;
else
idx = sum(cdf < rr) + 1;
R(ii) = x(idx);
end
end
%PLOT
% verfication plot
f = hist(R,x);
bar(x,f/sum(f))
hold on
plot(x, pmf, 'xr', 'Linewidth', 1.2)
xlabel('x')
ylabel('Probability mass')
legend('histogram of random values', 'analytical pdf')
Notes:
the code is general, just replace the pmf with your function;
it is strange that the same parameter a appears in the distribution function and in the interval too.
I want generate a number in Gaussian and Uniform distributions in matlab.
I know this function randi and rand() but all of them are in normal (Gaussian) distribution. How can a generate a random number in uniform distribution?
Use rand(dimensions) for a Uniform Distribution between 0 and 1.
Use randn(dimensions) * sqrt(sigma) + mu for a Gaussian Distribution with a mean of mu and standard deviation of sigma.
randn is the function to generate Gaussian distributed variables (randi and rand produce uniformly distributed ones).
You can generate any distribution from rand().
For example , lets say you want to generate 100000 samples for rayleigh dist.The way to do this is that you invert the cdf of that particular function.The basic idea is that since the cdf has to be between 0 and 1 , we can find the value of the random variable by inputting the value of cdf b/w 0 and 1. So for rayleigh, it would be
for i = 1:100000
data(i) = (2*sigma^2 *(-(log(1 - rand(1,1)))))^.5;
end
You can do something similar for gaussian distribution.
Congrulations, you already generating pseudo-random numbers with a gaussian distribution. Normal distribution is a synonym for it.
The only other possible interpretation I can get from your question is that you want something that has mean != 0 and/or variance != 1. To do that, simply perform mean + sqrt(var) * randn(X).
It is true you can generate just about anything from rand but that it isn't always convenient, especially for some complicated distributions.
MATLAB has introduced Probability Distribution Objects which make this a lot easier and allow you to seamlessly access mean, var, truncate, pdf, cdf, icdf (inverse transform), median, and other functions.
You can fit a distribution to data. In this case, we use makedist to define the probability distribution object. Then we can generate using random.
% Parameters
mu = 10;
sigma = 3;
a = 5; b = 15;
N = 5000;
% Older Approaches Still Work
rng(1775)
Z = randn(N,1); % Standard Normal Z~N(0,1)
X = mu + Z*sigma; % X ~ Normal(mu,sigma)
U = rand(N,1); % U ~ Uniform(0,1)
V = a + (b-a)*U; % V ~ Uniform(a,b)
% New Approaches Are Convenient
rng(1775)
pdX = makedist('Normal',mu,sigma);
X2 = random(pdX,N,1);
pdV = makedist('Uniform',a,b);
V2 = random(pdV,N,1);
A reproducible example:
Support = (0:0.01:20)';
figure
s(1) = subplot(2,2,1)
h(1) = histogram(X,'Normalization','pdf')
xlabel('Normal')
s(2) = subplot(2,2,2)
h(2) = histogram(V,'Normalization','pdf')
xlabel('Uniform')
s(3) = subplot(2,2,3), hold on, box on
h(3) = histogram(X2,'Normalization','pdf')
plot(Support,pdf(pdX,Support),'r-','LineWidth',1.2)
xlabel('Normal (new)')
s(4) = subplot(2,2,4), hold on, box on
h(4) = histogram(V2,'Normalization','pdf')
plot(Support,pdf(pdV,Support),'r-','LineWidth',1.2)
xlabel('Uniform (new)')
xlim(s,[0 20])
References:
Uniform Distribution
Normal (Gaussian) Distribution
Following raj's answer: by using the Box-Muller Transform you can generate independent standard Normal/Gaussian random numbers:
N = 1e6; z = sqrt(-2*log(rand(N, 1))) .* cos(2*pi * rand(N, 1)); figure; hist(z, 100)
N = 1e6; z = sqrt(-2*log(rand(N, 1))) .* sin(2*pi * rand(N, 1)); figure; hist(z, 100)
If you want to apply the Inverse Transformation Method, you can use the Inverse Complementary Error Function (erfcinv):
N = 1e6; z = -sqrt(2) * erfcinv(2 * rand(1e6, 1)); figure; hist(z, 100)
But I hope randn works better.