Method Validation and Numerical Assumptions
Source:vignettes/method-validation.Rmd
method-validation.RmdPurpose
This vignette documents numerical conventions used by grainsizeR and explains how the package validates them with synthetic examples and package example data. It is a method-audit document, not a new scientific method.
Detailed interpretation of table layouts versus measurement setups is covered in the table layouts and measurement workflows vignette.
Example Data Used for Validation
The package includes two example files:
long_file <- system.file("extdata", "grain.long.csv", package = "grainsizeR")
wide_file <- system.file("extdata", "grain.wide.csv", package = "grainsizeR")
gs_long <- read_gsd(
long_file,
format = "long",
sample_col = "sample",
size_col = "size",
value_col = "proportion",
size_unit = "mm",
value_type = "proportion"
)
gs_wide <- read_gsd(
wide_file,
format = "wide",
size_col = 1,
size_unit = "mm",
value_type = "percent"
)grain.long.csv is a sieve + hydrometer example stored as
a tidy long table and has finer particle-size resolution where
hydrometer data were added. grain.wide.csv is a dry-sieve
example stored as a multi-sample wide table and is useful for coarse
gravel/sand/mud summaries.
Input-Bin Convention
Input rows describe retained material in grain-size classes. A numeric label is used as a class boundary. Terminal coarse and fine classes can be open-ended. Open-ended classes are retained in the data structure, but their missing boundary is not silently replaced by zero or infinity for interpolation.
Long Versus Wide Example Data
The long and wide examples contain the same sample IDs. Here, the
file names describe table layout, not measurement method. Strict
Wentworth major summaries use the phi-scale 62.5 um sand/mud boundary.
The gravel_sand_mud scheme uses the GRADISTAT-compatible 63
um boundary, so it is not guaranteed to match
wentworth_major exactly for samples with material near that
boundary. Clay/silt-level schemes such as USDA, ISSS, and UK SSEW
require thresholds such as 2, 20, 50, 60, or 63 um; these are better
resolved when finite measured boundaries bracket those thresholds.
Instrument outputs such as laser cumulative percent-finer curves may
require preprocessing before import. read_gsd() expects
retained proportions or retained percentages by size class, not
cumulative percent passing.
Percent-Finer Convention
grainsizeR uses cumulative percent finer. At a finite boundary,
percent finer is the retained percent below that boundary. This
convention is used by gs_cumulative(),
gs_percent_finer(), and gs_d_values().
D-Values
D_p is the grain size at which p percent of
the sample is finer. For example, D50 is the median grain
size by the percent-finer convention.
gs_d_values(subset(gs_long, sample_id == "S01"), probs = c(10, 50, 90), extrapolate = "warn_linear")
#> # A tibble: 3 × 7
#> sample_id percentile grain_size_um grain_size_mm grain_size_phi
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 S01 10 40.9 0.0409 4.61
#> 2 S01 50 123. 0.123 3.02
#> 3 S01 90 390. 0.390 1.36
#> # ℹ 2 more variables: interpolation_scale <chr>, extrapolated <lgl>Threshold Interpolation
gs_percent_finer() estimates percent finer at arbitrary
thresholds by interpolating the cumulative percent-finer curve between
finite boundaries. Thresholds do not need to be measured boundaries.
This is how thresholds such as 2, 20, 50, 60, and 63 um are handled when
they are bracketed by finite class boundaries.
suppressWarnings(gs_percent_finer(
subset(gs_long, sample_id == "S01"),
sizes = c(20, 50, 60, 63),
size_unit = "um",
extrapolate = "warn_linear"
))
#> # A tibble: 4 × 8
#> sample_id threshold_um threshold_mm threshold_phi percent_finer
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 S01 20 0.02 5.64 3.90
#> 2 S01 50 0.05 4.32 12.5
#> 3 S01 60 0.06 4.06 14.0
#> 4 S01 63 0.063 3.99 14.4
#> # ℹ 3 more variables: percent_coarser <dbl>, interpolation_scale <chr>,
#> # extrapolated <lgl>Open-Ended Terminal Classes
Terminal open-ended classes are not silently treated as bounded
intervals. For example, a final class such as <0.0625 mm
in a strict Wentworth-style input is not treated as
[0, 62.5]. When a requested D-value or threshold falls
inside an open-ended class, extrapolate = "error" reports
the unresolved value. Users can explicitly set
extrapolate = "warn_linear" when extrapolation is
acceptable for their workflow.
gs_diagnostics() can be used before summary calculations
to identify samples with unresolved D-values, unresolved clay/silt
thresholds, open-ended terminal classes, or fraction schemes that
require finer measurements.
gs_diagnostics(subset(gs_wide, sample_id == "S01"), output = "summary")
#> # A tibble: 1 × 8
#> sample_id n_ok n_warning n_error n_info has_error has_warning overall_status
#> <chr> <int> <int> <int> <int> <lgl> <lgl> <chr>
#> 1 S01 16 12 0 3 FALSE TRUE warningFraction Schemes
Fraction schemes use gs_percent_finer() internally at
scheme boundaries. Coarse schemes such as wentworth_major
can be robust when their thresholds are resolved. Texture fraction
schemes with fine thresholds should use input data that bracket those
thresholds with finite boundaries, regardless of whether the table is
long or wide.
head(gs_fractions_wide(gs_long, scheme = "wentworth_major"))
#> # A tibble: 6 × 4
#> sample_id gravel_percent sand_percent mud_percent
#> <chr> <dbl> <dbl> <dbl>
#> 1 S01 0.624 85.1 14.3
#> 2 S02 0.224 97.8 1.93
#> 3 S03 0.312 95.1 4.60
#> 4 S04 0.153 89.7 10.2
#> 5 S05 0.295 89.4 10.4
#> 6 S06 0.230 98.8 0.964
head(gs_fractions_wide(gs_wide, scheme = "wentworth_major"))
#> # A tibble: 6 × 4
#> sample_id gravel_percent sand_percent mud_percent
#> <chr> <dbl> <dbl> <dbl>
#> 1 S01 0.624 85.0 14.4
#> 2 S02 0.224 97.8 1.93
#> 3 S03 0.312 95.1 4.60
#> 4 S04 0.153 89.6 10.2
#> 5 S05 0.295 88.8 10.9
#> 6 S06 0.230 98.8 0.964Folk and Ward Statistics
Folk and Ward statistics are calculated from boundary-interpolated D-values. If a required D-value falls outside the finite boundary curve, users must choose whether to error or explicitly extrapolate.
suppressWarnings(gs_folk_ward(
subset(gs_long, sample_id == "S01"),
extrapolate = "warn_linear"
))
#> # A tibble: 1 × 26
#> sample_id D5_um D16_um D25_um D50_um D75_um D84_um D95_um D5_phi D16_phi
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 S01 25.1 64.9 76.9 123. 233. 314. 468. 5.31 3.94
#> # ℹ 16 more variables: D25_phi <dbl>, D50_phi <dbl>, D75_phi <dbl>,
#> # D84_phi <dbl>, D95_phi <dbl>, mean_fw_phi <dbl>, mean_fw_um <dbl>,
#> # sorting_fw_phi <dbl>, skewness_fw <dbl>, kurtosis_fw <dbl>,
#> # interpolation_scale <chr>, any_extrapolated <lgl>, mean_size_class <chr>,
#> # sorting_class <chr>, skewness_class <chr>, kurtosis_class <chr>Moment Statistics
Moment statistics require explicit open-end handling.
open_end = "error" prevents silent assumptions.
open_end = "extend_phi" estimates open-ended midpoints by
extending adjacent phi intervals, and open_end = "omit"
omits open-ended retained classes.
suppressWarnings(gs_moments(
subset(gs_long, sample_id == "S01"),
open_end = "extend_phi"
))
#> # A tibble: 1 × 14
#> sample_id moment_method mean_moment mean_moment_unit mean_moment_um
#> <chr> <chr> <dbl> <chr> <dbl>
#> 1 S01 logarithmic_phi 2.97 phi 127.
#> # ℹ 9 more variables: mean_moment_phi <dbl>, sd_moment <dbl>,
#> # sd_moment_unit <chr>, skewness_moment <dbl>, kurtosis_moment <dbl>,
#> # retained_percent_used <dbl>, open_end <chr>, open_end_estimated <lgl>,
#> # open_end_omitted <lgl>Summary-Table Consistency
gs_parameters() is a reporting convenience wrapper
around lower-level functions. It does not introduce new calculations;
tests compare its output against gs_d_values(),
gs_grain_size_indices(), gs_folk_ward(), and
gs_fractions_wide().
sample_id <- "S01"
summary <- suppressWarnings(gs_parameters(
subset(gs_long, sample_id == sample_id),
parameters = c("d_values", "indices", "folk_ward", "fractions"),
fraction_scheme = "gradistat",
extrapolate = "warn_linear"
))
summary
#> # A tibble: 30 × 41
#> sample_id D5_um D10_um D16_um D25_um D50_um D75_um D84_um D90_um D95_um
#> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 S01 25.1 40.9 64.9 76.9 123. 233. 314. 390. 468.
#> 2 S02 68.2 77.6 90.7 114. 175. 267. 346. 412. 476.
#> 3 S03 63.5 69.5 77.5 91.3 151. 278. 347. 402. 455.
#> 4 S04 32.3 60.2 69.6 81.2 125. 258. 333. 395. 456.
#> 5 S05 35.3 62.2 68.7 80.1 123. 270. 347. 410. 472.
#> 6 S06 68.5 76.1 86.2 104. 216. 346. 399. 439. 475.
#> 7 S07 67.1 75.6 87.3 108. 175. 286. 366. 432. 496.
#> 8 S08 70.1 82.0 98.8 130. 251. 358. 408. 444. 477.
#> 9 S09 72.2 86.6 108. 142. 261. 365. 412. 447. 478.
#> 10 S10 67.0 77.6 92.7 121. 227. 350. 404. 444. 481.
#> # ℹ 20 more rows
#> # ℹ 31 more variables: D30_um <dbl>, D60_um <dbl>, Cu <dbl>, Cc <dbl>,
#> # So_trask <dbl>, Sk_trask <dbl>, fine_content_percent <dbl>,
#> # fine_threshold_um <dbl>, fine_equivalent <dbl>, interpolation_scale <chr>,
#> # D5_phi <dbl>, D16_phi <dbl>, D25_phi <dbl>, D50_phi <dbl>, D75_phi <dbl>,
#> # D84_phi <dbl>, D95_phi <dbl>, mean_fw_phi <dbl>, mean_fw_um <dbl>,
#> # sorting_fw_phi <dbl>, skewness_fw <dbl>, kurtosis_fw <dbl>, …What This Package Does Not Do
grainsizeR does not implement civil-engineering classification systems. Built-in official texture polygon datasets are not bundled yet. User-supplied texture polygons are supported, and source-audit scaffolds document how future official polygon datasets should be reconstructed and reviewed.