internal static void ApproximateExcessDistributionParametersV4(IList <double> sortedData, out double a, out double c, out double u) { // The upper tail is defined here by an ECDF interpolating linearly from (u,0) to (x_i, i/n) for data x_1, x_2, ..., x_n all greater than u. // This is the model from which we compute the upper tail parameters, using method of moments. // This midpoint version works slightly better than the plain ECDF double MidpointMSE(IList <double> tailData, double scaleParam, double shapeParam) { int n = tailData.Count; double sum = 0; for (int i = 0; i < n - 1; i++) { double GHat = TailCDF(0.5 * (tailData[i] + tailData[i + 1]) - tailData[0], scaleParam, shapeParam); double residual = (2.0 * i + 1) / (2.0 * n) - GHat; sum += residual * residual; } return(sum / (n - 1)); } double GetScore(double uval, out double scaleParam, out double shapeParam) { var tailData = GetTailData(sortedData, uval); EstimateParamsMOM(tailData, out double scaleEst, out double shapeEst); scaleParam = scaleEst; shapeParam = shapeEst; double score = MidpointMSE(tailData, scaleEst, shapeEst); return(score); } // Try several choices of u evenly spaced over (x_0, x_n-3), and keep the best fit var uValues = Interpolation.Linspace(sortedData[0], sortedData[sortedData.Count - 5], sortedData.Count / 4); double bestU = 0; double bestA = 0; double bestC = 0; double bestScore = double.PositiveInfinity; for (int i = 0; i < uValues.Length; i++) { double score = GetScore(uValues[i], out double scaleEst, out double shapeEst); if (score < bestScore) { bestScore = score; bestU = uValues[i]; bestA = scaleEst; bestC = shapeEst; } } // --- Refine the best so far by bisection search --- double delta = uValues[1] - uValues[0]; for (int i = 0; i < 10; i++) { delta *= 0.5; double forwardU = Math.Min(bestU + delta, sortedData[sortedData.Count - 3]); // Don't go so high that we don't have data to work with double forwardScore = GetScore(forwardU, out double forwardScale, out double forwardShape); double backwardScore = GetScore(bestU - delta, out double backwardScale, out double backwardShape); if (forwardScore < bestScore) { bestScore = forwardScore; bestU = forwardU; bestA = forwardScale; bestC = forwardShape; } if (backwardScore < bestScore) { bestScore = backwardScore; bestU -= delta; bestA = backwardScale; bestC = backwardShape; } } u = bestU; a = bestA; c = bestC; }