ANALYSIS TOOL BOX

Simulations

CreateMetalogDistribution

Fit a flexible Metalog distribution to empirical data for Monte Carlo simulation.

monte-carlodistributionuncertaintyriskmetalog

Introduction

Real-world data rarely matches the shape of a Normal or Lognormal distribution — project costs skew right, response times have fat tails, some outcomes are bimodal — and forcing a mismatched distribution onto a Monte Carlo model quietly corrupts every result downstream. CreateMetalogDistribution sidesteps that by fitting a Metalog distribution, an "any-shape" family that can represent virtually any continuous data — bounded, semi-bounded, or unbounded — directly from your empirical data, then returns as many random samples as you need for a simulation.

Teaching Note

Metalog distributions are essential for:

  • Epidemiology: Modeling disease incubation periods with heavy tails or irregular shapes.
  • Healthcare: Simulating patient hospital length of stay or recovery times.
  • Intelligence Analysis: Modeling uncertainty in target location, response times, or signal strength.
  • Finance: Estimating Value at Risk for non-normal asset returns or market volatility.
  • Engineering: Reliability analysis for components with complex or non-standard failure distributions.
  • Environmental Science: Assessing risks of extreme weather events like flood levels or peak winds.
  • Project Management: Estimating task durations in complex R&D or software development.
  • Supply Chain: Quantifying lead time variability and demand spikes in global logistics.

Parameters

ParameterTypeDefaultDescription
dataframerequiredpd.DataFrameThe pandas DataFrame containing the variable of interest.
variablerequiredstrThe name of the column in the DataFrame to be used for fitting the distribution.
lower_boundfloatNoneThe lower logical bound of the distribution (e.g., 0 for length of stay). If provided along with upper_bound, a bounded metalog is created. Defaults to None.
upper_boundfloatNoneThe upper logical bound of the distribution. If provided along with lower_bound, a bounded metalog is created. Defaults to None.
learning_ratefloat0.01The step length for the metalog fit algorithm. Decreasing this may improve fit accuracy for complex data. Defaults to 0.01.
term_maximumint9The maximum number of terms allowed in the metalog expansion (up to 9). Higher terms provide more shape flexibility but may lead to overfitting. Defaults to 9.
term_minimumint2The minimum number of terms allowed. Defaults to 2.
term_for_random_sampleintNoneThe specific number of terms to use when generating random samples. If None, the term_limit from the fitted metalog is used. Defaults to None.
number_of_samplesint10000The number of stochastic samples to generate from the fitted distribution. Defaults to 10000.
show_summaryboolTrueWhether to print a text summary of the fitted metalog coefficients and validation. Defaults to True.
return_formatstr'dataframe'The format of the returned samples: 'dataframe' (as a pd.DataFrame) or 'array' (as a np.ndarray). Defaults to 'dataframe'.
plot_metalog_distributionboolTrueWhether to display a histogram of the generated samples with optional mean/median indicators. Defaults to True.
fill_colorstr'#999999'The hex color code for the histogram bars. Defaults to '#999999'.
fill_transparencyfloat0.6The transparency level (0-1) for the histogram plot. Defaults to 0.6.
figure_sizetuple(8, 6)The size of the plot figure in inches (width, height). Defaults to (8, 6).
show_meanboolTrueWhether to display the mean value as a vertical dashed line on the plot. Defaults to True.
show_medianboolTrueWhether to display the median value as a vertical dotted line on the plot. Defaults to True.
title_for_plotstr'Metalog Distribution'The main title for the distribution plot. Defaults to 'Metalog Distribution'.
subtitle_for_plotstr'Showing the metalog distribution of the variable'The descriptive subtitle for the plot.
caption_for_plotstrNoneOptional caption text displayed at the bottom of the plot. Defaults to None.
data_source_for_plotstrNoneOptional data source identification text. Defaults to None.
show_y_axisboolFalseWhether to display the y-axis (frequency/density scale) on the plot. Defaults to False.
title_y_indentfloat1.1Vertical position for the title text. Defaults to 1.1.
subtitle_y_indentfloat1.05Vertical position for the subtitle text. Defaults to 1.05.
caption_y_indentfloat-0.15Vertical position for the caption text. Defaults to -0.15.

Returns

The generated samples from the Metalog distribution, in the format specified by return_format — a pandas DataFrame or a NumPy array.

Example

python
from analysistoolbox.simulations import CreateMetalogDistribution
import pandas as pd

# Historical project cost data (in $K)
cost_history = pd.DataFrame({
    'cost': [82, 95, 110, 125, 140, 155, 170, 195, 230, 280]
})

samples = CreateMetalogDistribution(
    dataframe=cost_history,
    variable='cost',
    lower_bound=0,     # costs can't be negative
    number_of_samples=10_000,
)

# Use samples in a Monte Carlo model
p10 = samples['cost'].quantile(0.10)
p50 = samples['cost'].quantile(0.50)
p90 = samples['cost'].quantile(0.90)
print(f"P10: ${p10:.0f}K  P50: ${p50:.0f}K  P90: ${p90:.0f}K")