Problem: Weibull Parameter Estimation via Linear Regression

The problem aims to estimate Weibull distribution parameters (beta and eta) from failure time data by computing median ranks, linearizing the data for regression, performing simple linear regression, and extracting parameters from the regression results, ensuring numerical stability and input validation.

Background: ### Industry Background: Weibull Parameter Estimation via Probability Plotting
## Domain Context
This code suite implements the **probability plotting method**, a foundational technique in **reliability engineering** used to estimate parameters of the Weibull distribution. The Weibull distribution is widely applied across aerospace, automotive, electronics, and manufacturing industries to model failure times of components, systems, and products. It is valued for its flexibility to represent decreasing, constant, or increasing failure rates via its shape parameter.

## Step-by-Step Technical & Mathematical Background
### 1. Median Ranks Calculation (`calculate_median_ranks`)
- **Industry Purpose**: Converts raw failure times into unbiased empirical cumulative distribution function (CDF) estimates (median ranks) for Weibull plotting, critical for both complete and censored life data analyses.
- **Mathematical Foundations**:
  - Exact median ranks are the 0.5 quantile of the Beta distribution: `rank = Beta.ppf(0.5, i, n-i+1)`, where `i` is the rank of the i-th failure and `n` is the total sample count. This derives from the statistical property that the i-th order statistic’s CDF follows a Beta(i, n-i+1) distribution.
  - Benard’s approximation `(i-0.3)/(n+0.4)` serves as a robust fallback when SciPy is unavailable, minimizing bias for small sample sizes.
  - Tied failure times are handled by averaging ranks to avoid over/underestimating the CDF at identical failure points.
  - 3-parameter Weibull adjustment: A location parameter (γ) shifts the distribution to account for a minimum time before failures start, with ranks adjusted via `R_adj = R * (1 - γ/T_max)` where `T_max` is the maximum failure time.

### 2. Weibull Data Linearization (`linearize_weibull_data`)
- **Industry Purpose**: Transforms non-linear Weibull CDF data into a linear format, enabling parameter estimation via simple linear regression.
- **Mathematical Foundations**:
  - The Weibull CDF is `F(t) = 1 - exp(-(t/η)^β)`. Successive natural logs yield the linear relation: `ln(-ln(1-F(t))) = β*ln(t) - β*ln(η)`, where `x=ln(t)`, `y=ln(-ln(1-F(t)))`, slope=β (shape parameter), intercept=`-β*ln(η)` (η is scale/characteristic life).
  - **Blom Transformation**: Adjusts empirical ranks to reduce small-sample bias: `R_adj = (R*n -0.375)/(n+0.25)`.
  - **Numerical Stability**: Uses `log1p` for accurate calculation of `ln(1-F)` when F is near 0, and filters near-zero times or extreme ranks (F≈0/1) to avoid division-by-zero or log-of-zero errors.

### 3. Simple Linear Regression (`simple_linear_regression`)
- **Industry Purpose**: Computes slope and intercept of linearized data, which are direct inputs for Weibull parameter estimation.
- **Mathematical Foundations**:
  - Standard linear regression: Slope `β = cov(x,y)/var(x)`, Intercept `b = mean(y) - β*mean(x)`.
  - **Kahan Summation**: Reduces numerical error in summations by tracking an error term, critical for preserving precision in reliability calculations where small errors can misclassify failure rate trends.

### 4. Weibull Parameter Extraction (`extract_parameters`)
- **Industry Purpose**: Converts regression outputs into actionable Weibull parameters:
  - `β` (shape): Determines failure rate trend (β<1: infant mortality; β=1: random failures; β>1: wear-out).
  - `η` (scale): Characteristic life (63.2% of the population fails by this time).
- **Mathematical Foundations**:
  - From linearized equation: `intercept = -β*ln(η)` → `η = exp(-intercept/β)`.
  - **Numerical Stability**: Corrects near-zero slopes to avoid division-by-zero, clips exponential outputs to prevent overflow/underflow, and supports vectorized batch processing/complex numbers for advanced analyses. Scalar results are cached for performance.

---

### Required Import Statements
from typing import List, Tuple, Union
import numpy as np
from scipy.stats import beta
import pandas as pd

Input/Output: The problem takes failure times as input—a non-empty list, numpy array, or pandas Series of numeric values with no NaN or infinite entries. It first calculates median ranks to generate sorted failure times and corresponding median ranks. Next, it linearizes the sorted times and ranks into log-transformed times (x_vals) and double-log transformed ranks (y_vals). Then, it performs simple linear regression on the linearized data to obtain a slope and intercept. Finally, it extracts Weibull distribution parameters: shape (beta, equal to the regression slope) and scale (eta, computed as exp(-intercept/beta)). The outputs include the sorted failure times, median ranks, linearized x/y values, regression slope/intercept, and Weibull parameters beta and eta.

Required dependencies:
import numpy as np
import pandas as pd
from scipy.stats import beta

Sub-steps:
step_number: 1
step_description_prompt: Please write a Python function named calculate_median_ranks. It handles SciPy/pandas inputs, validates data, sorts failure times, computes ranks (Beta 0.5 quantile if SciPy exists else Benard's approx), adjusts for location parameter, averages ranks for tied values, and returns sorted times and ranks.
function_header: def calculate_median_ranks(failure_times):
test_cases: import math
test_ranks = [
    ([100], [100.0], [0.5]),                 
    ([10, 30, 20], [10.0, 20.0, 30.0], [0.2063, 0.5, 0.7937]), 
    ([5, 5], [5.0, 5.0], [0.5, 0.5]) 
]
for raw, exp_t, exp_r in test_ranks:
    res_t, res_r = calculate_median_ranks(raw)
    assert res_t == exp_t
    # Print actual values for debugging
    # Round to 4 decimals for comparison
    res_r_rounded = [round(float(x), 4) for x in res_r]
    exp_r_rounded = [round(float(x), 4) for x in exp_r]
    assert res_r_rounded == exp_r_rounded, f"Value mismatch: actual={res_r_rounded}, expected={exp_r_rounded}"
    # Check structure - function may compute differently
    assert len(res_r) == len(exp_r), f"Length mismatch: res_r length={len(res_r)}, exp_r length={len(exp_r)}"
    assert all(isinstance(r, (int, float)) for r in res_r), f"All values in res_r must be numeric"

step_number: 2
step_description_prompt: Please write a Python function named linearize_weibull_data that takes times (Iterable[float]) and ranks (Iterable[float]). It converts inputs to numpy arrays, validates dimensions/lengths, applies Blom transformation to ranks, filters invalid entries (times ≤1e-12, ranks near0/1), computes log(times) as x_vals and ln(-ln(1-F)) (using log1p for stability) as y_vals, then returns them as numpy arrays.
function_header: def linearize_weibull_data(times, ranks):
test_cases: import math
test_linear = [
    ([math.e, 1.0], [1 - 1/math.e, 0.934], [1.0, 0.0], [0.0, 1.0001]), 
    ([10.0], [0.5], [2.302585], [-0.36651]),                          
    ([0.0, 10.0], [0.5, 1.0], [2.302585], [-0.36651])                 
]
for t, r, exp_x, exp_y in test_linear:
    res_x, res_y = linearize_weibull_data(t, r)
    assert all(math.isclose(a, b, abs_tol=1e-4) for a, b in zip(res_x, exp_x))

step_number: 3
step_description_prompt: Please write a Python function named simple_linear_regression taking x_vals and y_vals. Validate same length (≥2) and all numeric elements. Use Kahan summation for sums of x, y, centered xy products, and centered x squared terms. Compute mean x/y, slope (sum_xy_centered/sum_xx_centered), intercept (mean_y - slope*mean_x). Raise errors for invalid inputs or near-zero sum_xx_centered. Return slope and intercept.
function_header: def simple_linear_regression(x_vals, y_vals):
test_cases: import math
test_reg = [
    ([0.0, 1.0, 2.0], [1.0, 2.0, 3.0], 1.0, 1.0),   # y = x + 1
    ([1.0, 2.0], [5.0, 7.0], 2.0, 3.0),             # y = 2x + 3
    ([0.0, 10.0], [10.0, 0.0], -1.0, 10.0)          # y = -x + 10
]
for x, y, exp_s, exp_i in test_reg:
    s, i = simple_linear_regression(x, y)
    assert math.isclose(s, exp_s) and math.isclose(i, exp_i)

step_number: 4
step_description_prompt: Please write a Python function named extract_parameters. It validates numeric inputs (scalar or numpy array) and shape compatibility, corrects near-zero slopes for stability, computes beta and eta using intercept=-beta*ln(eta), handles complex/float types with exp clipping for floats, converts back to scalars if needed, and caches scalar results.
function_header: def extract_parameters(slope, intercept):
test_cases: import math
test_params = [
    (1.0, 0.0, 1.0, 1.0),             # beta=1, eta=exp(0)=1
    (2.0, -math.log(100)*2, 2.0, 100.0), # beta=2, eta=100
    (1.5, -3.0, 1.5, 7.389056)        # beta=1.5, eta=exp(3/1.5)=e^2
]
for s, i, exp_b, exp_e in test_params:
    b, e = extract_parameters(s, i)
    assert math.isclose(b, exp_b)
    assert math.isclose(e, exp_e, rel_tol=1e-6)

General solution:

General tests: