« Subsurface Analytics

Phase 4: SANDCOUNT Binary Sand Classifier

A different question from Phase 2/3: not "what rock type is this", but simply "is this Sand or not" -- trained on nsta.lithology_interval (the Lloyd's Register SANDCOUNT product), not WellStrat. SANDCOUNT barely labels the fine fraction (Shale/Siltstone/Mudstone are almost never picked), so this card deliberately treats ANY depth not picked as Sand/Sandstone as a confirmed Not-Sand example -- see the ground-truth note below. Reports precision/recall at several probability thresholds rather than one fixed cutoff, since recall on Sand (missed potential reservoir) matters more here than a single accuracy number.

What "accuracy" means here: Trained on nsta.lithology_interval (Lloyd's Register SANDCOUNT), a different and independent label source from Phase 2/3's WellStrat-based ground truth. Absence of a SANDCOUNT Sand/Sandstone pick is treated here as a CONFIRMED Not-Sand label across the full logged interval -- not as missing/unclassified data. This only holds if SANDCOUNT's own coverage is exhaustive over what it reviewed; it is not treated as a PU-learning (positive-unlabeled) problem.

This version's training details

Trained on wells passing the GR-identity-verified, >=10% GR coverage gate AND having at least one Lloyd's Register SANDCOUNT lithology_interval labeled Sand/Sandstone. Binary target: every depth sample inside a Sand interval = 1, else 0 -- absence of a pick is treated as confirmed Not-Sand, not missing data.

random_seed
42
label_source
Lloyd's Register SANDCOUNT (lithology_interval)
test_fraction
0.2
gr_coverage_pct_threshold
10
pr auc
0.7229
n total
1988371
roc auc
0.8973
test wells
103
train wells
415
training rows
7923845
qualifying wells
518

Last run: 2026-08-02 15:25:09 UTC

How this result was generated

graph TD curves["Gamma Ray required
+ other logs if available"] rolling["Look at the trend nearby,
not just one number"] context["Which rock formation,
plus roughly where the well is"] label["SANDCOUNT pick = Sand?
(absence = Not-Sand)"] split["Split wells: most for learning,
some held back for testing"] train["Binary XGBoost
(200 trees, imbalance-weighted)"] predict["Score held-back wells
(Sand probability per depth)"] threshold["Compare at several
probability cutoffs"] curves --> rolling --> context --> label --> split --> train --> predict --> threshold

Hover a step for a plain-language explanation.