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.