The millsratio package provides tools for computing and visualizing
Mills ratios across different probability distributions. It includes an
interactive dashboard for exploring how Mills ratios reveal fundamental
differences between distribution tails, particularly highlighting the
t(30) paradox where distributions appear similar centrally but diverge
substantially in the tails.
This package implements and extends concepts from John D. Cook’s blog post on Mills ratios.
The Mills ratio is defined as:
m(x) = [1 - F(x)] / f(x)
where F(x) is the CDF and f(x) is the PDF.
# Install development version from GitHub
# remotes::install_github("JohnGavin/millsratio")
install.packages("millsratio", repos = c('https://johngavin.r-universe.dev', 'https://cloud.r-project.org'))
# Load the package
library(millsratio)
For a fully reproducible environment with all dependencies:
# Clone and use the project's Nix shell
git clone https://github.com/JohnGavin/millsratio.git
cd millsratio
# Enter Nix shell with GC root (creates default.nix automatically)
chmod +x default.sh
./default.sh # First run: builds environment, subsequent runs: fast
# Now in Nix shell, R has all dependencies
R
> library(millsratio)
For integration with existing R projects:
library(rix)
rix(
r_ver = "4.5.0",
r_pkgs = c("tidyverse", "plotly", "shiny"),
git_pkgs = list(
millsratio = list(
package_source = "github",
repo_url = "JohnGavin/millsratio",
branch = "main"
)
),
ide = "code",
project_path = "."
)
# Then: nix-shell
# Launch the interactive dashboard
launch_dashboard()
The dashboard provides: - 12 interactive pages organized into Analysis, Theory, and Playground sections - Real-time visualization of Mills ratios - Comparison tools for multiple distributions - The t(30) paradox demonstration - Custom code playground for experiments
# Calculate Mills ratio for normal distribution
x <- seq(1, 5, by = 0.5)
m_normal <- mills_ratio_normal(x)
print(round(m_normal, 3))
# Calculate for t-distribution
m_t30 <- mills_ratio_t(x, df = 30)
print(round(m_t30, 3))
# Compare distributions
x_compare <- c(1, 2, 3, 4)
comparison <- compare_mills_ratios(x_compare, c("normal", "t30", "exponential"))
print(comparison)
# Generate curves for visualization
curves <- simulate_mills_curves(
x_range = c(0.5, 5),
distributions = c("normal", "t30", "exponential")
)
# Create plot
plot_mills_curves(curves, log_y = TRUE)
# Analyze the t(30) paradox
paradox_data <- analyze_t30_paradox(x_range = c(0, 5), n_points = 100)
# Show how t(30) diverges from normal in the tails
x_vals <- c(1, 2, 3, 4, 5)
ratio <- mills_ratio_t(x_vals, df = 30) / mills_ratio_normal(x_vals)
cat("t(30)/Normal Mills ratio divergence:\n")
for (i in seq_along(x_vals)) {
cat(sprintf("x = %d: ratio = %.3f\n", x_vals[i], ratio[i]))
}
# Visualize the paradox
plot_t30_paradox(paradox_data, focus = "mills")
mills_ratio_normal() - Mills ratio for normal distributionmills_ratio_t() - Mills ratio for Student’s t distributionmills_ratio_exp() - Mills ratio for exponential distributionmills_ratio_generic() - Generic Mills ratio for any distributioncompare_mills_ratios() - Compare Mills ratios across distributionsmills_asymptotic() - Calculate asymptotic approximationsmills_ratio_comparison() - Compute ratios between distributionsfind_crossover_point() - Find where two Mills ratios intersectsimulate_mills_curves() - Generate Mills ratio curves for plottinganalyze_tail_thickness() - Analyze tail thickness at specific pointsanalyze_t30_paradox() - Comprehensive t(30) vs normal comparisonmonte_carlo_mills() - Monte Carlo verification of Mills ratiosplot_mills_curves() - Plot Mills ratio curvesplot_tail_thickness_heatmap() - Create heatmap of tail thicknessplot_t30_paradox() - Visualize the t(30) paradoxplot_mills_comparison() - Compare two distributionslaunch_dashboard() - Launch interactive Shiny dashboard| Distribution | Mills Ratio Behavior | Tail Type |
|---|---|---|
| Normal | m(x) ~ 1/x as x→∞ | Thin |
| Student t(df) | m(x) ~ x/df as x→∞ | Fat |
| Exponential | m(x) = 1/rate (constant) | Medium |
The t(30) distribution is often considered “practically normal” for many applications. However: - Central region: Nearly indistinguishable from normal - Tail region: Substantially different Mills ratios - Implication: Risk models based on normality assumptions may severely underestimate tail probabilities
# Verify that normal has decreasing Mills ratio (thin tails)
x <- seq(1, 5, by = 0.5)
m <- mills_ratio_normal(x)
cat("Normal Mills ratio decreasing:", all(diff(m) < 0), "\n")
# Verify that t(3) has increasing Mills ratio (fat tails)
m_t <- mills_ratio_t(x, df = 3)
cat("t(3) Mills ratio increasing:", all(diff(m_t) > 0), "\n")
# Verify exponential has constant Mills ratio
m_exp <- mills_ratio_exp(x)
cat("Exponential Mills ratio constant:", all(abs(diff(m_exp)) < 1e-10), "\n")
# Generate interactive plot
library(plotly)
curves <- simulate_mills_curves(
x_range = c(0.1, 10),
distributions = c("normal", "t3", "t10", "t30"),
log_scale = TRUE
)
p <- plot_mills_curves(curves, log_y = TRUE, interactive = TRUE)
p # Opens interactive plotly visualization
# Verify Mills ratio empirically
set.seed(123)
mc_result <- monte_carlo_mills(
n_sim = 10000,
x_val = 2,
distribution = "normal"
)
cat("Empirical Mills ratio:", round(mc_result$empirical_mills, 4), "\n")
cat("True Mills ratio:", round(mc_result$true_mills, 4), "\n")
cat("Relative error:", round(mc_result$relative_error, 4), "\n")
Run the test suite:
devtools::test()
Check package:
devtools::check()
This package uses targets to ensure all examples are tested:
# Run the pipeline to test all examples
targets::tar_make()
# View the pipeline
targets::tar_visnetwork()
MIT License
John Gavin john.b.gavin@gmail.com
Implementation support: Claude Assistant
If you use this package in your research, please cite:
@software{millsratio2026,
author = {Gavin, John},
title = {millsratio: Interactive Analysis of Mills Ratios and Tail Thickness},
year = {2026},
url = {https://github.com/yourusername/millsratio}
}