Modeling Distributions

Modeling Distributions

In [1]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

Binomial Distribution

Let's find the distribution of skeet-shooting competition.

In [2]:
np.random.seed(10)
flip = lambda n, p: np.random.choice([1, 0], n, p=[p, 1 - p])
pd.Series(flip(1000, 0.9)).value_counts(normalize=True)
Out[2]:
1    0.905
0    0.095
Name: proportion, dtype: float64

Here we define a function that returns us a list of ones or zeros, we can specify the probability of getting 1 with p parameter. Also, we can specify how many tries we want using n parameter.

In [3]:
sim = lambda n, p: flip(n, p).sum()

Let's think of ones as "the target is hit", and the zeros as "target is missed".

We defined a sim function where we can specify the number of tries and the probability of hittin the target, and get the successful hits out of all hits.

In [4]:
n = 25
p = 0.9
results_sim = [sim(n, p) for i in range(1000)]
print(np.array(results_sim).mean(), n * p)
22.496 22.5

Out of 25 tries, 22.5 of the shootings were successful on average.

But instead of simulating, we can also calculate the mean by multiplying n with p. Why? Because skeet-shooting gives us a binomial distribution. But let's compare it to a standard (textbook, no simulations) binomial distribution to see how they look side by side.

In [5]:
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(
    results_sim,
    bins=range(17, 27),
    density=True,
    align="mid",
    color="skyblue",
    alpha=0.7,
    edgecolor="black",
)
ax.set_xlabel("Successful hits")
ax.set_ylabel("Probability")
ax.set_title("Simulation results")
plt.show()
No description has been provided for this image

We see a distribution skewed to the left. Let's see how the textbook binomial distribution looks like for p=0.9.

In [6]:
from scipy.stats import binom
In [7]:
print(n, p)
25 0.9
In [8]:
x_theoretical = np.arange(0, n + 1)
y_theoretical = binom.pmf(x_theoretical, n, p)
theoret_binom_freq = pd.DataFrame(y_theoretical, index=x_theoretical)
print(theoret_binom_freq)
               0
0   1.000000e-25
1   2.250000e-23
2   2.430000e-21
3   1.676700e-19
4   8.299665e-18
5   3.137273e-16
6   9.411820e-15
7   2.299173e-13
8   4.655826e-12
9   7.914904e-11
10  1.139746e-09
11  1.398779e-08
12  1.468718e-07
13  1.321846e-06
14  1.019710e-05
15  6.730087e-05
16  3.785674e-04
17  1.803762e-03
18  7.215049e-03
19  2.392358e-02
20  6.459368e-02
21  1.384150e-01
22  2.264973e-01
23  2.658881e-01
24  1.994161e-01
25  7.178980e-02
In [9]:
skeet_freq = pd.Series(results_sim).value_counts(normalize=True).sort_index()
print(skeet_freq)
16    0.001
17    0.001
18    0.011
19    0.017
20    0.054
21    0.164
22    0.223
23    0.256
24    0.201
25    0.072
Name: proportion, dtype: float64
In [10]:
fig, ax = plt.subplots(figsize=(8, 5))
width = 0.4
ax.bar(
    x=skeet_freq.index - width / 2,
    height=skeet_freq,
    width=width,
    alpha=0.6,
    label="simulation",
)
ax.bar(
    x=theoret_binom_freq.index + width / 2,
    height=theoret_binom_freq[0],
    width=width,
    alpha=0.6,
    label="binomial",
)
ax.set_xlabel("Successful hits")
ax.set_ylabel("Probability")
ax.set_xlim((15, 27))
ax.legend()
plt.show()
No description has been provided for this image

They look very similar. But this is no surprise since we assumed that every shooting have the same success rate.

To put our simulation in a much stronger test, we are going to use real world data.

In [11]:
rw_skeet = pd.read_html("./data/Shooting_at_the_2020_Summer_Olympics_Mens_skeet.html")[
    6
]
rw_skeet
Out[11]:
Rank Athlete Country 1 2 3 4 5 Total[3] Shoot-off Notes
0 1 Éric Delaunay France 25 25 25 24 25 124 +6 Q, OR
1 2 Tammaro Cassandro Italy 24 25 25 25 25 124 +5 Q, OR
2 3 Eetu Kallioinen Finland 25 25 24 25 24 123 NaN Q
3 4 Vincent Hancock United States 25 25 25 25 22 122 +8 Q
4 5 Abdullah  Al-Rashidi Kuwait 25 25 24 25 23 122 +7 Q
5 6 Jesper Hansen Denmark 25 24 23 25 25 122 +5+8+20 Q
6 7 Jakub Tomeček Czech Republic 24 25 25 25 23 122 +5+8+19 NaN
7 8 Nicolás Pacheco Peru 24 24 25 25 24 122 +5+7 NaN
8 9 Georgios  Achilleos Cyprus 25 24 24 25 24 122 +3 NaN
9 10 Gabriele Rossetti Italy 23 25 24 24 25 121 CB:37 NaN NaN
10 11 Emmanuel Petit France 23 25 24 24 25 121 CB:28 NaN NaN
11 12 Dimitris Konstantinou Cyprus 24 25 24 23 25 121 NaN NaN
12 13 Lee Jong-jun South Korea 24 25 24 24 24 121 NaN NaN
13 14 Erik Watndal Norway 25 24 25 23 24 121 NaN NaN
14 15 Phillip Jungman United States 24 24 23 24 25 120 CB:47 NaN NaN
15 16 Mansour Al-Rashedi Kuwait 24 24 23 24 25 120 CB:36 NaN NaN
16 17 Federico Gil Argentina 25 23 25 23 24 120 NaN NaN
17 18 Angad  Bajwa India 24 25 24 23 24 120 NaN NaN
18 19 Azmy Mehelba Egypt 23 22 22 24 23 120 NaN NaN
19 20 Nikolaos  Mavrommatis Greece 23 24 23 24 25 119 NaN NaN
20 21 Paul Adams Australia 25 25 23 22 24 119 NaN NaN
21 22 Saeed Al-Mutairi Saudi Arabia 24 24 23 25 23 119 NaN NaN
22 23 Stefan Nilsson Sweden 25 24 23 24 23 119 NaN NaN
23 24 Saif Bin Futtais United Arab Emirates 24 23 23 23 24 117 NaN NaN
24 25 Mairaj Ahmad Khan India 25 24 22 23 23 117 NaN NaN
25 26 Emin Jafarov Azerbaijan 25 23 23 22 23 116 NaN NaN
26 27 Hiroyuki Ikawa Japan 23 23 23 22 23 114 NaN NaN
27 28 Lari Pesonen Finland 23 25 23 24 19 114 NaN NaN
28 29 Mostafa Hamdy Egypt 23 22 22 25 20 112 NaN NaN
29 30 Juan Schaeffer Guatemala 21 22 22 23 19 107 NaN NaN

Looks like 1,2,3,4,5 columns are the rounds. Let's get all the results and put it in a list.

In [12]:
rw_skeet
rw_flat = rw_skeet[["1", "2", "3", "4", "5"]].values.flatten()
In [13]:
rw_success_rate = rw_flat.mean() / 25
rw_success_rate
Out[13]:
np.float64(0.9533333333333333)

Real-world success rate is much higher. Let's create a binomial distribution with p=0.953333

In [14]:
rw_pmf = pd.Series(rw_flat).value_counts(normalize=True).sort_index()
rw_pmf
Out[14]:
19    0.013333
20    0.006667
21    0.006667
22    0.073333
23    0.246667
24    0.313333
25    0.340000
Name: proportion, dtype: float64
In [15]:
n = 25
x_theoretical = np.arange(0, n + 1)
y_theoretical = binom.pmf(x_theoretical, n, rw_success_rate)
binom_pmf = pd.DataFrame(y_theoretical, index=x_theoretical)
print(binom_pmf)
               0
0   5.310917e-34
1   2.712361e-31
2   6.649160e-29
3   1.041385e-26
4   1.170071e-24
5   1.003921e-22
6   6.836221e-21
7   3.790615e-19
8   1.742329e-17
9   6.723177e-16
10  2.197518e-14
11  6.121659e-13
12  1.458995e-11
13  2.980519e-10
14  5.218950e-09
15  7.818483e-08
16  9.982528e-07
17  1.079623e-05
18  9.802291e-05
19  7.377514e-04
20  4.521362e-03
21  2.199166e-02
22  8.168331e-02
23  2.176530e-01
24  3.705284e-01
25  3.027746e-01
In [16]:
fig, ax = plt.subplots(figsize=(8, 5))
width = 0.4
ax.bar(
    x=rw_pmf.index - width / 2,
    height=rw_pmf,
    width=width,
    alpha=0.6,
    label="actual",
)
ax.bar(
    x=binom_pmf.index + width / 2,
    height=binom_pmf[0],
    width=width,
    alpha=0.6,
    label="binomial",
)
ax.set_xlabel("Successful hits")
ax.set_ylabel("Probability")
ax.set_xlim((15, 26))
ax.legend()
plt.show()
No description has been provided for this image

Binomial distribution is a good fit for the distribution of the real-world data.

Poisson Distribution

We will simulate an 60-minute ice hockey game to understand this distribution, assuming that the teams score a total of 6 goals per game.

We can use binomial distribution as usual, let's do it.

In [17]:
n = 3600
m = 6
p = 6 / 3600
p
Out[17]:
0.0016666666666666668
In [18]:
goals_b = np.array([sim(n, p) for i in range(1001)])
goals_b_table = pd.Series(goals_b, np.arange(1001))
goals_b_pmf = goals_b_table.value_counts(normalize=True).sort_index()
goals_b_pmf
Out[18]:
1     0.010989
2     0.034965
3     0.079920
4     0.131868
5     0.182817
6     0.169830
7     0.129870
8     0.110889
9     0.063936
10    0.036963
11    0.024975
12    0.016983
13    0.003996
15    0.001998
Name: proportion, dtype: float64

We used binomial distribution perfectly, but using binomial distribution in such rare events is not ideal, I'm going to compare the success rate of getting heads and scoring in an ice-hockey game to demonstrate this.

In [19]:
n = 1001
width = 0.5
test_coin = flip(n, 0.5)
test_goals = flip(n, p)
test_coin_sum = test_coin.sum()
test_goals_sum = test_goals.sum()

fig, ax = plt.subplots()
ax.bar(
    x=["Goals", "Coin Flips (getting heads)"], height=[test_goals_sum, test_coin_sum]
)
ax.set_ylabel("Success count")
ax.set_xlabel("Over 1000 tries")
plt.show()
No description has been provided for this image

As you can see, even though we get a decent result, using binomial distribution is not ideal in this sceneario since we have another distribution for this purpose and simulating is expensive when the n gets larger.

Poisson distribution PMF formula looks like this:

$$ \begin{gathered} P(X=x) = \frac{\lambda^x e^{-\lambda}}{x!} \\ x = \text{number of success} \\ \lambda = \text{rate of success} \end{gathered} $$

For a binomial distribution, if $n\ge50$ and $p\le0.1$, it approximates to Poisson distribution $Po(np)$.

In the context of ice-hockey game, $\lambda = 6$, and we will have an array ranging from 0 to 20 for the $x$

In [20]:
from scipy.stats import poisson

goals_range = np.arange(0, 20)
goals_p_tries = poisson.pmf(goals_range, m)
goals_p_pmf = pd.Series(goals_p_tries, goals_range)
goals_p_pmf
Out[20]:
0     0.002479
1     0.014873
2     0.044618
3     0.089235
4     0.133853
5     0.160623
6     0.160623
7     0.137677
8     0.103258
9     0.068838
10    0.041303
11    0.022529
12    0.011264
13    0.005199
14    0.002228
15    0.000891
16    0.000334
17    0.000118
18    0.000039
19    0.000012
dtype: float64

We have our Poission PMF and simulation PMF. Let's plot them to see if Poission distribution is appropriate for this situation.

In [21]:
fig, ax = plt.subplots()
width = 0.4

ax.bar(
    x=goals_b_pmf.index - width / 2,
    height=goals_b_pmf,
    width=width,
    alpha=0.8,
    label="simulation",
)
ax.bar(
    x=goals_p_pmf.index + width / 2,
    height=goals_p_pmf,
    width=width,
    alpha=0.8,
    label="poisson",
)
ax.set_xlabel("Number of goals")
ax.set_ylabel("Probability")
ax.legend()
plt.show()
No description has been provided for this image

Yes, Poission distribution is very appropriate for this event. To put our model to a stronger test, we can use real world data.

In [22]:
filename = "./data/nhl_2023_2024.hdf"

with pd.HDFStore(filename, "r") as store:
    keys = store.keys()

len(keys), keys[0]
Out[22]:
(1312, '/202310100PIT')

We can obtain the goals and when they were scored as follows.

In [23]:
times = pd.read_hdf(filename, key=keys[0])
times
Out[23]:
0     424
1    1916
2    2137
3    3005
4    3329
5    3513
dtype: int64

This is for the first game of the season.

To get the total scores of the game, we can use len()

In [24]:
len(times)
Out[24]:
6

Now let's create a list that has all the scores throughout all the games in the season.

In [25]:
goals_real = []

for key in keys:
    goals_real.append(len(pd.read_hdf(filename, key=key)))

Now, let's compare the real scores' PMF with the Poisson distribution PMF.

In [26]:
goals_real_df = pd.Series(goals_real)
goals_real_pmf = goals_real_df.value_counts(normalize=True).sort_index()
goals_real_pmf
Out[26]:
0     0.002287
1     0.009909
2     0.045732
3     0.070884
4     0.146341
5     0.151677
6     0.185976
7     0.137957
8     0.100610
9     0.072409
10    0.035061
11    0.028963
12    0.008384
13    0.003049
17    0.000762
Name: proportion, dtype: float64
In [27]:
fig, ax = plt.subplots()
width = 0.4

ax.bar(
    x=goals_real_pmf.index - width / 2,
    height=goals_real_pmf,
    width=width,
    alpha=0.8,
    label="real",
)
ax.bar(
    x=goals_p_pmf.index + width / 2,
    height=goals_p_pmf,
    width=width,
    alpha=0.8,
    label="poisson",
)
ax.set_xlabel("Number of goals")
ax.set_ylabel("Probability")
ax.legend()
plt.show()
No description has been provided for this image

The Poisson distribution also fits real-world data, meaning we can use the Poisson distribution to model the distribution of hockey scores.

The Exponential Distribution

We found the average number of goals in a hockey game. But the first goal timing follows the exponential distribution. Let's see how.

In [28]:
n = 3600
m = 6
p = m / 3600
p
Out[28]:
0.0016666666666666668
In [29]:
np.random.seed(100)
simulate_first_goal = lambda n, p: pd.Series(flip(n, p)).argmax()
first_goal_times = [simulate_first_goal(n, p) for i in range(1001)]
first_goal_sim_cdf = (
    pd.Series(first_goal_times).value_counts(normalize=True).sort_index().cumsum()
)
first_goal_sim_cdf
Out[29]:
0       0.004995
1       0.006993
2       0.008991
5       0.009990
6       0.013986
          ...   
3098    0.996004
3201    0.997003
3341    0.998002
3563    0.999001
3567    1.000000
Name: proportion, Length: 689, dtype: float64
In [30]:
mean = np.mean(first_goal_times)
mean
Out[30]:
np.float64(567.6223776223776)

You can look up formulas of the exponential distribution from its Wikipedia page. In this section we will use the below formulas:

$$ \begin{gathered} \text{Mean} = \frac{1}{\lambda} \\ \text{CDF} = 1 - e^{-\lambda x} \end{gathered} $$

$\lambda$ represents the rate at which events occur. So we can flip the formula to find the lambda.

In [31]:
lam = 1 / mean
lam
Out[31]:
np.float64(0.0017617346310213134)

Here, lambda represents how many goals are scored each second. Which is 0.0016 goal/second.

In [32]:
first_goal_lam = 1 / mean
cdf_aggregator = lambda lam, x: 1 - np.exp(-lam * x)
cdf_size = 3600
cdf_ticks = np.linspace(0, 3600, cdf_size)
first_goal_expo_model = pd.Series(
    [cdf_aggregator(tick, lam) for tick in cdf_ticks], index=cdf_ticks
)
first_goal_expo_model
Out[32]:
0.000000       0.000000
1.000278       0.001761
2.000556       0.003518
3.000834       0.005273
4.001111       0.007024
                 ...   
3595.998889    0.998227
3596.999166    0.998230
3597.999444    0.998233
3598.999722    0.998237
3600.000000    0.998240
Length: 3600, dtype: float64

Or we can just use scipy's expon function.

In [33]:
from scipy.stats import expon

first_goal_expo_model = pd.Series(
    data=expon.cdf(cdf_ticks, scale=mean), index=cdf_ticks
)
first_goal_expo_model
Out[33]:
0.000000       0.000000
1.000278       0.001761
2.000556       0.003518
3.000834       0.005273
4.001111       0.007024
                 ...   
3595.998889    0.998227
3596.999166    0.998230
3597.999444    0.998233
3598.999722    0.998237
3600.000000    0.998240
Length: 3600, dtype: float64
In [34]:
fig, ax = plt.subplots()
ax.step(x=first_goal_sim_cdf.index, y=first_goal_sim_cdf, alpha=0.4, label="simulation")
ax.step(
    x=first_goal_expo_model.index, y=first_goal_expo_model, alpha=0.4, label="expon"
)
ax.legend()
plt.show()
No description has been provided for this image

It matches up perfectly with the exponential distribution model.

Normal Distribution

Most things we measure in the real world follow a normal distribution. To test this, let's consider a model of the way giant pumpkins grow.

Suppose that each day, pumpkin grows 1 pound if the weather is bad, 2 pounds if the weather is fair, and 3 pounds if the weather is good.

In [35]:
simulate_growth = lambda n: np.random.choice([1, 2, 3], n).sum()

We will use this function to calculate the total weight gains over n days.

In [36]:
sim_weights = [simulate_growth(100) for i in range(2000)]

Here we simulated 1001 pumpkins over 100 days. Let's also create the CDF of this sim_wights.

In [37]:
sim_weights_cdf = (
    pd.Series(sim_weights).value_counts(normalize=True).sort_index().cumsum()
)
sim_cdf_size = sim_weights_cdf.count()

For real-world plotting, plotting from negative to positive inifinity is impractical. We are only going to use the curev between $\mu - 4\sigma$ to $\mu + 4\sigma$ for this example.

In [38]:
mean, std = np.mean(sim_weights), np.std(sim_weights)
low, high = mean - 4 * std, mean + 4 * std
print(mean, std)
print(low, high)
200.303 8.235240797936635
167.36203680825346 233.24396319174653
In [39]:
x_ticks = np.linspace(low, high, sim_cdf_size)
x_ticks
Out[39]:
array([167.36203681, 168.65383929, 169.94564176, 171.23744424,
       172.52924672, 173.8210492 , 175.11285168, 176.40465416,
       177.69645663, 178.98825911, 180.28006159, 181.57186407,
       182.86366655, 184.15546902, 185.4472715 , 186.73907398,
       188.03087646, 189.32267894, 190.61448141, 191.90628389,
       193.19808637, 194.48988885, 195.78169133, 197.0734938 ,
       198.36529628, 199.65709876, 200.94890124, 202.24070372,
       203.5325062 , 204.82430867, 206.11611115, 207.40791363,
       208.69971611, 209.99151859, 211.28332106, 212.57512354,
       213.86692602, 215.1587285 , 216.45053098, 217.74233345,
       219.03413593, 220.32593841, 221.61774089, 222.90954337,
       224.20134584, 225.49314832, 226.7849508 , 228.07675328,
       229.36855576, 230.66035824, 231.95216071, 233.24396319])
In [40]:
from scipy.stats import norm
In [41]:
norm_dist = norm.cdf(x_ticks, loc=mean, scale=std)
norm_dist_cdf = pd.Series(norm_dist, index=x_ticks)
norm_dist_cdf
Out[41]:
167.362037    0.000032
168.653839    0.000061
169.945642    0.000114
171.237444    0.000208
172.529247    0.000372
173.821049    0.000651
175.112852    0.001111
176.404654    0.001854
177.696457    0.003025
178.988259    0.004823
180.280062    0.007521
181.571864    0.011468
182.863667    0.017102
184.155469    0.024952
185.447272    0.035622
186.739074    0.049773
188.030876    0.068086
189.322679    0.091211
190.614481    0.119703
191.906284    0.153957
193.198086    0.194139
194.489889    0.240131
195.781691    0.291496
197.073494    0.347471
198.365296    0.406990
199.657099    0.468742
200.948901    0.531258
202.240704    0.593010
203.532506    0.652529
204.824309    0.708504
206.116111    0.759869
207.407914    0.805861
208.699716    0.846043
209.991519    0.880297
211.283321    0.908789
212.575124    0.931914
213.866926    0.950227
215.158728    0.964378
216.450531    0.975048
217.742333    0.982898
219.034136    0.988532
220.325938    0.992479
221.617741    0.995177
222.909543    0.996975
224.201346    0.998146
225.493148    0.998889
226.784951    0.999349
228.076753    0.999628
229.368556    0.999792
230.660358    0.999886
231.952161    0.999939
233.243963    0.999968
dtype: float64

Let's plot them and see if the normal distribution model fits to the simulation.

In [42]:
fig, ax = plt.subplots(figsize=(8, 6))
ax.step(sim_weights_cdf.index, sim_weights_cdf, label="simulation", alpha=0.4)
ax.step(norm_dist_cdf.index, norm_dist_cdf, label="normal", alpha=0.4)
ax.legend()
plt.show()
No description has been provided for this image

Because our data is discrete, it is much more appropriate to use a .step(), but when te sample size is small, it makes it hard to read.

In this situation, we can use the .plot() function the create a line plot.

In [43]:
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(sim_weights_cdf.index, sim_weights_cdf, label="simulation", alpha=0.4)
ax.plot(norm_dist_cdf.index, norm_dist_cdf, label="normal", alpha=0.4)
ax.legend()
plt.show()
No description has been provided for this image

The normal distribution fits our simulation very well.

In general, when we add up enough random variables, the sum tends to follow a normal distribution, as the consequence of CLT (Central Limit Theorem).

NSFG Data

Let's see how we see normal distribution in the real-world data.

In [44]:
preg = pd.read_csv("./data/2002FemPreg_after_01.csv")
preg
Out[44]:
caseid pregordr howpreg_n howpreg_p moscurrp nowprgdk pregend1 pregend2 nbrnaliv multbrth ... religion_i metro_i basewgt adj_mod_basewgt finalwgt secu_p sest cmintvw totalwgt_lb totalwgt_kg
0 1 1 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 3410.389399 3869.349602 6448.271112 2 9 1231 8.8125 3.997283
1 1 2 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 3410.389399 3869.349602 6448.271112 2 9 1231 7.8750 3.572040
2 2 1 NaN NaN NaN NaN 5.0 NaN 3.0 5.0 ... 0 0 7226.301740 8567.549110 12999.542264 2 12 1231 9.1250 4.139030
3 2 2 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 7226.301740 8567.549110 12999.542264 2 12 1231 7.0000 3.175147
4 2 3 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 7226.301740 8567.549110 12999.542264 2 12 1231 6.1875 2.806603
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
13588 12571 1 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 4670.540953 5795.692880 6269.200989 1 78 1227 6.1875 2.806603
13589 12571 2 NaN NaN NaN NaN 3.0 NaN NaN NaN ... 0 0 4670.540953 5795.692880 6269.200989 1 78 1227 NaN NaN
13590 12571 3 NaN NaN NaN NaN 3.0 NaN NaN NaN ... 0 0 4670.540953 5795.692880 6269.200989 1 78 1227 NaN NaN
13591 12571 4 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 4670.540953 5795.692880 6269.200989 1 78 1227 7.5000 3.401943
13592 12571 5 NaN NaN NaN NaN 6.0 NaN 1.0 NaN ... 0 0 4670.540953 5795.692880 6269.200989 1 78 1227 7.5000 3.401943

13593 rows × 245 columns

We will use the birth weights column.

In [45]:
total_weights = preg["totalwgt_kg"].dropna()
print(total_weights)
0        3.997283
1        3.572040
2        4.139030
3        3.175147
4        2.806603
           ...   
13581    2.891651
13584    2.891651
13588    2.806603
13591    3.401943
13592    3.401943
Name: totalwgt_kg, Length: 9083, dtype: float64

To eliminate the extreme values (outliers), we will use trimboth.

In [46]:
from scipy.stats import trimboth

trimmed_weights = trimboth(total_weights, 0.01)

Let's also create the CDF for the total weights.

In [47]:
trimmed_weights_s = pd.Series(trimmed_weights)
trimmed_weights_cdf = (
    trimmed_weights_s.value_counts(normalize=True).sort_index().cumsum()
)

Let's create a normal distribution with the same parameters as our weights dataset.

In [48]:
mean, std = np.mean(trimmed_weights), np.std(trimmed_weights)
low, high = mean - std * 4, mean + std * 4

x_ticks = np.linspace(low, high, 500)
norm_dist_cdf_arr = norm.cdf(x_ticks, mean, std)
norm_dist_cdf = pd.Series(norm_dist_cdf_arr, index=x_ticks)
print(norm_dist_cdf)
1.021660    0.000032
1.030831    0.000034
1.040002    0.000036
1.049174    0.000039
1.058345    0.000041
              ...   
5.561344    0.999959
5.570515    0.999961
5.579686    0.999964
5.588857    0.999966
5.598028    0.999968
Length: 500, dtype: float64
In [49]:
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(trimmed_weights_cdf.index, trimmed_weights_cdf, label="rw", alpha=0.4)
ax.plot(norm_dist_cdf.index, norm_dist_cdf, label="normal", alpha=0.4)
ax.legend()
plt.show()
No description has been provided for this image

Normal model fits the data well except below 3 kilograms. It can be interpreted as lightest babies are lighter than we'd expect.

Lognormal Distribution

We simulated pumpkin growth under the assumption that pumpkins grow 1-3 pounds per day, depending on the weather. Instead, let's suppose the growth is proportional to their current weight. So big pumpkins gain more weight per day, which is probably more realistic.

In [50]:
sim_propo_grow = lambda n: np.random.choice([1.03, 1.05, 1.07], n).prod()
print(sim_propo_grow(20))
2.6979561421175045

Here we have a simulation of a pumpkin growing for 20 days. Let's simulate 1001 pumpkins over 100 days.

In [51]:
sim_weights = [sim_propo_grow(100) for _ in range(1001)]
print(sim_weights)
[np.float64(134.835115962981), np.float64(122.74059257418709), np.float64(160.255921772537), np.float64(113.65274204626114), np.float64(120.44637589055763), np.float64(105.23776611903418), np.float64(160.25592177253705), np.float64(125.21474734262056), np.float64(134.93300877468624), np.float64(124.98776517204875), np.float64(129.98308583004922), np.float64(111.64987131220678), np.float64(101.30364402112639), np.float64(135.0799812683846), np.float64(151.54647609229016), np.float64(145.66959279516118), np.float64(122.82970453609443), np.float64(137.55306719749578), np.float64(115.85959140638266), np.float64(169.40441907371869), np.float64(103.38319017056055), np.float64(145.72246262287024), np.float64(148.55202500389666), np.float64(111.32621940391583), np.float64(130.03026234246374), np.float64(127.55349544070243), np.float64(160.19777903447894), np.float64(140.2748939266882), np.float64(122.60704583543831), np.float64(132.41089646114074), np.float64(142.99867827477917), np.float64(115.85959140638259), np.float64(145.828259851529), np.float64(142.63590207785953), np.float64(111.56887022930998), np.float64(129.79455088025273), np.float64(120.2716735338108), np.float64(120.40267652515499), np.float64(118.23793988053954), np.float64(118.19504176176187), np.float64(105.42888159779292), np.float64(137.70289352602322), np.float64(197.86624545478378), np.float64(137.60299118522744), np.float64(209.23770958367135), np.float64(151.27176126184128), np.float64(151.3266643600218), np.float64(132.31483342161684), np.float64(88.60857222688587), np.float64(132.55512180542428), np.float64(105.39063075639665), np.float64(99.44619821797839), np.float64(111.69039390409945), np.float64(125.03312866544213), np.float64(142.9986782747792), np.float64(151.32666436002185), np.float64(127.5997901491467), np.float64(103.38319017056065), np.float64(129.84165896513807), np.float64(122.65154526229085), np.float64(132.458954131574), np.float64(97.58739077465172), np.float64(122.78514047095662), np.float64(122.65154526229091), np.float64(166.29831632407257), np.float64(163.0123793154722), np.float64(127.27607969841728), np.float64(116.02788492950138), np.float64(101.37719235813336), np.float64(105.35239379285709), np.float64(127.50721752852445), np.float64(101.34041151736837), np.float64(157.1463927671556), np.float64(140.27489392668804), np.float64(118.10929221039017), np.float64(125.12390505135583), np.float64(160.13965739129173), np.float64(101.3771923581334), np.float64(142.9467966681488), np.float64(93.83705429060831), np.float64(157.37465802998915), np.float64(151.32666436002177), np.float64(132.60323182150455), np.float64(175.98323146493112), np.float64(137.40340388608539), np.float64(113.73525605900527), np.float64(154.32103568365974), np.float64(140.07143114600936), np.float64(101.41398654826432), np.float64(157.37465802998918), np.float64(118.02360486962758), np.float64(127.55349544070258), np.float64(120.31532535253295), np.float64(182.95026065742138), np.float64(122.78514047095656), np.float64(163.18993657969742), np.float64(179.53062961709605), np.float64(142.89493388477706), np.float64(120.31532535253294), np.float64(132.4589541315739), np.float64(157.08937820288648), np.float64(118.19504176176196), np.float64(179.40038159046384), np.float64(87.11024572978687), np.float64(105.27596147920805), np.float64(122.60704583543836), np.float64(120.53382220831683), np.float64(154.15312814301925), np.float64(134.7861961930171), np.float64(76.22150251452015), np.float64(118.2808535689092), np.float64(129.88878414759515), np.float64(127.55349544070256), np.float64(120.31532535253285), np.float64(122.65154526229088), np.float64(127.55349544070245), np.float64(111.44747877720327), np.float64(140.2240005411365), np.float64(120.27167353381095), np.float64(163.24916530180238), np.float64(122.60704583543836), np.float64(125.07850862321928), np.float64(137.40340388608533), np.float64(148.60594099155813), np.float64(132.4108964611407), np.float64(148.44425170554504), np.float64(127.36848450865914), np.float64(154.04129128942697), np.float64(107.55407396215352), np.float64(116.19642290980804), np.float64(99.55451756751087), np.float64(140.1731256203052), np.float64(99.51839801465182), np.float64(140.07143114600933), np.float64(101.48761499600616), np.float64(116.02788492950128), np.float64(111.52839172763937), np.float64(179.59578908705933), np.float64(97.55198491858835), np.float64(145.88118726641022), np.float64(140.17312562030523), np.float64(134.8840534880561), np.float64(186.63809734044966), np.float64(137.5031613227756), np.float64(182.9502606574214), np.float64(115.69154198587725), np.float64(120.62133201373591), np.float64(109.60272299000407), np.float64(145.6167421492491), np.float64(122.74059257418709), np.float64(172.81920421084934), np.float64(135.07998126838459), np.float64(122.74059257418712), np.float64(179.85666354480463), np.float64(160.25592177253705), np.float64(95.83285335937956), np.float64(122.65154526229078), np.float64(169.58893871158116), np.float64(107.3591060555782), np.float64(109.48347078577143), np.float64(142.99867827477908), np.float64(120.44637589055738), np.float64(148.49812857759147), np.float64(117.98078451411934), np.float64(154.26504619225523), np.float64(132.41089646114077), np.float64(134.8840534880561), np.float64(139.96981045023608), np.float64(129.79455088025279), np.float64(175.9193826734699), np.float64(124.94241813706566), np.float64(107.5150520759088), np.float64(151.43653034377834), np.float64(134.93300877468624), np.float64(113.69399156701095), np.float64(120.18441740280453), np.float64(130.1246667407287), np.float64(142.84308991783485), np.float64(151.6014788964249), np.float64(166.3586732123128), np.float64(111.60936342238836), np.float64(132.41089646114088), np.float64(115.9016418887006), np.float64(118.23793988053946), np.float64(132.362856226597), np.float64(118.10929221039014), np.float64(120.31532535253285), np.float64(137.60299118522738), np.float64(113.44671882113339), np.float64(107.35910605557805), np.float64(113.6527420462611), np.float64(137.50316132277553), np.float64(148.28273834402796), np.float64(148.3903943806634), np.float64(157.20342802448863), np.float64(125.16931795582953), np.float64(109.5629578297357), np.float64(157.1463927671555), np.float64(172.75650336578414), np.float64(117.9807845141192), np.float64(125.16931795582968), np.float64(157.26048398239607), np.float64(127.50721752852445), np.float64(124.8970875545216), np.float64(125.0785086232192), np.float64(134.78619619301716), np.float64(115.85959140638279), np.float64(107.35910605557815), np.float64(169.65048990973415), np.float64(137.50316132277555), np.float64(132.26682803987651), np.float64(120.35899301439764), np.float64(137.65293329254442), np.float64(122.56256255350256), np.float64(120.44637589055736), np.float64(163.36768724384848), np.float64(166.23798133402295), np.float64(134.9330087746862), np.float64(107.47604434726445), np.float64(134.98198182931827), np.float64(115.69154198587728), np.float64(129.98308583004928), np.float64(85.42000032252425), np.float64(127.27607969841732), np.float64(120.40267652515499), np.float64(111.40704431778293), np.float64(99.62679600605438), np.float64(115.8595914063827), np.float64(129.7945508802527), np.float64(115.90164188870068), np.float64(118.36672767703051), np.float64(148.55202500389683), np.float64(160.02347735891928), np.float64(137.65293329254436), np.float64(127.59979014914664), np.float64(137.45327355449524), np.float64(143.20639307055947), np.float64(143.20639307055933), np.float64(132.31483342161678), np.float64(163.36768724384845), np.float64(127.41471206859335), np.float64(142.99867827477917), np.float64(132.60323182150466), np.float64(132.45895413157393), np.float64(122.65154526229078), np.float64(113.90046384812852), np.float64(115.94370763296654), np.float64(92.14994470211658), np.float64(125.1239050513558), np.float64(113.73525605900527), np.float64(142.94679666814866), np.float64(97.55198491858837), np.float64(137.5530671974958), np.float64(157.14639276715565), np.float64(130.0302623424636), np.float64(125.03312866544215), np.float64(140.3258057836618), np.float64(101.56129689937578), np.float64(113.5290832571693), np.float64(127.64610165995299), np.float64(135.03097265840063), np.float64(172.8192042108492), np.float64(142.84308991783487), np.float64(151.27176126184122), np.float64(120.533822208317), np.float64(129.841658965138), np.float64(130.07745597728555), np.float64(154.48912611349976), np.float64(113.69399156701105), np.float64(130.03026234246366), np.float64(163.36768724384848), np.float64(125.12390505135578), np.float64(97.65824104241541), np.float64(125.07850862321925), np.float64(148.4442517055452), np.float64(182.9502606574214), np.float64(135.12900766572372), np.float64(151.43653034377823), np.float64(169.7120634474931), np.float64(142.89493388477715), np.float64(122.82970453609444), np.float64(142.89493388477706), np.float64(111.5688702293099), np.float64(115.85959140638259), np.float64(154.26504619225528), np.float64(157.20342802448866), np.float64(148.55202500389657), np.float64(148.22893961809805), np.float64(140.2240005411364), np.float64(105.19958461658732), np.float64(120.18441740280451), np.float64(111.48792791204659), np.float64(109.20571998669844), np.float64(101.45079409260617), np.float64(156.97541112351126), np.float64(139.9698104502361), np.float64(176.36681115172124), np.float64(134.884053488056), np.float64(105.39063075639677), np.float64(129.8416589651381), np.float64(137.5031613227755), np.float64(142.791264760495), np.float64(143.1024979851454), np.float64(109.4040414090177), np.float64(105.31417070213853), np.float64(129.88878414759523), np.float64(111.60936342238828), np.float64(92.21684728250862), np.float64(148.6059409915581), np.float64(137.65293329254436), np.float64(120.31532535253294), np.float64(140.3258057836616), np.float64(105.46714632208189), np.float64(156.91845859339853), np.float64(97.55198491858836), np.float64(120.14081307903031), np.float64(137.60299118522735), np.float64(130.03026234246366), np.float64(201.4157391319453), np.float64(151.27176126184114), np.float64(129.93592643382965), np.float64(145.45830523271306), np.float64(169.58893871158116), np.float64(113.57028789676905), np.float64(140.32580578366165), np.float64(163.1307293464711), np.float64(135.12900766572366), np.float64(88.70508695624315), np.float64(197.43590670815516), np.float64(145.828259851529), np.float64(127.46095640651866), np.float64(154.04129128942714), np.float64(105.39063075639682), np.float64(132.41089646114082), np.float64(111.60936342238833), np.float64(111.60936342238833), np.float64(105.23776611903413), np.float64(132.50702924422498), np.float64(127.55349544070249), np.float64(113.69399156701122), np.float64(175.98323146493127), np.float64(137.6029911852274), np.float64(122.78514047095663), np.float64(151.2168780831522), np.float64(111.5688702293099), np.float64(129.93592643382968), np.float64(120.40267652515494), np.float64(132.3628562265969), np.float64(148.28273834402788), np.float64(137.65293329254442), np.float64(157.14639276715553), np.float64(120.31532535253291), np.float64(111.48792791204662), np.float64(109.52320709673614), np.float64(157.03238432417317), np.float64(142.94679666814878), np.float64(160.197779034479), np.float64(157.3746580299891), np.float64(122.56256255350259), np.float64(113.5290832571693), np.float64(122.69606083991981), np.float64(145.45830523271312), np.float64(122.82970453609452), np.float64(140.27489392668812), np.float64(122.60704583543824), np.float64(169.71206344749294), np.float64(105.31417070213858), np.float64(122.78514047095668), np.float64(111.44747877720333), np.float64(160.19777903447908), np.float64(183.2825047551821), np.float64(154.37704549608455), np.float64(122.69606083991985), np.float64(190.1931087183629), np.float64(129.88878414759523), np.float64(115.81755618047552), np.float64(132.3148334216168), np.float64(127.59979014914666), np.float64(127.36848450865926), np.float64(166.11737701671944), np.float64(132.31483342161684), np.float64(129.74745988673598), np.float64(113.69399156701105), np.float64(145.56391067817464), np.float64(140.1731256203052), np.float64(179.59578908705947), np.float64(129.93592643382988), np.float64(97.5519849185883), np.float64(157.26048398239607), np.float64(111.56887022930988), np.float64(157.08937820288634), np.float64(134.78619619301705), np.float64(125.03312866544204), np.float64(135.03097265840066), np.float64(120.2280375524835), np.float64(109.36434833276952), np.float64(122.82970453609447), np.float64(111.48792791204653), np.float64(134.8840534880561), np.float64(122.82970453609441), np.float64(142.94679666814883), np.float64(130.03026234246377), np.float64(145.72246262287018), np.float64(95.69385187251993), np.float64(105.2759614792081), np.float64(134.83511596298098), np.float64(151.38158738492334), np.float64(107.51505207590877), np.float64(132.2188400750549), np.float64(148.60594099155816), np.float64(129.84165896513812), np.float64(99.4101179646567), np.float64(134.93300877468621), np.float64(94.00746567634376), np.float64(125.03312866544212), np.float64(122.69606083991997), np.float64(120.44637589055742), np.float64(118.2808535689091), np.float64(145.40553124442005), np.float64(127.46095640651873), np.float64(148.6059409915581), np.float64(122.69606083991981), np.float64(111.48792791204673), np.float64(118.15215920692789), np.float64(97.55198491858843), np.float64(117.98078451411924), np.float64(157.26048398239615), np.float64(99.51839801465178), np.float64(122.5625625535025), np.float64(137.5031613227756), np.float64(105.35239379285701), np.float64(145.61674214924915), np.float64(109.36434833276954), np.float64(134.9819818293183), np.float64(107.39807134223294), np.float64(99.51839801465185), np.float64(148.39039438066337), np.float64(134.83511596298112), np.float64(90.329127027408), np.float64(135.0799812683846), np.float64(135.12900766572383), np.float64(140.42768493869391), np.float64(154.09719957045027), np.float64(137.55306719749578), np.float64(132.21884007505494), np.float64(117.89519040465588), np.float64(99.48229156639252), np.float64(122.78514047095658), np.float64(82.2565729370102), np.float64(95.76332739568626), np.float64(127.59979014914666), np.float64(122.69606083991981), np.float64(163.30841552058538), np.float64(105.27596147920812), np.float64(135.1290076657236), np.float64(105.35239379285711), np.float64(140.42768493869397), np.float64(137.45327355449515), np.float64(160.1396573912917), np.float64(127.32227372062893), np.float64(103.30818649828818), np.float64(154.60128810353575), np.float64(127.46095640651853), np.float64(145.72246262287018), np.float64(154.20907701449832), np.float64(132.21884007505486), np.float64(122.69606083991978), np.float64(118.15215920692786), np.float64(113.69399156701108), np.float64(120.22803755248351), np.float64(109.56295782973565), np.float64(115.81755618047566), np.float64(132.3628562265971), np.float64(148.60594099155801), np.float64(132.45895413157393), np.float64(111.60936342238836), np.float64(107.24229499749205), np.float64(125.1239050513557), np.float64(122.8742847754688), np.float64(113.57028789676895), np.float64(129.74745988673607), np.float64(137.45327355449498), np.float64(166.47945271515985), np.float64(137.70289352602325), np.float64(107.28121788833583), np.float64(113.57028789676905), np.float64(88.76948850559249), np.float64(103.38319017056065), np.float64(132.3148334216167), np.float64(109.60272299000408), np.float64(122.78514047095662), np.float64(160.0815568353222), np.float64(115.81755618047559), np.float64(127.4147120685934), np.float64(113.85913941680045), np.float64(118.02360486962748), np.float64(105.46714632208194), np.float64(129.9830858300492), np.float64(118.15215920692778), np.float64(142.894933884777), np.float64(140.27489392668804), np.float64(137.60299118522738), np.float64(111.64987131220671), np.float64(107.43705077108397), np.float64(134.835115962981), np.float64(118.28085356890921), np.float64(148.17516041098037), np.float64(137.40340388608539), np.float64(142.791264760495), np.float64(145.66959279516115), np.float64(97.62280948103938), np.float64(111.56887022930991), np.float64(134.88405348805594), np.float64(103.30818649828834), np.float64(95.72858333132508), np.float64(127.59979014914664), np.float64(122.74059257418716), np.float64(107.32015490598884), np.float64(132.45895413157388), np.float64(115.85959140638268), np.float64(124.94241813706567), np.float64(129.88878414759517), np.float64(151.3815873849234), np.float64(130.17189463901045), np.float64(103.45824829689943), np.float64(137.45327355449515), np.float64(125.12390505135583), np.float64(154.09719957045033), np.float64(142.9467966681488), np.float64(140.12226915749514), np.float64(137.60299118522724), np.float64(111.48792791204666), np.float64(120.44637589055738), np.float64(145.61674214924912), np.float64(116.11212334032528), np.float64(148.55202500389677), np.float64(148.33655659585395), np.float64(145.7224626228702), np.float64(127.41471206859332), np.float64(134.83511596298098), np.float64(129.88878414759517), np.float64(137.25390341442292), np.float64(132.41089646114074), np.float64(118.15215920692779), np.float64(179.66097220622717), np.float64(157.37465802998918), np.float64(120.40267652515496), np.float64(113.73525605900525), np.float64(166.3586732123128), np.float64(124.94241813706563), np.float64(127.4609564065188), np.float64(105.23776611903425), np.float64(103.49579779269729), np.float64(146.093089092265), np.float64(127.73877511304768), np.float64(142.99867827477908), np.float64(137.50316132277544), np.float64(157.37465802998915), np.float64(111.52839172763944), np.float64(113.693991567011), np.float64(154.20907701449846), np.float64(132.41089646114074), np.float64(120.402676525155), np.float64(163.24916530180246), np.float64(130.03026234246383), np.float64(120.49009111635922), np.float64(130.0774559772853), np.float64(132.36285622659696), np.float64(120.40267652515494), np.float64(120.40267652515496), np.float64(109.64250258277785), np.float64(145.61674214924895), np.float64(190.3311924664017), np.float64(157.26048398239607), np.float64(140.22400054113638), np.float64(127.32227372062904), np.float64(132.3628562265971), np.float64(99.51839801465182), np.float64(132.50702924422498), np.float64(135.0309726584006), np.float64(127.50721752852449), np.float64(137.7528718922427), np.float64(169.71206344749308), np.float64(145.5639106781745), np.float64(160.02347735891937), np.float64(154.041291289427), np.float64(115.98578864471965), np.float64(129.98308583004922), np.float64(154.32103568365974), np.float64(105.31417070213853), np.float64(154.09719957045033), np.float64(107.43705077108413), np.float64(201.19659064545337), np.float64(107.5150520759087), np.float64(163.48629523503732), np.float64(103.34568153013598), np.float64(148.4981285775914), np.float64(120.31532535253292), np.float64(197.2926685471244), np.float64(160.19777903447897), np.float64(148.65987654767505), np.float64(186.4350275270866), np.float64(127.32227372062894), np.float64(99.62679600605435), np.float64(130.0302623424638), np.float64(145.72246262287024), np.float64(166.23798133402306), np.float64(109.48347078577142), np.float64(105.4671463220819), np.float64(132.41089646114082), np.float64(92.18338992295035), np.float64(151.3815873849233), np.float64(113.6527420462611), np.float64(85.51304197758792), np.float64(115.94370763296656), np.float64(120.35899301439756), np.float64(132.31483342161684), np.float64(166.60031990618103), np.float64(140.32580578366182), np.float64(118.10929221039), np.float64(127.46095640651873), np.float64(172.69382526932478), np.float64(109.40404140901758), np.float64(113.73525605900521), np.float64(115.94370763296668), np.float64(109.4040414090177), np.float64(132.3148334216168), np.float64(125.0785086232192), np.float64(148.3903943806633), np.float64(132.45895413157402), np.float64(129.98308583004928), np.float64(107.2812178883357), np.float64(125.07850862321922), np.float64(94.00746567634374), np.float64(127.41471206859333), np.float64(115.98578864471968), np.float64(151.32666436002185), np.float64(137.45327355449515), np.float64(109.60272299000405), np.float64(90.46033590569894), np.float64(132.5551218054242), np.float64(127.59979014914659), np.float64(154.26504619225517), np.float64(142.84308991783482), np.float64(115.9016418887005), np.float64(120.40267652515494), np.float64(105.27596147920818), np.float64(107.43705077108403), np.float64(113.65274204626117), np.float64(140.17312562030511), np.float64(127.46095640651855), np.float64(113.65274204626111), np.float64(120.49009111635931), np.float64(105.42888159779278), np.float64(145.72246262287013), np.float64(120.27167353381088), np.float64(120.44637589055736), np.float64(109.28500537839668), np.float64(120.31532535253291), np.float64(163.13072934647116), np.float64(127.46095640651862), np.float64(124.98776517204871), np.float64(120.40267652515503), np.float64(132.3628562265971), np.float64(129.88878414759523), np.float64(122.87428477546872), np.float64(140.32580578366165), np.float64(145.8811872664104), np.float64(157.31756064839098), np.float64(125.0331286654421), np.float64(103.53336091683947), np.float64(113.73525605900528), np.float64(120.40267652515503), np.float64(134.98198182931822), np.float64(107.47604434726448), np.float64(124.98776517204875), np.float64(120.44637589055752), np.float64(151.05234799177623), np.float64(130.0302623424637), np.float64(122.65154526229088), np.float64(120.27167353381083), np.float64(157.14639276715553), np.float64(183.01666126014618), np.float64(132.65135929880128), np.float64(107.43705077108405), np.float64(97.5519849185883), np.float64(122.60704583543841), np.float64(127.46095640651863), np.float64(111.52839172763937), np.float64(132.36285622659702), np.float64(154.0971995704503), np.float64(125.12390505135582), np.float64(132.55512180542428), np.float64(111.44747877720332), np.float64(120.27167353381084), np.float64(132.45895413157393), np.float64(118.06644076650426), np.float64(140.2240005411365), np.float64(124.89708755452159), np.float64(160.31408561312224), np.float64(127.55349544070242), np.float64(127.59979014914674), np.float64(142.8430899178348), np.float64(118.23793988053946), np.float64(134.98198182931822), np.float64(120.44637589055748), np.float64(111.64987131220676), np.float64(163.36768724384837), np.float64(137.60299118522738), np.float64(163.4269804793964), np.float64(122.8297045360944), np.float64(132.31483342161684), np.float64(129.84165896513807), np.float64(109.36434833276948), np.float64(125.03312866544202), np.float64(154.32103568365972), np.float64(182.9502606574215), np.float64(132.45895413157402), np.float64(160.31408561312227), np.float64(129.93592643382974), np.float64(132.50702924422487), np.float64(151.32666436002174), np.float64(109.2453554898239), np.float64(135.03097265840054), np.float64(157.08937820288625), np.float64(103.38319017056057), np.float64(120.22803755248339), np.float64(137.7528718922427), np.float64(113.77653552767745), np.float64(157.31756064839098), np.float64(115.9857886447197), np.float64(197.5792488627655), np.float64(124.94241813706564), np.float64(148.49812857759153), np.float64(83.88422213039578), np.float64(122.82970453609451), np.float64(115.85959140638265), np.float64(145.6167421492492), np.float64(105.19958461658737), np.float64(154.09719957045021), np.float64(125.03312866544222), np.float64(109.52320709673616), np.float64(140.1222691574951), np.float64(118.195041761762), np.float64(118.15215920692788), np.float64(127.646101659953), np.float64(157.3746580299892), np.float64(176.04710342989426), np.float64(179.66097220622729), np.float64(173.07023524234324), np.float64(130.07745597728544), np.float64(109.36434833276962), np.float64(151.38158738492325), np.float64(159.9654189544354), np.float64(103.38319017056071), np.float64(135.12900766572366), np.float64(122.56256255350254), np.float64(140.4786522501679), np.float64(169.40441907371874), np.float64(107.35910605557817), np.float64(140.12226915749523), np.float64(125.07850862321918), np.float64(157.26048398239607), np.float64(140.27489392668804), np.float64(166.41905200669189), np.float64(142.8430899178348), np.float64(129.98308583004933), np.float64(107.28121788833585), np.float64(134.93300877468624), np.float64(172.8192042108492), np.float64(111.56887022930992), np.float64(151.38158738492328), np.float64(140.12226915749505), np.float64(93.93926401672087), np.float64(116.02788492950138), np.float64(109.60272299000411), np.float64(88.89843190841911), np.float64(107.35910605557812), np.float64(151.4365303437782), np.float64(107.51505207590887), np.float64(115.77553620544415), np.float64(132.41089646114077), np.float64(197.65095896125473), np.float64(209.01005047634465), np.float64(140.122269157495), np.float64(132.2668280398766), np.float64(160.1977790344787), np.float64(157.26048398239624), np.float64(151.16201481672744), np.float64(117.98078451411929), np.float64(137.45327355449518), np.float64(97.51659190818708), np.float64(132.362856226597), np.float64(124.98776517204871), np.float64(125.12390505135573), np.float64(97.69368546344514), np.float64(153.9854032925874), np.float64(151.38158738492328), np.float64(143.3103635857457), np.float64(109.5232070967362), np.float64(125.03312866544213), np.float64(137.60299118522727), np.float64(122.51809541062599), np.float64(137.4532735544952), np.float64(125.26019321771078), np.float64(129.98308583004933), np.float64(107.39807134223291), np.float64(92.28379843542312), np.float64(125.12390505135588), np.float64(176.2388584499182), np.float64(122.69606083991971), np.float64(127.4609564065186), np.float64(125.16931795582963), np.float64(151.27176126184114), np.float64(156.97541112351135), np.float64(134.78619619301708), np.float64(145.775351639338), np.float64(130.1246667407288), np.float64(105.35239379285706), np.float64(97.69368546344515), np.float64(169.465903301674), np.float64(154.0971995704504), np.float64(166.1776682342193), np.float64(120.09722457541875), np.float64(125.16931795582946), np.float64(99.5906502297257), np.float64(129.98308583004913), np.float64(134.8840534880561), np.float64(115.94370763296646), np.float64(120.57756917218902), np.float64(157.03238432417317), np.float64(113.69399156701101), np.float64(129.93592643382974), np.float64(107.28121788833577), np.float64(140.17312562030523), np.float64(137.40340388608539), np.float64(122.65154526229091), np.float64(120.40267652515504), np.float64(113.85913941680047), np.float64(148.55202500389686), np.float64(109.32466965763751), np.float64(122.69606083991984), np.float64(127.46095640651865), np.float64(145.93413389095113), np.float64(127.46095640651858), np.float64(93.80300910084303), np.float64(166.41905200669186), np.float64(129.74745988673612), np.float64(135.17805185687368), np.float64(145.61674214924903), np.float64(163.24916530180244), np.float64(132.4108964611408), np.float64(163.3676872438483), np.float64(115.81755618047559), np.float64(120.402676525155), np.float64(160.43047663251318), np.float64(120.31532535253285), np.float64(127.41471206859339), np.float64(107.43705077108399), np.float64(125.21474734262044), np.float64(142.73945840593353), np.float64(113.69399156701105), np.float64(160.43047663251312), np.float64(120.44637589055735), np.float64(132.45895413157393), np.float64(154.37704549608458), np.float64(160.13965739129182), np.float64(186.4350275270868), np.float64(135.07998126838464), np.float64(122.74059257418699), np.float64(157.08937820288622), np.float64(115.77553620544408), np.float64(127.46095640651866), np.float64(111.60936342238828), np.float64(122.74059257418709), np.float64(113.85913941680042), np.float64(118.23793988053947), np.float64(148.44425170554516), np.float64(115.98578864471969), np.float64(107.24229499749201), np.float64(134.884053488056), np.float64(157.3175606483912), np.float64(135.03097265840069), np.float64(93.90518174406166), np.float64(166.41905200669166), np.float64(115.77553620544427), np.float64(124.8517734184475), np.float64(140.12226915749514), np.float64(120.27167353381093), np.float64(124.98776517204877), np.float64(113.77653552767738), np.float64(125.21474734262056), np.float64(137.75287189224258), np.float64(120.53382220831688), np.float64(115.98578864471966), np.float64(151.27176126184133), np.float64(109.40404140901767), np.float64(137.6529332925443), np.float64(129.9830858300491), np.float64(145.61674214924892), np.float64(115.77553620544414), np.float64(107.32015490598876), np.float64(115.90164188870058), np.float64(157.14639276715542), np.float64(140.17312562030537), np.float64(111.64987131220673), np.float64(127.36848450865926), np.float64(148.55202500389674), np.float64(129.93592643382968), np.float64(113.65274204626111), np.float64(129.79455088025276), np.float64(113.69399156701101), np.float64(148.55202500389672), np.float64(79.18156086459858), np.float64(93.93926401672078), np.float64(130.03026234246374), np.float64(115.98578864471968), np.float64(127.50721752852441), np.float64(111.56887022930991), np.float64(137.5530671974958), np.float64(120.4463758905574), np.float64(142.79126476049498), np.float64(125.2147473426205), np.float64(140.37673611876158), np.float64(107.28121788833583), np.float64(125.03312866544206), np.float64(113.69399156701103), np.float64(118.15215920692776), np.float64(129.74745988673604), np.float64(107.3201549059887), np.float64(134.88405348805608), np.float64(163.3084155205854), np.float64(111.77148321545317), np.float64(118.19504176176203), np.float64(135.22711384829248), np.float64(145.98709973212374), np.float64(120.40267652515494), np.float64(118.15215920692776), np.float64(148.49812857759156), np.float64(151.10717145534275), np.float64(172.81920421084922), np.float64(135.1780518568735), np.float64(111.52839172763946), np.float64(157.2034280244888), np.float64(122.74059257418709), np.float64(137.55306719749575), np.float64(148.2289396180981), np.float64(113.81782997846324), np.float64(113.6115074913236), np.float64(95.69385187252003), np.float64(132.3148334216168), np.float64(132.26682803987663), np.float64(129.84165896513807), np.float64(148.4981285775914), np.float64(166.47945271515985), np.float64(101.52444926331253), np.float64(160.25592177253702), np.float64(132.36285622659707), np.float64(124.98776517204874), np.float64(118.28085356890918), np.float64(157.31756064839092), np.float64(140.12226915749497), np.float64(120.27167353381095), np.float64(148.33655659585418), np.float64(122.91888119494985), np.float64(140.32580578366185), np.float64(148.28273834402782), np.float64(154.2090770144983), np.float64(166.17766823421925), np.float64(132.4108964611407), np.float64(111.52839172763946), np.float64(140.17312562030511), np.float64(109.44374889160882), np.float64(142.8949338847772), np.float64(120.44637589055743), np.float64(127.69242997921981), np.float64(135.03097265840057), np.float64(132.50702924422507), np.float64(120.40267652515499), np.float64(154.26504619225526), np.float64(118.1950417617619), np.float64(94.0415850722883), np.float64(127.6924299792198), np.float64(186.7058364194228), np.float64(122.82970453609435), np.float64(151.27176126184133), np.float64(151.10717145534272), np.float64(103.4207124245015), np.float64(157.08937820288634), np.float64(134.93300877468627), np.float64(176.1749169139726), np.float64(125.07850862321916), np.float64(88.7050869562431), np.float64(103.42071242450153), np.float64(118.28085356890921), np.float64(95.83285335937953), np.float64(148.5520250038967), np.float64(113.77653552767742), np.float64(160.13965739129168), np.float64(113.90046384812848), np.float64(151.2717612618412)]

Let's create a CDF with this simulation results.

In [52]:
sim_weights_cdf = (
    pd.Series(sim_weights).value_counts(normalize=True).sort_index().cumsum()
)
sim_weights_cdf
Out[52]:
76.221503     0.000999
79.181561     0.001998
82.256573     0.002997
83.884222     0.003996
85.420000     0.004995
                ...   
197.866245    0.996004
201.196591    0.997003
201.415739    0.998002
209.010050    0.999001
209.237710    1.000000
Name: proportion, Length: 841, dtype: float64

Let's first see how this CDF looks.

In [53]:
fig, ax = plt.subplots()
ax.plot(sim_weights_cdf.index, sim_weights_cdf)
plt.show()
No description has been provided for this image

And now, let's compare it to a normal distribution with the same parameters.

In [54]:
mean, std = np.mean(sim_weights), np.std(sim_weights)
low, high = mean - 4 * std, mean + 4 * std
print(mean, std)
print(low, high)
131.19098690328582 21.242076774032693
46.222679807155046 216.1592939994166
In [55]:
x_ticks = np.linspace(low, high, sim_cdf_size)
print(x_ticks)
[ 46.22267981  49.55477028  52.88686076  56.21895123  59.5510417
  62.88313218  66.21522265  69.54731313  72.8794036   76.21149408
  79.54358455  82.87567503  86.2077655   89.53985597  92.87194645
  96.20403692  99.5361274  102.86821787 106.20030835 109.53239882
 112.86448929 116.19657977 119.52867024 122.86076072 126.19285119
 129.52494167 132.85703214 136.18912261 139.52121309 142.85330356
 146.18539404 149.51748451 152.84957499 156.18166546 159.51375594
 162.84584641 166.17793688 169.51002736 172.84211783 176.17420831
 179.50629878 182.83838926 186.17047973 189.5025702  192.83466068
 196.16675115 199.49884163 202.8309321  206.16302258 209.49511305
 212.82720353 216.159294  ]
In [56]:
norm_dist = norm.cdf(x_ticks, loc=mean, scale=std)
norm_dist_cdf = pd.Series(norm_dist, index=x_ticks)
norm_dist_cdf
Out[56]:
46.222680     0.000032
49.554770     0.000061
52.886861     0.000114
56.218951     0.000208
59.551042     0.000372
62.883132     0.000651
66.215223     0.001111
69.547313     0.001854
72.879404     0.003025
76.211494     0.004823
79.543585     0.007521
82.875675     0.011468
86.207765     0.017102
89.539856     0.024952
92.871946     0.035622
96.204037     0.049773
99.536127     0.068086
102.868218    0.091211
106.200308    0.119703
109.532399    0.153957
112.864489    0.194139
116.196580    0.240131
119.528670    0.291496
122.860761    0.347471
126.192851    0.406990
129.524942    0.468742
132.857032    0.531258
136.189123    0.593010
139.521213    0.652529
142.853304    0.708504
146.185394    0.759869
149.517485    0.805861
152.849575    0.846043
156.181665    0.880297
159.513756    0.908789
162.845846    0.931914
166.177937    0.950227
169.510027    0.964378
172.842118    0.975048
176.174208    0.982898
179.506299    0.988532
182.838389    0.992479
186.170480    0.995177
189.502570    0.996975
192.834661    0.998146
196.166751    0.998889
199.498842    0.999349
202.830932    0.999628
206.163023    0.999792
209.495113    0.999886
212.827204    0.999939
216.159294    0.999968
dtype: float64
In [57]:
fig, ax = plt.subplots()
ax.plot(sim_weights_cdf.index, sim_weights_cdf)
ax.plot(norm_dist_cdf.index, norm_dist_cdf)
plt.show()
No description has been provided for this image

Even though we gave the function the same parameters, it is not matching. Because it is not a normal distribution, it is a log-normal distribution. Which means, logarithm of this distribution is normal distribution. Let's take a closer look.

Note: In our example, it doesn't really matter if we use log10, ln or log2. But we should consider carefully which one to use when we start working with regression models.

In [58]:
sim_weights_log_cdf = (
    pd.Series(np.log10(sim_weights)).value_counts(normalize=True).sort_index().cumsum()
)
print(sim_weights_log_cdf)
1.882078    0.000999
1.898624    0.001998
1.915171    0.002997
1.923680    0.003996
1.931560    0.004995
              ...   
2.296372    0.996004
2.303621    0.997003
2.304093    0.998002
2.320167    0.999001
2.320640    1.000000
Name: proportion, Length: 526, dtype: float64
In [59]:
mean_log, std_log = np.mean(np.log10(sim_weights)), np.std(np.log10(sim_weights))
low_log, high_log = mean_log - 4 * std_log, mean_log + 4 * std_log
sim_cdf_size = sim_weights_log_cdf.count()
print(mean_log, std_log)
print(low_log, high_log)
2.1123188399072723 0.06954068560921654
1.834156097470406 2.3904815823441385
In [60]:
x_ticks = np.linspace(low_log, high_log, sim_cdf_size)
print(x_ticks)
[1.8341561  1.83521577 1.83627543 1.8373351  1.83839477 1.83945444
 1.8405141  1.84157377 1.84263344 1.84369311 1.84475277 1.84581244
 1.84687211 1.84793178 1.84899144 1.85005111 1.85111078 1.85217045
 1.85323011 1.85428978 1.85534945 1.85640912 1.85746878 1.85852845
 1.85958812 1.86064779 1.86170745 1.86276712 1.86382679 1.86488646
 1.86594613 1.86700579 1.86806546 1.86912513 1.8701848  1.87124446
 1.87230413 1.8733638  1.87442347 1.87548313 1.8765428  1.87760247
 1.87866214 1.8797218  1.88078147 1.88184114 1.88290081 1.88396047
 1.88502014 1.88607981 1.88713948 1.88819914 1.88925881 1.89031848
 1.89137815 1.89243781 1.89349748 1.89455715 1.89561682 1.89667649
 1.89773615 1.89879582 1.89985549 1.90091516 1.90197482 1.90303449
 1.90409416 1.90515383 1.90621349 1.90727316 1.90833283 1.9093925
 1.91045216 1.91151183 1.9125715  1.91363117 1.91469083 1.9157505
 1.91681017 1.91786984 1.9189295  1.91998917 1.92104884 1.92210851
 1.92316818 1.92422784 1.92528751 1.92634718 1.92740685 1.92846651
 1.92952618 1.93058585 1.93164552 1.93270518 1.93376485 1.93482452
 1.93588419 1.93694385 1.93800352 1.93906319 1.94012286 1.94118252
 1.94224219 1.94330186 1.94436153 1.94542119 1.94648086 1.94754053
 1.9486002  1.94965986 1.95071953 1.9517792  1.95283887 1.95389854
 1.9549582  1.95601787 1.95707754 1.95813721 1.95919687 1.96025654
 1.96131621 1.96237588 1.96343554 1.96449521 1.96555488 1.96661455
 1.96767421 1.96873388 1.96979355 1.97085322 1.97191288 1.97297255
 1.97403222 1.97509189 1.97615155 1.97721122 1.97827089 1.97933056
 1.98039022 1.98144989 1.98250956 1.98356923 1.9846289  1.98568856
 1.98674823 1.9878079  1.98886757 1.98992723 1.9909869  1.99204657
 1.99310624 1.9941659  1.99522557 1.99628524 1.99734491 1.99840457
 1.99946424 2.00052391 2.00158358 2.00264324 2.00370291 2.00476258
 2.00582225 2.00688191 2.00794158 2.00900125 2.01006092 2.01112059
 2.01218025 2.01323992 2.01429959 2.01535926 2.01641892 2.01747859
 2.01853826 2.01959793 2.02065759 2.02171726 2.02277693 2.0238366
 2.02489626 2.02595593 2.0270156  2.02807527 2.02913493 2.0301946
 2.03125427 2.03231394 2.0333736  2.03443327 2.03549294 2.03655261
 2.03761227 2.03867194 2.03973161 2.04079128 2.04185095 2.04291061
 2.04397028 2.04502995 2.04608962 2.04714928 2.04820895 2.04926862
 2.05032829 2.05138795 2.05244762 2.05350729 2.05456696 2.05562662
 2.05668629 2.05774596 2.05880563 2.05986529 2.06092496 2.06198463
 2.0630443  2.06410396 2.06516363 2.0662233  2.06728297 2.06834263
 2.0694023  2.07046197 2.07152164 2.07258131 2.07364097 2.07470064
 2.07576031 2.07681998 2.07787964 2.07893931 2.07999898 2.08105865
 2.08211831 2.08317798 2.08423765 2.08529732 2.08635698 2.08741665
 2.08847632 2.08953599 2.09059565 2.09165532 2.09271499 2.09377466
 2.09483432 2.09589399 2.09695366 2.09801333 2.099073   2.10013266
 2.10119233 2.102252   2.10331167 2.10437133 2.105431   2.10649067
 2.10755034 2.10861    2.10966967 2.11072934 2.11178901 2.11284867
 2.11390834 2.11496801 2.11602768 2.11708734 2.11814701 2.11920668
 2.12026635 2.12132601 2.12238568 2.12344535 2.12450502 2.12556468
 2.12662435 2.12768402 2.12874369 2.12980336 2.13086302 2.13192269
 2.13298236 2.13404203 2.13510169 2.13616136 2.13722103 2.1382807
 2.13934036 2.14040003 2.1414597  2.14251937 2.14357903 2.1446387
 2.14569837 2.14675804 2.1478177  2.14887737 2.14993704 2.15099671
 2.15205637 2.15311604 2.15417571 2.15523538 2.15629504 2.15735471
 2.15841438 2.15947405 2.16053372 2.16159338 2.16265305 2.16371272
 2.16477239 2.16583205 2.16689172 2.16795139 2.16901106 2.17007072
 2.17113039 2.17219006 2.17324973 2.17430939 2.17536906 2.17642873
 2.1774884  2.17854806 2.17960773 2.1806674  2.18172707 2.18278673
 2.1838464  2.18490607 2.18596574 2.18702541 2.18808507 2.18914474
 2.19020441 2.19126408 2.19232374 2.19338341 2.19444308 2.19550275
 2.19656241 2.19762208 2.19868175 2.19974142 2.20080108 2.20186075
 2.20292042 2.20398009 2.20503975 2.20609942 2.20715909 2.20821876
 2.20927842 2.21033809 2.21139776 2.21245743 2.21351709 2.21457676
 2.21563643 2.2166961  2.21775577 2.21881543 2.2198751  2.22093477
 2.22199444 2.2230541  2.22411377 2.22517344 2.22623311 2.22729277
 2.22835244 2.22941211 2.23047178 2.23153144 2.23259111 2.23365078
 2.23471045 2.23577011 2.23682978 2.23788945 2.23894912 2.24000878
 2.24106845 2.24212812 2.24318779 2.24424745 2.24530712 2.24636679
 2.24742646 2.24848613 2.24954579 2.25060546 2.25166513 2.2527248
 2.25378446 2.25484413 2.2559038  2.25696347 2.25802313 2.2590828
 2.26014247 2.26120214 2.2622618  2.26332147 2.26438114 2.26544081
 2.26650047 2.26756014 2.26861981 2.26967948 2.27073914 2.27179881
 2.27285848 2.27391815 2.27497782 2.27603748 2.27709715 2.27815682
 2.27921649 2.28027615 2.28133582 2.28239549 2.28345516 2.28451482
 2.28557449 2.28663416 2.28769383 2.28875349 2.28981316 2.29087283
 2.2919325  2.29299216 2.29405183 2.2951115  2.29617117 2.29723083
 2.2982905  2.29935017 2.30040984 2.3014695  2.30252917 2.30358884
 2.30464851 2.30570818 2.30676784 2.30782751 2.30888718 2.30994685
 2.31100651 2.31206618 2.31312585 2.31418552 2.31524518 2.31630485
 2.31736452 2.31842419 2.31948385 2.32054352 2.32160319 2.32266286
 2.32372252 2.32478219 2.32584186 2.32690153 2.32796119 2.32902086
 2.33008053 2.3311402  2.33219986 2.33325953 2.3343192  2.33537887
 2.33643854 2.3374982  2.33855787 2.33961754 2.34067721 2.34173687
 2.34279654 2.34385621 2.34491588 2.34597554 2.34703521 2.34809488
 2.34915455 2.35021421 2.35127388 2.35233355 2.35339322 2.35445288
 2.35551255 2.35657222 2.35763189 2.35869155 2.35975122 2.36081089
 2.36187056 2.36293022 2.36398989 2.36504956 2.36610923 2.3671689
 2.36822856 2.36928823 2.3703479  2.37140757 2.37246723 2.3735269
 2.37458657 2.37564624 2.3767059  2.37776557 2.37882524 2.37988491
 2.38094457 2.38200424 2.38306391 2.38412358 2.38518324 2.38624291
 2.38730258 2.38836225 2.38942191 2.39048158]
In [61]:
norm_dist_log = norm.cdf(x_ticks, loc=mean_log, scale=std_log)
norm_dist_log_cdf = pd.Series(norm_dist_log, index=x_ticks)
norm_dist_log_cdf
Out[61]:
1.834156    0.000032
1.835216    0.000034
1.836275    0.000036
1.837335    0.000038
1.838395    0.000041
              ...   
2.386243    0.999959
2.387303    0.999962
2.388362    0.999964
2.389422    0.999966
2.390482    0.999968
Length: 526, dtype: float64

Let's see the first result where we compared the distribution to a normal distribution. And the second result where we compared the lograithm of the distribution with a normal distribution.

In [62]:
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
ax[0].plot(sim_weights_cdf.index, sim_weights_cdf)
ax[0].plot(norm_dist_cdf.index, norm_dist_cdf)
ax[1].plot(sim_weights_log_cdf.index, sim_weights_log_cdf)
ax[1].plot(norm_dist_log_cdf.index, norm_dist_log_cdf)
ax[0].set_title("Direct comparison with normal distribution")
ax[1].set_title("Comparison with normal distribution after log10")
ax[0].set_ylabel("Cum. Prob.")
ax[1].set_ylabel("Cum. Prob.")
ax[0].set_xlabel("lbs")
ax[1].set_xlabel("log10(lbs)")
plt.show()
No description has been provided for this image

If people are like pumpkins, we expect adult weights distribution to be the same. Let's test this out.

First we import our database which is from BRFSS.

In [63]:
brfss = pd.read_csv("./data/brfss.csv", compression="gzip")
brfss
Out[63]:
age sex wtyrago finalwt wtkg2 htm3
0 82.0 2 76.363636 185.870345 70.91 157.0
1 65.0 2 72.727273 126.603027 72.73 163.0
2 48.0 2 NaN 181.063210 NaN 165.0
3 61.0 1 73.636364 517.926275 73.64 170.0
4 26.0 1 88.636364 1252.624630 88.64 185.0
... ... ... ... ... ... ...
414504 23.0 1 84.090909 43.439320 88.64 191.0
414505 49.0 2 70.454545 6.216538 72.73 170.0
414506 45.0 1 86.363636 20.617560 90.91 178.0
414507 52.0 2 89.090909 11.188138 89.09 157.0
414508 38.0 1 75.000000 25.989792 75.00 178.0

414509 rows × 6 columns

In [64]:
adult_weights = brfss["wtkg2"].dropna()
adult_weights
log_adult_weights = pd.Series(np.log10(adult_weights))
In [65]:
mean, std = adult_weights.mean(), adult_weights.std()
low, high = mean - 4 * std, mean + 4 * std
print("Mean:", mean)
print("std:", std)
print("-4 std:", low)
print("+4 std:", high)
x_ticks = np.linspace(low, high, 200)
print("X ticks", x_ticks)
Mean: 78.99245299685809
std: 19.546156913060994
-4 std: 0.8078253446141161
+4 std: 157.17708064910207
X ticks [  0.80782534   1.5936005    2.37937565   3.1651508    3.95092595
   4.73670111   5.52247626   6.30825141   7.09402656   7.87980172
   8.66557687   9.45135202  10.23712717  11.02290232  11.80867748
  12.59445263  13.38022778  14.16600293  14.95177809  15.73755324
  16.52332839  17.30910354  18.09487869  18.88065385  19.666429
  20.45220415  21.2379793   22.02375446  22.80952961  23.59530476
  24.38107991  25.16685507  25.95263022  26.73840537  27.52418052
  28.30995567  29.09573083  29.88150598  30.66728113  31.45305628
  32.23883144  33.02460659  33.81038174  34.59615689  35.38193205
  36.1677072   36.95348235  37.7392575   38.52503265  39.31080781
  40.09658296  40.88235811  41.66813326  42.45390842  43.23968357
  44.02545872  44.81123387  45.59700902  46.38278418  47.16855933
  47.95433448  48.74010963  49.52588479  50.31165994  51.09743509
  51.88321024  52.6689854   53.45476055  54.2405357   55.02631085
  55.812086    56.59786116  57.38363631  58.16941146  58.95518661
  59.74096177  60.52673692  61.31251207  62.09828722  62.88406238
  63.66983753  64.45561268  65.24138783  66.02716298  66.81293814
  67.59871329  68.38448844  69.17026359  69.95603875  70.7418139
  71.52758905  72.3133642   73.09913935  73.88491451  74.67068966
  75.45646481  76.24223996  77.02801512  77.81379027  78.59956542
  79.38534057  80.17111573  80.95689088  81.74266603  82.52844118
  83.31421633  84.09999149  84.88576664  85.67154179  86.45731694
  87.2430921   88.02886725  88.8146424   89.60041755  90.3861927
  91.17196786  91.95774301  92.74351816  93.52929331  94.31506847
  95.10084362  95.88661877  96.67239392  97.45816908  98.24394423
  99.02971938  99.81549453 100.60126968 101.38704484 102.17281999
 102.95859514 103.74437029 104.53014545 105.3159206  106.10169575
 106.8874709  107.67324606 108.45902121 109.24479636 110.03057151
 110.81634666 111.60212182 112.38789697 113.17367212 113.95944727
 114.74522243 115.53099758 116.31677273 117.10254788 117.88832303
 118.67409819 119.45987334 120.24564849 121.03142364 121.8171988
 122.60297395 123.3887491  124.17452425 124.96029941 125.74607456
 126.53184971 127.31762486 128.10340001 128.88917517 129.67495032
 130.46072547 131.24650062 132.03227578 132.81805093 133.60382608
 134.38960123 135.17537639 135.96115154 136.74692669 137.53270184
 138.31847699 139.10425215 139.8900273  140.67580245 141.4615776
 142.24735276 143.03312791 143.81890306 144.60467821 145.39045336
 146.17622852 146.96200367 147.74777882 148.53355397 149.31932913
 150.10510428 150.89087943 151.67665458 152.46242974 153.24820489
 154.03398004 154.81975519 155.60553034 156.3913055  157.17708065]
In [66]:
norm_dist = norm.cdf(x_ticks, loc=mean, scale=std)
norm_dist_cdf = pd.Series(norm_dist, index=x_ticks)
norm_dist_cdf
Out[66]:
0.807825      0.000032
1.593600      0.000038
2.379376      0.000044
3.165151      0.000052
3.950926      0.000062
                ...   
154.033980    0.999938
154.819755    0.999948
155.605530    0.999956
156.391305    0.999962
157.177081    0.999968
Length: 200, dtype: float64
In [67]:
adult_weights_cdf = adult_weights.value_counts(normalize=True).sort_index().cumsum()
adult_weights_cdf
Out[67]:
wtkg2
20.00     0.000003
22.73     0.000083
23.64     0.000090
24.09     0.000093
24.55     0.000095
            ...   
265.91    0.999977
272.73    0.999992
295.45    0.999995
300.00    0.999997
309.09    1.000000
Name: proportion, Length: 473, dtype: float64
In [68]:
fig, ax = plt.subplots()
ax.plot(adult_weights_cdf.index, adult_weights_cdf)
ax.plot(norm_dist_cdf.index, norm_dist_cdf)
plt.show()
No description has been provided for this image

Without taking the logarithm of the weights, normal distribution doesn't really align with it.

Let's limit the x with the high to see it better.

In [69]:
fig, ax = plt.subplots()
ax.plot(adult_weights_cdf.index, adult_weights_cdf)
ax.plot(norm_dist_cdf.index, norm_dist_cdf)
ax.set_xlim(right=high)
plt.show()
No description has been provided for this image

Another thing to notice is that the data we have is right skewed. In this distribution, taking the logarithm results with bigger values getting normalized.

In [70]:
log_adult_weights_cdf = (
    log_adult_weights.value_counts(normalize=True).sort_index().cumsum()
)
log_adult_weights_cdf
fig, ax = plt.subplots()
ax.plot(log_adult_weights_cdf.index, log_adult_weights_cdf)
plt.show()
No description has been provided for this image
In [71]:
mean, std = log_adult_weights.mean(), log_adult_weights.std()
low, high = mean - 4 * std, mean + 4 * std
print("Mean:", mean)
print("std:", std)
print("-4 std:", low)
print("+4 std:", high)
x_ticks = np.linspace(low, high, 200)
print("X ticks", x_ticks)
Mean: 1.8851167216523483
std: 0.10326489424508913
-4 std: 1.472057144671992
+4 std: 2.298176298632705
X ticks [1.47205714 1.4762085  1.48035985 1.4845112  1.48866255 1.49281391
 1.49696526 1.50111661 1.50526796 1.50941932 1.51357067 1.51772202
 1.52187338 1.52602473 1.53017608 1.53432743 1.53847879 1.54263014
 1.54678149 1.55093284 1.5550842  1.55923555 1.5633869  1.56753825
 1.57168961 1.57584096 1.57999231 1.58414366 1.58829502 1.59244637
 1.59659772 1.60074907 1.60490043 1.60905178 1.61320313 1.61735448
 1.62150584 1.62565719 1.62980854 1.63395989 1.63811125 1.6422626
 1.64641395 1.6505653  1.65471666 1.65886801 1.66301936 1.66717071
 1.67132207 1.67547342 1.67962477 1.68377612 1.68792748 1.69207883
 1.69623018 1.70038153 1.70453289 1.70868424 1.71283559 1.71698694
 1.7211383  1.72528965 1.729441   1.73359235 1.73774371 1.74189506
 1.74604641 1.75019776 1.75434912 1.75850047 1.76265182 1.76680317
 1.77095453 1.77510588 1.77925723 1.78340858 1.78755994 1.79171129
 1.79586264 1.80001399 1.80416535 1.8083167  1.81246805 1.8166194
 1.82077076 1.82492211 1.82907346 1.83322481 1.83737617 1.84152752
 1.84567887 1.84983023 1.85398158 1.85813293 1.86228428 1.86643564
 1.87058699 1.87473834 1.87888969 1.88304105 1.8871924  1.89134375
 1.8954951  1.89964646 1.90379781 1.90794916 1.91210051 1.91625187
 1.92040322 1.92455457 1.92870592 1.93285728 1.93700863 1.94115998
 1.94531133 1.94946269 1.95361404 1.95776539 1.96191674 1.9660681
 1.97021945 1.9743708  1.97852215 1.98267351 1.98682486 1.99097621
 1.99512756 1.99927892 2.00343027 2.00758162 2.01173297 2.01588433
 2.02003568 2.02418703 2.02833838 2.03248974 2.03664109 2.04079244
 2.04494379 2.04909515 2.0532465  2.05739785 2.0615492  2.06570056
 2.06985191 2.07400326 2.07815461 2.08230597 2.08645732 2.09060867
 2.09476002 2.09891138 2.10306273 2.10721408 2.11136543 2.11551679
 2.11966814 2.12381949 2.12797084 2.1321222  2.13627355 2.1404249
 2.14457625 2.14872761 2.15287896 2.15703031 2.16118167 2.16533302
 2.16948437 2.17363572 2.17778708 2.18193843 2.18608978 2.19024113
 2.19439249 2.19854384 2.20269519 2.20684654 2.2109979  2.21514925
 2.2193006  2.22345195 2.22760331 2.23175466 2.23590601 2.24005736
 2.24420872 2.24836007 2.25251142 2.25666277 2.26081413 2.26496548
 2.26911683 2.27326818 2.27741954 2.28157089 2.28572224 2.28987359
 2.29402495 2.2981763 ]
In [72]:
norm_dist = norm.cdf(x_ticks, loc=mean, scale=std)
norm_dist_cdf = pd.Series(norm_dist, index=x_ticks)
norm_dist_cdf
Out[72]:
1.472057    0.000032
1.476208    0.000038
1.480360    0.000044
1.484511    0.000052
1.488663    0.000062
              ...   
2.281571    0.999938
2.285722    0.999948
2.289874    0.999956
2.294025    0.999962
2.298176    0.999968
Length: 200, dtype: float64
In [73]:
fig, ax = plt.subplots()
ax.plot(log_adult_weights_cdf.index, log_adult_weights_cdf, label="log10(actual)")
ax.plot(norm_dist_cdf.index, norm_dist_cdf, label="normal_cdf")
ax.set_ylabel("Cum. Prob.")
ax.set_xlabel("log10(weight)")
ax.legend()
Out[73]:
<matplotlib.legend.Legend at 0x7f6bceddcf50>
No description has been provided for this image

The normal model fits the logratihms of the weights well. Which suggests that proportional growth is a better model for weight gain.

Exercises

In [74]:
from statadict import parse_stata_dict

fem_dict = parse_stata_dict("./data/2002FemResp.dct")
fem = pd.read_fwf(
    "./data/2002FemResp.dat", names=fem_dict.names, colspecs=fem_dict.colspecs
)
In [75]:
fem
Out[75]:
caseid rscrinf rdormres rostscrn rscreenhisp rscreenrace age_a age_r cmbirth agescrn ... pubassis_i basewgt adj_mod_basewgt finalwgt secu_r sest cmintvw cmlstyr screentime intvlngth
0 2298 1 5 5 1 5.0 27 27 902 27 ... 0 3247.916977 5123.759559 5556.717241 2 18 1234 1222 18:26:36 110.492667
1 5012 1 5 1 5 5.0 42 42 718 42 ... 0 2335.279149 2846.799490 4744.191350 2 18 1233 1221 16:30:59 64.294000
2 11586 1 5 1 5 5.0 43 43 708 43 ... 0 2335.279149 2846.799490 4744.191350 2 18 1234 1222 18:19:09 75.149167
3 6794 5 5 4 1 5.0 15 15 1042 15 ... 0 3783.152221 5071.464231 5923.977368 2 18 1234 1222 15:54:43 28.642833
4 616 1 5 4 1 5.0 20 20 991 20 ... 0 5341.329968 6437.335772 7229.128072 2 18 1233 1221 14:19:44 69.502667
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
7638 11018 1 5 2 5 3.0 34 34 811 34 ... 0 3247.445399 3784.333145 6565.818007 2 76 1228 1216 15:57:38 82.907333
7639 6075 5 5 3 5 3.0 17 17 1014 17 ... 0 2273.211779 2497.234491 4392.385746 2 76 1228 1216 18:23:53 54.044833
7640 5649 1 5 2 5 5.0 29 29 873 29 ... 0 3247.445399 3569.313710 6003.228729 2 76 1228 1216 18:42:41 68.168000
7641 501 5 5 3 5 2.0 16 16 1034 16 ... 0 5304.160818 5954.644352 10473.623950 2 76 1228 1216 16:02:45 32.717333
7642 10252 1 5 2 5 2.0 28 28 889 28 ... 0 3247.445399 3476.637428 5847.356491 2 76 1230 1218 12:45:19 74.061500

7643 rows × 3087 columns

In [76]:
fem["cmmarrhx"] = fem.cmmarrhx.replace([9997, 9998, 9999], np.nan)
fem["agemarry"] = (fem.cmmarrhx - fem.cmbirth) / 12.0
fem["age"] = (fem.cmintvw - fem.cmbirth) / 12.0
/tmp/ipykernel_2786/872713941.py:2: PerformanceWarning: DataFrame is highly fragmented.  This is usually the result of calling `frame.insert` many times, which has poor performance.  Consider joining all columns at once using pd.concat(axis=1) instead. To get a de-fragmented frame, use `newframe = frame.copy()`
  fem["agemarry"] = (fem.cmmarrhx - fem.cmbirth) / 12.0
/tmp/ipykernel_2786/872713941.py:3: PerformanceWarning: DataFrame is highly fragmented.  This is usually the result of calling `frame.insert` many times, which has poor performance.  Consider joining all columns at once using pd.concat(axis=1) instead. To get a de-fragmented frame, use `newframe = frame.copy()`
  fem["age"] = (fem.cmintvw - fem.cmbirth) / 12.0
In [77]:
older = fem[fem["age"] >= 25]
older_numfmhh_pmf = older["numfmhh"].value_counts(normalize=True).sort_index()
mean_o_n_pmf = older["numfmhh"].mean()
older_numfmhh_pmf
Out[77]:
numfmhh
0    0.118334
1    0.229272
2    0.239587
3    0.231608
4    0.120475
5    0.039510
6    0.013429
7    0.007785
Name: proportion, dtype: float64
In [78]:
fig, ax = plt.subplots()
ax.plot(older_numfmhh_pmf.index, older_numfmhh_pmf, color="blue")
plt.show()
No description has been provided for this image
In [79]:
x_ticks = np.arange(0, 8)
x_ticks
poisson_dist = poisson.pmf(k=x_ticks, mu=mean_o_n_pmf, loc=0)
poisson_pmf = pd.Series(poisson_dist, index=x_ticks)
poisson_pmf
Out[79]:
0    0.108850
1    0.241405
2    0.267693
3    0.197895
4    0.109723
5    0.048668
6    0.017989
7    0.005700
dtype: float64
In [80]:
fig, ax = plt.subplots()
ax.plot(older_numfmhh_pmf.index, older_numfmhh_pmf, color="blue")
ax.plot(poisson_pmf.index, poisson_pmf, color="red")
plt.show()
No description has been provided for this image

It seems that for the older female population (>=25), distribution of number of people in the household aligns with the Poisson distribution.


In [81]:
adult_heights = brfss["htm3"].dropna()
adult_heights_log = pd.Series(np.log10(adult_heights))
m, s = np.mean(adult_heights), np.std(adult_heights)
m, s
Out[81]:
(np.float64(168.82518961012298), np.float64(10.352640156455918))
In [82]:
mean, std = adult_heights.mean(), adult_heights.std()
low, high = mean - 4 * std, mean + 4 * std
print("Mean:", mean)
print("std:", std)
print("-4 std:", low)
print("+4 std:", high)
x_ticks = np.linspace(low, high, 200)
print("X ticks", x_ticks)
Mean: 168.82518961012298
std: 10.352652808527923
-4 std: 127.41457837601129
+4 std: 210.23580084423466
X ticks [127.41457838 127.83076542 128.24695247 128.66313952 129.07932657
 129.49551361 129.91170066 130.32788771 130.74407476 131.1602618
 131.57644885 131.9926359  132.40882295 132.82500999 133.24119704
 133.65738409 134.07357114 134.48975818 134.90594523 135.32213228
 135.73831933 136.15450638 136.57069342 136.98688047 137.40306752
 137.81925457 138.23544161 138.65162866 139.06781571 139.48400276
 139.9001898  140.31637685 140.7325639  141.14875095 141.56493799
 141.98112504 142.39731209 142.81349914 143.22968618 143.64587323
 144.06206028 144.47824733 144.89443437 145.31062142 145.72680847
 146.14299552 146.55918256 146.97536961 147.39155666 147.80774371
 148.22393075 148.6401178  149.05630485 149.4724919  149.88867895
 150.30486599 150.72105304 151.13724009 151.55342714 151.96961418
 152.38580123 152.80198828 153.21817533 153.63436237 154.05054942
 154.46673647 154.88292352 155.29911056 155.71529761 156.13148466
 156.54767171 156.96385875 157.3800458  157.79623285 158.2124199
 158.62860694 159.04479399 159.46098104 159.87716809 160.29335513
 160.70954218 161.12572923 161.54191628 161.95810333 162.37429037
 162.79047742 163.20666447 163.62285152 164.03903856 164.45522561
 164.87141266 165.28759971 165.70378675 166.1199738  166.53616085
 166.9523479  167.36853494 167.78472199 168.20090904 168.61709609
 169.03328313 169.44947018 169.86565723 170.28184428 170.69803132
 171.11421837 171.53040542 171.94659247 172.36277951 172.77896656
 173.19515361 173.61134066 174.0275277  174.44371475 174.8599018
 175.27608885 175.6922759  176.10846294 176.52464999 176.94083704
 177.35702409 177.77321113 178.18939818 178.60558523 179.02177228
 179.43795932 179.85414637 180.27033342 180.68652047 181.10270751
 181.51889456 181.93508161 182.35126866 182.7674557  183.18364275
 183.5998298  184.01601685 184.43220389 184.84839094 185.26457799
 185.68076504 186.09695208 186.51313913 186.92932618 187.34551323
 187.76170027 188.17788732 188.59407437 189.01026142 189.42644847
 189.84263551 190.25882256 190.67500961 191.09119666 191.5073837
 191.92357075 192.3397578  192.75594485 193.17213189 193.58831894
 194.00450599 194.42069304 194.83688008 195.25306713 195.66925418
 196.08544123 196.50162827 196.91781532 197.33400237 197.75018942
 198.16637646 198.58256351 198.99875056 199.41493761 199.83112465
 200.2473117  200.66349875 201.0796858  201.49587285 201.91205989
 202.32824694 202.74443399 203.16062104 203.57680808 203.99299513
 204.40918218 204.82536923 205.24155627 205.65774332 206.07393037
 206.49011742 206.90630446 207.32249151 207.73867856 208.15486561
 208.57105265 208.9872397  209.40342675 209.8196138  210.23580084]
In [83]:
adult_heights_cdf = adult_heights.value_counts(normalize=True).sort_index().cumsum()
adult_heights_cdf
Out[83]:
htm3
61.0     0.000007
64.0     0.000010
66.0     0.000012
74.0     0.000017
76.0     0.000020
           ...   
221.0    0.999976
225.0    0.999978
226.0    0.999983
229.0    0.999998
236.0    1.000000
Name: proportion, Length: 103, dtype: float64
In [84]:
norm_dist = norm.cdf(x_ticks, loc=mean, scale=std)
norm_dist_cdf = pd.Series(norm_dist, index=x_ticks)
norm_dist_cdf
Out[84]:
127.414578    0.000032
127.830765    0.000038
128.246952    0.000044
128.663140    0.000052
129.079327    0.000062
                ...   
208.571053    0.999938
208.987240    0.999948
209.403427    0.999956
209.819614    0.999962
210.235801    0.999968
Length: 200, dtype: float64
In [85]:
fig, ax = plt.subplots()
ax.plot(norm_dist_cdf.index, norm_dist_cdf)
ax.plot(adult_heights_cdf.index, adult_heights_cdf)
plt.show()
No description has been provided for this image

It looks like normal distribution fits, but let's try log-normal distribution anyway.

In [86]:
mean_log, std_log = adult_heights_log.mean(), adult_heights_log.std()
low_log, high_log = mean_log - 4 * std_log, mean_log + 4 * std_log
print("Mean:", mean_log)
print("std:", std_log)
print("-4 std:", low_log)
print("+4 std:", high_log)
x_ticks_log = np.linspace(low_log, high_log, 200)
print("X ticks", x_ticks_log)
Mean: 2.226620551788273
std: 0.026655992313131358
-4 std: 2.1199965825357476
+4 std: 2.333244521040798
X ticks [2.11999658 2.12106818 2.12213978 2.12321138 2.12428297 2.12535457
 2.12642617 2.12749777 2.12856936 2.12964096 2.13071256 2.13178416
 2.13285575 2.13392735 2.13499895 2.13607055 2.13714215 2.13821374
 2.13928534 2.14035694 2.14142854 2.14250013 2.14357173 2.14464333
 2.14571493 2.14678652 2.14785812 2.14892972 2.15000132 2.15107292
 2.15214451 2.15321611 2.15428771 2.15535931 2.1564309  2.1575025
 2.1585741  2.1596457  2.16071729 2.16178889 2.16286049 2.16393209
 2.16500369 2.16607528 2.16714688 2.16821848 2.16929008 2.17036167
 2.17143327 2.17250487 2.17357647 2.17464806 2.17571966 2.17679126
 2.17786286 2.17893445 2.18000605 2.18107765 2.18214925 2.18322085
 2.18429244 2.18536404 2.18643564 2.18750724 2.18857883 2.18965043
 2.19072203 2.19179363 2.19286522 2.19393682 2.19500842 2.19608002
 2.19715162 2.19822321 2.19929481 2.20036641 2.20143801 2.2025096
 2.2035812  2.2046528  2.2057244  2.20679599 2.20786759 2.20893919
 2.21001079 2.21108239 2.21215398 2.21322558 2.21429718 2.21536878
 2.21644037 2.21751197 2.21858357 2.21965517 2.22072676 2.22179836
 2.22286996 2.22394156 2.22501316 2.22608475 2.22715635 2.22822795
 2.22929955 2.23037114 2.23144274 2.23251434 2.23358594 2.23465753
 2.23572913 2.23680073 2.23787233 2.23894393 2.24001552 2.24108712
 2.24215872 2.24323032 2.24430191 2.24537351 2.24644511 2.24751671
 2.2485883  2.2496599  2.2507315  2.2518031  2.25287469 2.25394629
 2.25501789 2.25608949 2.25716109 2.25823268 2.25930428 2.26037588
 2.26144748 2.26251907 2.26359067 2.26466227 2.26573387 2.26680546
 2.26787706 2.26894866 2.27002026 2.27109186 2.27216345 2.27323505
 2.27430665 2.27537825 2.27644984 2.27752144 2.27859304 2.27966464
 2.28073623 2.28180783 2.28287943 2.28395103 2.28502263 2.28609422
 2.28716582 2.28823742 2.28930902 2.29038061 2.29145221 2.29252381
 2.29359541 2.294667   2.2957386  2.2968102  2.2978818  2.2989534
 2.30002499 2.30109659 2.30216819 2.30323979 2.30431138 2.30538298
 2.30645458 2.30752618 2.30859777 2.30966937 2.31074097 2.31181257
 2.31288417 2.31395576 2.31502736 2.31609896 2.31717056 2.31824215
 2.31931375 2.32038535 2.32145695 2.32252854 2.32360014 2.32467174
 2.32574334 2.32681493 2.32788653 2.32895813 2.33002973 2.33110133
 2.33217292 2.33324452]
In [87]:
adult_heights_log_cdf = (
    adult_heights_log.value_counts(normalize=True).sort_index().cumsum()
)
adult_heights_log_cdf
Out[87]:
htm3
1.785330    0.000007
1.806180    0.000010
1.819544    0.000012
1.869232    0.000017
1.880814    0.000020
              ...   
2.344392    0.999976
2.352183    0.999978
2.354108    0.999983
2.359835    0.999998
2.372912    1.000000
Name: proportion, Length: 103, dtype: float64
In [88]:
norm_dist_log = norm.cdf(x_ticks_log, loc=mean_log, scale=std_log)
norm_dist_log_cdf = pd.Series(norm_dist_log, index=x_ticks_log)
norm_dist_log_cdf
Out[88]:
2.119997    0.000032
2.121068    0.000038
2.122140    0.000044
2.123211    0.000052
2.124283    0.000062
              ...   
2.328958    0.999938
2.330030    0.999948
2.331101    0.999956
2.332173    0.999962
2.333245    0.999968
Length: 200, dtype: float64
In [89]:
fig, ax = plt.subplots(1, 2, figsize=(12, 4))
ax[0].plot(norm_dist_log_cdf.index, norm_dist_log_cdf)
ax[0].plot(adult_heights_log_cdf.index, adult_heights_log_cdf)
ax[1].plot(norm_dist_cdf.index, norm_dist_cdf)
ax[1].plot(adult_heights_cdf.index, adult_heights_cdf)
ax[0].set_title("log10")
ax[1].set_title("normal")
ax[0].set_xlim(np.log10(120))
ax[1].set_xlim(120)
plt.show()
No description has been provided for this image

I couldn't find any noticable difference.

Glossary from the resource

  • binomial distribution: A theoretical distribution often used to model the number of successes or hits in a sequence of hits and misses.
  • Poisson distribution: A theoretical distribution often used to model the number of events that occur in an interval of time.
  • exponential distribution: A theoretical distribution often used to model the time between events.
  • normal distribution: A theoretical distribution often used to model data that follow a symmetric, bell-like curve.
  • lognormal distribution: A theoretical distribution often used to model data that follow a bell-like curve that is skewed to the right.
>