Skip to contents

Combine unseen ensemble point predictions with reconstructed training-row OOF predictions, add signed residual offsets, select one candidate per sample and response by cell R2, and write delivery/feature products. This final selector is distinct from predictModel()'s response-level RMSE-or-R2 selector.

Usage

aggregateResponses(
  .path.summary,
  .path.export,
  .path.datasets,
  key,
  responses,
  datasets,
  boundsMin = -Inf,
  boundsMax = Inf,
  .path.iter = NULL
)

Arguments

.path.summary

Existing directory containing the four summary inputs described in Input tables.

.path.export

Existing writable output directory. It is not created by this helper. Yo.csv, X.csv, and Xo.csv are assigned here.

.path.datasets

Directory containing assembled dataset.csv inputs used only for feature exports.

key

A non-empty character key name used when extracting feature tables. Response aggregation itself is hard-coded to SampleID; setting another key does not replace that requirement.

responses

Non-empty character vector excluded, with set, from feature columns in X.csv and Xo.csv.

datasets

Non-empty character vector of assembled dataset basenames. Feature extraction uses the last declared file that actually exists.

boundsMin, boundsMax

Numeric response bounds used to project every point-plus-offset value. Their order and finiteness are not validated.

.path.iter

NULL, or an iteration directory. When non-null, its parent path for Y.csv is created recursively and the training-row median-offset table is written or merged.

Value

Invisibly, NULL. Results are Yo.csv, optional Y.csv, and the feature files written under the rules above.

Input tables

The helper stops unless response_long.csv, metrics.csv, and prediction_quantiles.csv exist. It then separately requires residuals_oof.csv.

  • response_long.csv must provide SampleID, value, method, dataset, and response. Only rows with method == "ensemble.RMSE" are retained and marked as unseen rows.

  • residuals_oof.csv must provide SampleID, y_hat_oof, dataset, and response. Every row is converted to a training candidate with value = y_hat_oof and method ensemble.RMSE.

  • metrics.csv must provide method, response, dataset, and r2. Only ensemble.RMSE metric rows enter.

  • prediction_quantiles.csv must provide response, dataset, q_50, q_90, and q_95.

Extra columns can propagate through joins. Missing, duplicate, or incompatible keys retain data.table join behavior; no uniqueness assertion is made.

Offset construction and final selector

Metric and offset rows are joined to every unseen or training candidate by response and dataset. Let \(i\) index SampleID, \(r\) a response, \(d\) a joined candidate dataset, and \(p\in\{0.50,0.90,0.95\}\) an offset probability. Let \(\hat y_{ird}\) be a candidate point value and \(q_{p,rd}\) its signed OOF-residual offset, all in response units. Let \(a\) and \(b\) be the bounds supplied by boundsMin and boundsMax, respectively. When \(a\le b\), define interval projection by \(\Pi_{[a,b]}(z)=\min\{b,\max\{a,z\}\}\) for real \(z\). The helper computes $$Q_{p,ird}=\Pi_{[a,b]}\{\hat y_{ird}+q_{p,rd}\}.$$ These are projected point-plus-offset estimates, not calibrated predictive quantiles or intervals. Bound order and finiteness are not checked; with invalid bounds the nested minimum/maximum is still executed but is not an interval projection.

For each (SampleID, response), all RMSE-ensemble candidates are ordered by descending joined cell r2; the first row is selected and supplies one dataset for all three probabilities: $$d^*_{ir}=\operatorname*{first\,argmax}_{d}R^2_{rd}.$$ The score is cell-level even though selection is grouped by sample. Ties follow joined-row order. Finite scores precede missing scores; when all joined scores in a group are missing, the first joined row is still selected with missing r2. Missing offsets propagate missing delivery values because this helper does not remove them. This selector never considers ensemble.R2 rows and is not the response-level metric selector used by predictModel(). Therefore modelPipeline(ensemble = ) does not change the final candidate family assembled here.

Delivery and iteration products

The selected table is expanded to long probabilities p = 0.50, 0.90, and 0.95. It retains available identifier columns from SampleID, HoleID, From, and To, followed by response, p, value, r2, and dataset. Response values and offsets share response units; r2 and p are dimensionless.

Unseen rows are written to Yo.csv. If that file exists, every old row whose response occurs in the current unseen result is removed, current rows are appended, and other responses are preserved. Replacement is by response, not dataset.

When .path.iter is non-null, training rows at p = 0.50 are cast to Y.csv with one response column per SampleID. On merge, new response columns replace same-named old columns and old non-overlapping response columns remain. Thus Y.csv contains median signed-offset estimates; p = 0.50 is not necessarily the unadjusted point value.

Feature products, conditions, and return

Among declared assembled files that exist, the last one is read. Every column except key, the declared responses, and set is treated as a feature. Rows with set == "train" write X.csv; rows with set == "predict" write Xo.csv. If no assembled file exists, a warning is emitted and neither feature file is written. If no feature column remains, a warning is emitted but key-only files are still written.

Assigned CSVs are overwritten and writes are not transactional. Required file, column, join, cast, and directory errors propagate after any earlier writes. Valid progress is sent through ssel's debug channel, which emits only when global verbose is enabled and quiet is disabled. The helper uses no random numbers.

References

Parikh, N., and Boyd, S. (2014). Proximal Algorithms. Foundations and Trends in Optimization, 1(3), 127–239. doi:10.1561/2400000003 .

See also

predictModel() for cell evaluation and the first selector, auditQuantiles() for signed residual offsets, and chainPipeline() for the consumer of optional Y.csv.

Examples

root <- tempfile("ssel-aggregate-")
summary_dir <- file.path(root, "summary")
export_dir <- file.path(root, "export")
datasets_dir <- file.path(root, "datasets")
iter_dir <- file.path(root, "iter")
for (path in c(summary_dir, export_dir, datasets_dir)) {
  dir.create(path, recursive = TRUE)
}

data.table::fwrite(
  data.table::data.table(
    SampleID = rep(c("P1", "P2"), each = 2),
    value = c(10, 20, 11, 21),
    method = "ensemble.RMSE",
    dataset = rep(c("d1", "d2"), 2),
    response = "target"
  ),
  file.path(summary_dir, "response_long.csv")
)
data.table::fwrite(
  data.table::data.table(
    method = "ensemble.RMSE", response = "target",
    dataset = c("d1", "d2"), r2 = c(0.5, 0.9)
  ),
  file.path(summary_dir, "metrics.csv")
)
data.table::fwrite(
  data.table::data.table(
    response = "target", dataset = c("d1", "d2"),
    q_50 = c(0, 1), q_90 = c(1, 2), q_95 = c(2, 3)
  ),
  file.path(summary_dir, "prediction_quantiles.csv")
)
data.table::fwrite(
  data.table::data.table(
    response = "target", dataset = c("d1", "d2"),
    rowIndex = 1L, SampleID = "S1",
    y_obs = c(8, 18), y_hat_oof = c(9, 19), residual = -1
  ),
  file.path(summary_dir, "residuals_oof.csv")
)

for (dataset in c("d1", "d2")) {
  data.table::fwrite(
    data.table::data.table(
      SampleID = c("S1", "P1", "P2"),
      set = c("train", "predict", "predict"),
      feature = 1:3
    ),
    file.path(datasets_dir, paste0(dataset, ".csv"))
  )
}

aggregateResponses(
  .path.summary = summary_dir,
  .path.export = export_dir,
  .path.datasets = datasets_dir,
  key = "SampleID",
  responses = "target",
  datasets = c("d1", "d2"),
  .path.iter = iter_dir
)
data.table::fread(file.path(export_dir, "Yo.csv"))
#>    SampleID response     p value    r2 dataset
#>      <char>   <char> <num> <int> <num>  <char>
#> 1:       P1   target  0.50    21   0.9      d2
#> 2:       P2   target  0.50    22   0.9      d2
#> 3:       P1   target  0.90    22   0.9      d2
#> 4:       P2   target  0.90    23   0.9      d2
#> 5:       P1   target  0.95    23   0.9      d2
#> 6:       P2   target  0.95    24   0.9      d2
data.table::fread(file.path(iter_dir, "Y.csv"))
#>    SampleID target
#>      <char>  <int>
#> 1:       S1     20
# Dataset d2 wins because its RMSE-ensemble cell has the larger R2.

unlink(root, recursive = TRUE)