Is there a way to generate a lognormal distribution from a pre-defined normal distribution?

Question:

I have the code which generates a normal distribution as a pdf, centered at the mean 400, with st

import numpy as np
import matplotlib.pyplot as plt
import scipy.stats

muPrev, sigmaPrev = 400, 40.
a = np.random.normal(muPrev, sigmaPrev, 100000)
count, bins, ignored = plt.hist(a, 1000, density=True)
plt.plot(bins, 1/(sigmaPrev * np.sqrt(2 * np.pi)) *
           np.exp( - (bins - muPrev)**2 / (2 * sigmaPrev**2) ),linewidth=3, color='r')

enter image description here

and I can visualise it. But what if I wanted to convert this into a lognormal distribution? So that I now get values of mu and sigma that correspond to this as a log distribution?

Asked By: Landon

||

Answers:

You could directly generate samples for a lognormal distribution with https://numpy.org/doc/stable/reference/random/generated/numpy.random.lognormal.html, alternatively:

log_norm = np.exp(a)

Note that if you want to generate the lognormal directly you need to calculate the appropriate mean and variance https://en.wikipedia.org/wiki/Log-normal_distribution

Answered By: Mattiatore

To give a more complete answer, here’s some code that draws a figure with two plots: one shows your existing Gaussian draws and another for log-normal draws. I keep the first and second moments the same (i.e. mean and variance) by setting the log-normal mu=log(mu) and sigma=sd/mu.

import numpy as np
import scipy.stats as sps
import matplotlib.pyplot as plt

mu, sd = 400, 40
n = 100_000

# draw samples from distributions
a = np.random.normal(mu, sd, n)
b = np.random.lognormal(np.log(mu), sd / mu, n)

# use Scipy for analytical PDFs
d1 = sps.norm(mu, sd)
# warning: scipy parameterises its distributions very strangely
d2 = sps.lognorm(sd / mu, scale=mu)

# bins to use for histogram and x for PDFs
lo, hi = np.min([a, b]), np.max([a, b])
dx = (hi - lo) * 0.06
bins = np.linspace(lo, hi, 101)
x = np.linspace(lo - dx, hi + dx, 501)

# draw figure
fig, [ax1, ax2] = plt.subplots(nrows=2, sharex=True, sharey=True, figsize=(8, 5))

ax1.set_title("Normal draws")
ax1.set_xlim(lo - dx, hi + dx)
ax1.hist(a, bins, density=True, alpha=0.5)
ax1.plot(x, d1.pdf(x))
ax1.plot(x, d2.pdf(x), '--')

ax2.set_title("Log-Normal draws")
ax2.hist(b, bins, density=True, alpha=0.5, label="Binned density")
ax2.plot(x, d1.pdf(x), '--', label="Normal PDF")
ax2.plot(x, d2.pdf(x), label="Log-Normal PDF")

ax2.legend()
fig.supylabel("Density")

which produces the following output:

matplotlib output

Because the distributions are so close here, I’ve included dashed lines to show the other distribution for easier comparison. Note that the log-normal distribution will always be slightly right-skewed, more so as the variance increases.

Answered By: Sam Mason

What is posted by @SamMason is not correct. It is somewhat working because your mean and sd are relative large.

Ok, here is what would be correct way to get parameters of the Log-Normal distribution.

You have predefined values of mean (corresponding to your Gaussian mean) and sd (again, your Gaussian sd).

Mean=exp(μ+σ2/2)

Var =(exp(σ2) – 1)(exp(2μ+σ2))

Here μ and σ are log-normal (NOT gaussian) parameter. You have to find them.

  1. Compute mean from your Gaussian mean (ok, that one is easy, they are equal)
  2. Compute variance from your Gaussian sd (square)
  3. Using formulas above solve two non-linear equations system and get your μ and σ
  4. Plug μ and σ into your sampling routine and draw samples

UPDATE

Mean2=exp(2μ+σ2)

Var/Mean2 = (exp(σ2) – 1)

So here is your σ. To be more elaborate

Sd2/Mean2 = exp(σ2) – 1

exp(σ2) = 1 + Sd2/Mean2

σ2 = ln(1 + Sd2/Mean2)

From first equation now you could get μ

2μ+σ2 = ln(Mean2)

2μ=ln(Mean2) – σ2 = ln(Mean2) – ln(1 + Sd2/Mean2) = ln((Mean2)/(1 + Sd2/Mean2))

Please, check the math, but this is the way to get PRECISE log-normal μ,σ parameters to match desired Mean and Sd.

@SamMason approximation works, I believe, only if in the expression for

σ2 = ln(1 + Sd2/Mean2)

one have second term much larger than 1. THen you could drop 1 and have log of ratios.

UPDATE II

2μ=ln((Mean2)/(1 + Sd2/Mean2)) = ln(Mean4/(Mean2 + Sd2))

μ=1/2 ln(Mean4/(Mean2 + Sd2))=ln(Mean2/Sqrt(Mean2 + Sd2))

Answered By: Severin Pappadeux