5
$\begingroup$

I have min and max values for certain variables such as:

  1. Expenses
  2. Loss
  3. Growth

I'd like to add a distribution around them and plot a histogram in python. Distribution could be beta, gamma right type as these variables tend to follow.

Based on the documentation for sampling from different distribution types, I need to define mean and sigma.

My function calculates the mean of two numbers which define the range, and sample from a different distribution types.

def draw_samples(min, max):
    
    avg = (min + max)/2
    print(avg)

    arr = []

    for i in range(200):

        # Simulate rent growth %
        r = np.random.disttype(avg, .2, size=1)

        arr.append(r[0])


    #print("List of values", arr)    
    print("Mean value", np.mean(arr))

    plt.hist(arr, density=True, bins=30)  # density=False would make counts
    plt.ylabel('Probability')
    plt.xlabel('Data')

# Call function
draw_samples(1,6)

The range is values on the x-axis is not within the defined range (1,6). How are values sampled being defined?

Basically, I'd like to randomly sample n times but follow a certain distribution type and then compute the average value.

Say, growth variable can take values anywhere from -2 to 4% but with a non uniform distribution like gamma right, small chance it is <0. So then, how do I add a truncated distribution and sample from it?

Another way of doing this could be to sample n times using r = np.random.disttype(mu, .2, size=n)?

$\endgroup$
7
  • 1
    $\begingroup$ The log-normal distribution doesn't have an upper limit, so either you don't want to draw from a log-normal distribution or you don't want to constrain the values to be in a certain range. Can you explain in more detail what problem you're trying to solve by drawing random samples in this way, and how a log-normal distribution fits with that goal? $\endgroup$
    – Sycorax
    Commented Jul 12, 2021 at 22:39
  • $\begingroup$ Log-normal was an example I used. I need to constrain my data within an interval (1,6) and sample from this range by following a certain distribution type. @Sycorax $\endgroup$
    – kms
    Commented Jul 12, 2021 at 22:52
  • 1
    $\begingroup$ Are you asking how to sample from a distribution which has probability density proportional to disttype for values in [1,6] and probability 0 otherwise? In other words, how to sample from a truncated gamma, truncated log-normal, etc. distributions? $\endgroup$
    – Sycorax
    Commented Jul 12, 2021 at 22:58
  • $\begingroup$ Ok. Say, growth variable can take values anywhere from -2 to 4% but with a non uniform distribution like gamma right, small chance it is <0. So then, how do I add a distribution around that constrain and sample from it? $\endgroup$
    – kms
    Commented Jul 12, 2021 at 23:01
  • 1
    $\begingroup$ Can you edit your post to clarify that you are asking how to sample from truncated distributions? Right now, it's hard to understand what you want to know & where you are stuck $\endgroup$
    – Sycorax
    Commented Jul 12, 2021 at 23:17

2 Answers 2

7
$\begingroup$

Perhaps the simplest and most generic method is inverse transform sampling. All of the distributions mentioned in OP are absolutely continuous, so it's very straightforward. Morevoer, for these distributions, the key components are all implemented in scipy already, so we only need to do algebra.

Suppose $X$ is distributed according to some CDF $G$. The truncated distribution $F$ is how $x$ is distributed given that it's restricted to the interval $[a,b]$. This is just rescaling and shifting the CDF $G$, so we have $$ F(y) = \frac{G(y) - G(a)}{G(b)-G(a)}. $$

Inverse transform sampling observes that for some continuous random variable, we can sample from a CDF $F$ using a uniform distribution. This is because $F(F^{-1}(u))=u$.

Putting it all together,

$$ F^{-1}(u)=G^{-1}\left[u(G(b) - G(a)) +G(a) \right] $$

scipy implements CDFs $G$ and quantile functions $G^{-1}$ for all of the distributions you list, so the rest is just writing some (simple!) code:

  • draw a value of $u$ from a Uniform(0,1) distribution
  • compute $F^{-1}(u)$

There are lots of other algorithms to solve this problem (rejection sampling, ziggurat algorithm and many more), at various trade-offs of speed, precision and complexity of implementation. I think this one sits at a nice compromise.

If this seems too complicated, then you could do something even more naive and just reject any values that are outside of your desired bounds. This is inefficient, especially if $F(b) - F(a)$ the probability in the interval $[a,b]$ is small.

$\endgroup$
1
$\begingroup$

I don’t have permission to comment, so I’ll try to frame this as an answer. If you really have truncated data —data that only takes values inside (and up to) the bounds, then @Sycorax’s answer applies.

However, if your data can take on wider values than your bounds in reality but are reported within the bounds — if high and low values are censored— then you’ll want to account for this by modeling spikes for the probability a number is less/greater than the bound. Looks like built-in functionality is coming https://github.com/scipy/scipy/pull/13699, so until then, you may have to roll your own optimizer (discussed here https://stackoverflow.com/questions/64229245/mle-for-censored-distributions-of-the-exponential-family and surely many other places).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.