strata.data(..., method = "exact") runs Dynamic Programming on the data
themselves. It returns the partition with the smallest empirical Neyman cost
over all partitions into h contiguous strata with at least two units each,
never splitting tied values. It is exact for data with up to 20,000 distinct
cut points (about 15 to 20 seconds for 14,000 units at h = 6); beyond that
it searches 20,000 cut points spread evenly in rank, which can be changed
with options(stratifyR.exact_max_cand = ). The result records this in
exact_full. The "dp" solver remains the default: it is globally optimal
on a fine grid for the fitted distribution.method = "exact" places each boundary exactly on a data value. The
allocation step rebuilt it as (b / maxval) * maxval, which on some
platforms (for example Apple silicon) can round one bit below b and move
the boundary unit into the next stratum, so the reported cost could be a
little above the true optimum (0.25% for a Cauchy sample at h = 3). The
allocation step now uses a cut point halfway to the next data value.data.alloc() treated each stratum as closed at both ends, so a value equal
to a boundary was counted in two strata. Every unit is now assigned to
exactly one stratum, (b[h-1], b[h]].WhShTot is now reported to six decimals instead of three.n and never exceed N_h. Rounding each n_h on its
own could give a total of n + 1 or n - 1 (for example 63 + 103 + 135 =
301 for the anaemia Iron data at h = 3, n = 300).h boundaries in OSB, the last being the top of
the range.method = "cobyla" and method = "global" no longer force every stratum to
be at least (range)/(3h) wide. That rule excluded the narrow strata that
skewed and heavy-tailed populations need, so on Pareto, log-normal and Cauchy
data the two solvers returned designs with up to several times the optimal
Neyman cost. Boundaries now only need to be range/1000 apart, as in the
problem formulation.method = "global" no longer accepts a result whose objective is the
infeasibility penalty (a design with empty strata). If the global phase finds
no feasible point, it falls back to multi-start COBYLA.method = "global" no longer reports converged = FALSE when the BOBYQA
refinement confirms the DIRECT-L point without improving it.N argument to strata.data(), which has none. Previously this made the
H curve drop points and the generated code fail when a population size was set.method = "exact".compare_designs(), cost-aware allocation, plots and
large frames, with a worked example on household expenditure.DESCRIPTION now describes the four solvers correctly (GLOBAL refines with
BOBYQA, not COBYLA).fit_distribution() is documented as internal; it was marked for export
but never exported.cobyla.optim.R updated to match the 2.0-2 behaviour.strata.data() corrected: "dp" is the default, and the
"exact" method is documented.strata.data() and strata.distr() gain a method argument supporting
three optimisation strategies:
"dp" (default) — the original exhaustive Dynamic Programming grid search,
guaranteed globally optimal on the discrete grid."cobyla" — multi-start gradient-free local optimiser (COBYLA) via
nloptr; faster than DP for large h, suitable for exploratory work."global" — two-phase global optimiser combining DIRECT-L (deterministic
space-filling search) followed by COBYLA local refinement; closest to DP
accuracy among the two new methods.n_starts, max_iter, tol, and verbose in both functions
to control COBYLA/GLOBAL behaviour.DESCRIPTION updated: nloptr added to Imports.compare_designs() function and print method. For the same total
sample size it compares simple random sampling, proportional allocation and
Neyman allocation, with design effects, the equivalent SRS sample size and
the percentage cost saving.plot() method for "strata" objects with three types: "2d"
(histogram or density with boundaries and the fitted density formula),
"3d" and "interactive" (both need plotly).stratifyRApp() launches a self-contained shiny application:
upload a CSV or Excel file or pick a built-in dataset, compute OSB and OSS
with any solver, choose the number of strata, compare designs, explore the
boundaries, download results and generate the equivalent R code.cost = TRUE with per-stratum costs ch).summary().distr.root() and data.root()
were using the unregularized upper incomplete gamma function UGamma(s, z)
(via zipfR) where the regularized form Q(s, z) was required by the
mathematical formulation. Fixed by replacing all calls with
pgamma(z, shape = s, lower.tail = FALSE), which correctly implements
Q(s, z) = Γ(s, z) / Γ(s) as defined in Abramowitz & Stegun (1972),
Chapter 6, equation 6.5.2. Results for Weibull and Gamma populations are now
consistent with the published theory..refit_real_scale(): simultaneous MLE of all three
triangular parameters (min, max, mode) produced degenerate estimates
(min ≈ max ≈ mode). Fixed by fixing min and max to sample bounds and
estimating only the mode via MLE, matching the approach already used in
get.dist().get.dist(): triangular distribution now only selected over a tail model
(Gamma, Weibull, Log-normal, Exponential) when its AIC advantage exceeds 10
units, preventing spurious triangular wins on mildly right-skewed data.Q(r, y) (regularized) vs Γ(r, y) (unregularized)
in the Weibull and Gamma sections, with explicit R implementation note.mean/sd parameters).fix.arg
approach.NeedsCompilation field, removed stray
\url{} in Introduction.strata.data() / strata.distr() call signatures from v1.0-5 work
without modification (method defaults to "dp")."strata" S3 class and all slot names are unchanged.Initial CRAN-submission-ready version. Implemented DP solver, 10-family
distribution fitting via AIC, Neyman allocation with cost support, and
coloured summary.strata() output.