The destination is a maintained R analysis. TemporalHazard(Ehrlinger 2026) can read the structure of a historical PROC HAZARD job, translate the parts it recognizes, and import a legacy OUTHAZ= artifact for a bounded comparison. Once we have checked the mapping, the R code, fitted object, diagnostics, and predictions become the source we keep. A SAS license or live SAS job is not part of the new workflow.
Think of the old program as a set of labeled boxes. TIME, EVENT, PARMS, and the phase statements tell us what each box contained. The migration job is to unpack those boxes once into named R arguments, inspect the result, and then work in the current API.
The example below is synthetic and contains no patient data.
25.2 Translate a short historical input
hzr_translate_sas() reads a file path, so we write the small text block to a session temporary file. No legacy software is called. The parser reads the statements and returns an hzr_sas_job containing the R expressions it would place in a translated Quarto document.
Coverage reports what the parser recognized, not whether a model has been validated. For this bounded input all six tokens are mapped and no construct is left untranslated. Now inspect the emitted fit call itself.
fit <- hazard(data = AVC, time = INT_DEAD, status = .hzr_status, fit = TRUE, dist = "multiphase",
phases = list(hzr_phase("cdf", nu = 1)), theta = c(log(0.2), log(1), 1, 0), weights = ifelse(DEAD >
0, DEAD, 1), control = list(condition = 14, conserve = TRUE))
The generated call carries TIME to time, derives a 0/1 status from the historical event count, preserves event counts as weights, turns the early parameters into hzr_phase("cdf", ...), and carries CONSERVE and CONDITION=14 into control. The returned job also contains a data guard and status expression. Translation does not recreate a SAS DATA step, so you must bind the reviewed R data frame before running the generated fit.
WarningRead the generated code before you run it
The translator deliberately refuses some constructs, including a SELECTION statement and censoring combinations that cannot be expressed by one current R argument. An unresolved external fit or prediction grid can also stop the generated document. Never replace that stop with guessed code. Rebuild the unsupported step directly in R and document the choice.
25.3 Map the old names once
hzr_argument_mapping() exposes the package’s formal crosswalk. Keep planned rows visible during migration: the status column distinguishes a maintained mapping from a broader legacy concept that still needs review.
sas_statement legacy_input r_parameter
8 TIME t time
9 EVENT status status
10 PARMS theta0 theta
11 DIST dist dist
14 G1 THALF / RHO (early) hzr_phase(t_half=)
15 G1 NU (early) hzr_phase(nu=)
16 G1 M (early) hzr_phase(m=)
17 G1 DELTA (early) (absorbed by decompos)
18 G2 G2 constant phase hzr_phase('constant')
transform_rule
8 pass through
9 coerce to numeric
10 map PARMS/INITIAL to theta
11 map DIST= to dist
14 maps directly to hzr_phase(t_half=) starting value
15 maps directly to hzr_phase(nu=) starting value
16 maps directly to hzr_phase(m=) starting value
17 time transform B(t) = (exp(delta*t)-1)/delta absorbed into decompos shape
18 hzr_phase('constant') with no shape parameters
implementation_status
8 implemented
9 implemented
10 planned
11 implemented
14 implemented
15 implemented
16 implemented
17 implemented
18 implemented
The general PARMS row remains marked as planned even though the translator can parse the small early-phase subset above. Treat that as a limit on the claim: the successful example is evidence for those statements, not blanket support for every historical parameter combination. For a new analysis, write the reviewed hazard() and hzr_phase() call directly rather than carrying legacy names forward.
25.4 Import an old OUTHAZ= artifact for a bounded check
The package ships a public synthetic fixture with the row layout of an OUTHAZ= dataset. hzr_read_outhaz() reads that artifact into estimates, free/fixed status, a covariance block for free parameters, and model-structure flags. The same function can read a real .sas7bdat file when haven is installed, but the fixture keeps this recipe public and reproducible. The reader is experimental, and its return shape may change. Its _STATUS_ coding is asserted only for this synthetic fixture. A historical file that uses a different convention may populate the estimates while returning an empty covariance matrix.
For this fixture, the output inventories parameter names, stored estimates, fixture-specific status codes, covariance dimensions, and imported phase flags. It does not validate the coding conventions of another OUTHAZ= artifact. Nor can it recover the original analysis rows, the original log-likelihood, deletion tallies, event-time coding, or every data transformation. It is not a refit and cannot establish that a new model is correct merely because selected numbers match.
Use the artifact once as a migration checksum. Then refit from the reviewed R analysis data, run the diagnostics in Check a hazard model, save the R model object and code under version control, and retire the legacy artifact from the active workflow.
25.5 What the maintained record should contain
Before calling the migration complete, keep these pieces together:
the reviewed R data-building code and a public or synthetic test example;
the explicit hazard() model, phase definitions, starting values, and time units;
any translator warnings and every manual resolution;
the comparison quantities chosen in advance, with tolerances;
the fitted R object, diagnostics, predictions, and session/package versions.
That record answers the question the old file cannot: not only whether a few stored estimates matched, but exactly how the maintained analysis was built and how we will rerun it next time.