Removes the variation in each indicator that is attributable to properties a manager cannot change – parent material, land-use history – so that what remains is the part an index can fairly hold anyone responsible for. A soil should not be scored down for being what its geology made it.
Usage
adjust_inherent(
data,
indicators,
inherent,
method = c("residual", "none"),
warn_r_squared = 0.95
)Arguments
- data
A data frame containing the indicators and the inherent factors.
- indicators
Character vector naming the numeric columns to adjust.
- inherent
A one-sided formula naming the inherent factors, for example
~ soil_type * land_use_history. Interactions are usually what you want: the effect of history often depends on the parent material.- method
"residual"(the default) adjusts;"none"returns the data untouched, for switching the step off without restructuring code.- warn_r_squared
Flag indicators whose inherent model explains more than this share of their variation. Defaults to 0.95.
NAdisables.
Value
An object of class inherent_adjustment:
- data
The data frame, with the named indicators replaced by their adjusted values and every other column untouched
- r_squared
Named vector: the share of each indicator's variation explained by the inherent factors
- models
The fitted
lmobjects, for inspection- indicators, inherent, method
The settings used
Details
The idea, and its provenance. Maaz et al. (2023) built scoring functions that account for inherent properties precisely to stop them biasing the overall score, noting that land-use history and soil type were the two most influential inherent drivers in their region. No additive-index paper in the reviewed corpus does this. Everyone else scores the raw measurement, which means a clay soil on old alluvium and a sandy soil on weathered granite are judged on the same scale for a difference neither farmer created.
The arithmetic is unremarkable and the framing is the point. Each indicator is regressed on the inherent factors and the residuals are kept, recentred on the indicator's own mean so that the scale is preserved and the scoring functions behave exactly as before. A reviewer can fairly say "those are just residuals". They are. What this function adds is an opinionated, documented, correctly-defaulted step in an index pipeline, and a report of what the adjustment cost.
Do not do this by reflex. Adjusting removes inherent variation by design. If your question is "which of these soils is inherently better?" – siting a plantation, valuing land, mapping capability – then adjusting destroys the answer you came for. Adjust when the question is about management: has this field been looked after, given what it started as. The two questions look similar and are not.
What the R-squared tells you. The returned r_squared is the
share of each indicator's variation that was inheritance rather than
anything a manager did. It is informative in its own right. On
soil_inherent, Clay and pH come back above
0.9 – almost entirely inherited – while P and bulk density sit near
0.2, meaning they were mostly telling you about management all along and had
little to adjust away. An indicator above warn_r_squared is flagged,
because once nearly all the variation is inherited the residual is mostly
noise.
On the default. The plan this implements proposed
method = "none" so that adjustment is always deliberate. The default
here is "residual" instead, because calling this function is
the deliberate act; a no-op default would mean
adjust_inherent(data, indicators, ~ soil_type) silently did nothing,
which is its own footgun. The deliberateness lives where it belongs: the
inherent argument of compute_sqi_df defaults to
NULL, so the pipeline never adjusts unless asked. "none"
remains available for switching the step off programmatically.
References
Maaz, T. M. et al. (2023), after Crow et al. (2022) on land-use history and soil type as the dominant inherent drivers.
See also
compute_sqi_df, whose inherent argument runs
this as a pre-scoring step; soil_inherent for data that has
the factors this needs
Examples
# Parent material dominates the exchange capacity
summary(aov(CEC ~ soil_type, data = soil_inherent))[[1]][["Pr(>F)"]][1]
#> [1] 5.3539e-56
adjusted <- adjust_inherent(
soil_inherent,
indicators = c("OM", "CEC", "pH", "P"),
inherent = ~ soil_type * land_use_history
)
# What each indicator inherited rather than earned
round(adjusted$r_squared, 3)
#> OM CEC pH P
#> 0.822 0.878 0.935 0.219
# The soil-type effect is gone, and the management signal is sharper
summary(aov(CEC ~ soil_type, data = adjusted$data))[[1]][["Pr(>F)"]][1]
#> [1] 1
summary(aov(CEC ~ management, data = adjusted$data))[[1]][["Pr(>F)"]][1]
#> [1] 8.136476e-07
