soiltillr provides a complete workflow for analysing soil tillage depth and erosion data across years and field treatments. It covers data validation, tillage summaries, compaction detection, erosion tracking, soil loss estimation, and visualisation.
This vignette walks through the full analysis pipeline using the two
built-in datasets: tillage_operations and
erosion_profile.
library(soiltillr)data(tillage_operations)
data(erosion_profile)
head(tillage_operations)
#> year date field_id operation depth_cm speed_kmh
#> 1 2018 2018-03-15 Field_A moldboard_plow 32 6.5
#> 2 2018 2018-10-20 Field_A disc_harrow 12 8.0
#> 3 2019 2019-03-18 Field_A moldboard_plow 30 6.5
#> 4 2019 2019-10-22 Field_A disc_harrow 11 8.0
#> 5 2020 2020-03-20 Field_A chisel_plow 22 7.0
#> 6 2020 2020-10-18 Field_A disc_harrow 10 8.5
head(erosion_profile)
#> year field_id erosion_depth_mm soil_loss_t_ha bulk_density_g_cm3
#> 1 2018 Field_A 4.8 12.4 1.42
#> 2 2019 Field_A 4.5 11.8 1.45
#> 3 2020 Field_A 3.9 10.2 1.48
#> 4 2021 Field_A 3.2 8.5 1.44
#> 5 2022 Field_A 2.4 6.3 1.40
#> 6 2023 Field_A 1.8 4.7 1.36
#> organic_matter_pct rainfall_mm slope_pct
#> 1 2.8 680 4.5
#> 2 2.6 720 4.5
#> 3 2.5 590 4.5
#> 4 2.6 810 4.5
#> 5 2.8 640 4.5
#> 6 3.1 700 4.5tillage_operations contains 20 records of tillage events
across two fields (Field_A and Field_B) from 2018 to 2023. Field_A
transitions from conventional deep tillage (moldboard plowing at 30+ cm)
to conservation tillage and no-till by 2023. Field_B maintains reduced
tillage throughout.
erosion_profile contains annual soil erosion depth, soil
loss, bulk density, organic matter, rainfall, and slope for the same two
fields and period.
Always validate your data before analysis.
validate_soil_data() checks for missing columns, negative
values, empty data frames, and implausible years.
chk <- validate_soil_data(
data = tillage_operations,
year_col = "year",
field_col = "field_id",
value_col = "depth_cm"
)
chk$valid # TRUE = data is clean
#> [1] TRUE
chk$issues # critical errors
#> character(0)
chk$warnings # informational notices
#> character(0)summarise_tillage() computes year × field statistics:
number of operations, mean, maximum, and total depth, plus dominant
operation type.
till_sum <- summarise_tillage(
data = tillage_operations,
year_col = "year",
field_col = "field_id",
depth_col = "depth_cm",
op_col = "operation"
)
print(till_sum)
#> year field_id n_operations mean_depth_cm max_depth_cm total_depth_cm
#> 1 2018 Field_A 2 22.0 32 44
#> 2 2018 Field_B 1 20.0 20 20
#> 3 2019 Field_A 2 20.5 30 41
#> 4 2019 Field_B 2 13.5 18 27
#> 5 2020 Field_A 2 16.0 22 32
#> 6 2020 Field_B 1 17.0 17 17
#> 7 2021 Field_A 2 15.0 20 30
#> 8 2021 Field_B 2 7.0 14 14
#> 9 2022 Field_A 2 9.0 18 18
#> 10 2022 Field_B 1 13.0 13 13
#> 11 2023 Field_A 2 7.5 15 15
#> 12 2023 Field_B 1 0.0 0 0
#> dominant_operation
#> 1 disc_harrow
#> 2 chisel_plow
#> 3 disc_harrow
#> 4 chisel_plow
#> 5 chisel_plow
#> 6 chisel_plow
#> 7 chisel_plow
#> 8 conservation_till
#> 9 chisel_plow
#> 10 conservation_till
#> 11 conservation_till
#> 12 no_tillField_A shows mean depth declining from 22.0 cm in 2018 to 7.5 cm in 2023, reflecting the shift toward conservation tillage.
tillage_depth_trend() calculates the year-on-year change
in mean depth per field and classifies each year as
"baseline", "increasing",
"decreasing", or "stable".
till_trend <- tillage_depth_trend(
data = tillage_operations,
year_col = "year",
field_col = "field_id",
depth_col = "depth_cm"
)
print(till_trend)
#> year field_id mean_depth_cm depth_change_cm trend
#> 1 2018 Field_A 22.0 NA baseline
#> 2 2019 Field_A 20.5 -1.5 decreasing
#> 3 2020 Field_A 16.0 -4.5 decreasing
#> 4 2021 Field_A 15.0 -1.0 decreasing
#> 5 2022 Field_A 9.0 -6.0 decreasing
#> 6 2023 Field_A 7.5 -1.5 decreasing
#> 7 2018 Field_B 20.0 NA baseline
#> 8 2019 Field_B 13.5 -6.5 decreasing
#> 9 2020 Field_B 17.0 3.5 increasing
#> 10 2021 Field_B 7.0 -10.0 decreasing
#> 11 2022 Field_B 13.0 6.0 increasing
#> 12 2023 Field_B 0.0 -13.0 decreasing
# Years with decreasing tillage depth
till_trend[till_trend$trend == "decreasing", ]
#> year field_id mean_depth_cm depth_change_cm trend
#> 2 2019 Field_A 20.5 -1.5 decreasing
#> 3 2020 Field_A 16.0 -4.5 decreasing
#> 4 2021 Field_A 15.0 -1.0 decreasing
#> 5 2022 Field_A 9.0 -6.0 decreasing
#> 6 2023 Field_A 7.5 -1.5 decreasing
#> 8 2019 Field_B 13.5 -6.5 decreasing
#> 10 2021 Field_B 7.0 -10.0 decreasing
#> 12 2023 Field_B 0.0 -13.0 decreasingdetect_compaction() classifies compaction risk as
"high", "moderate", or "low"
based on a user-defined depth threshold (default 20 cm) and estimates
the depth of potential plow pan formation.
risk <- detect_compaction(
data = tillage_operations,
year_col = "year",
field_col = "field_id",
depth_col = "depth_cm",
op_col = "operation",
compaction_threshold_cm = 20
)
print(risk)
#> year field_id mean_depth_cm compaction_risk plow_pan_depth_cm
#> 1 2018 Field_A 22.0 high 24.0
#> 2 2018 Field_B 20.0 high 22.0
#> 3 2019 Field_A 20.5 high 22.5
#> 4 2019 Field_B 13.5 low NA
#> 5 2020 Field_A 16.0 moderate 18.0
#> 6 2020 Field_B 17.0 moderate 19.0
#> 7 2021 Field_A 15.0 moderate 17.0
#> 8 2021 Field_B 7.0 low NA
#> 9 2022 Field_A 9.0 low NA
#> 10 2022 Field_B 13.0 low NA
#> 11 2023 Field_A 7.5 low NA
#> 12 2023 Field_B 0.0 low NA
# High-risk years only
risk[risk$compaction_risk == "high", ]
#> year field_id mean_depth_cm compaction_risk plow_pan_depth_cm
#> 1 2018 Field_A 22.0 high 24.0
#> 2 2018 Field_B 20.0 high 22.0
#> 3 2019 Field_A 20.5 high 22.5track_erosion_depth() computes annual erosion depth,
year-on-year change, and cumulative erosion per field. Trend is
classified as "baseline", "improving",
"worsening", or "stable".
eros <- track_erosion_depth(
data = erosion_profile,
year_col = "year",
field_col = "field_id",
erosion_col = "erosion_depth_mm"
)
print(eros)
#> year field_id erosion_depth_mm change_mm cumulative_loss_mm trend
#> 1 2018 Field_A 4.8 NA 4.8 baseline
#> 2 2019 Field_A 4.5 -0.3 9.3 improving
#> 3 2020 Field_A 3.9 -0.6 13.2 improving
#> 4 2021 Field_A 3.2 -0.7 16.4 improving
#> 5 2022 Field_A 2.4 -0.8 18.8 improving
#> 6 2023 Field_A 1.8 -0.6 20.6 improving
#> 7 2018 Field_B 2.9 NA 2.9 baseline
#> 8 2019 Field_B 2.6 -0.3 5.5 improving
#> 9 2020 Field_B 2.3 -0.3 7.8 improving
#> 10 2021 Field_B 2.0 -0.3 9.8 improving
#> 11 2022 Field_B 1.6 -0.4 11.4 improving
#> 12 2023 Field_B 1.3 -0.3 12.7 improvingBoth fields show consistently improving erosion trends. Field_A’s cumulative loss (20.6 mm) is higher than Field_B’s (12.7 mm), reflecting its historically more intensive tillage.
estimate_soil_loss() uses a mass-balance approach to
estimate soil loss in t/ha. The optional slope correction applies the
McCool et al. (1987) simplified LS factor:
LS = (slope% / 9)^0.6.
loss <- estimate_soil_loss(
data = erosion_profile,
year_col = "year",
field_col = "field_id",
erosion_col = "erosion_depth_mm",
bulk_density_col = "bulk_density_g_cm3",
slope_col = "slope_pct"
)
print(loss)
#> year field_id erosion_depth_mm estimated_loss_t_ha loss_category
#> 1 2018 Field_A 4.8 71.23 very severe
#> 2 2019 Field_A 4.5 68.19 very severe
#> 3 2020 Field_A 3.9 60.32 very severe
#> 4 2021 Field_A 3.2 48.15 very severe
#> 5 2022 Field_A 2.4 35.11 very severe
#> 6 2023 Field_A 1.8 25.58 very severe
#> 7 2018 Field_B 2.9 40.25 very severe
#> 8 2019 Field_B 2.6 35.82 very severe
#> 9 2020 Field_B 2.3 31.45 very severe
#> 10 2021 Field_B 2.0 27.14 very severe
#> 11 2022 Field_B 1.6 21.38 very severe
#> 12 2023 Field_B 1.3 17.11 severeNote:
estimated_loss_t_hais a physics-based estimate from erosion depth and bulk density. It will differ from independently measuredsoil_loss_t_havalues in the dataset. For full RUSLE prediction, all five factors (R, K, LS, C, P) should be considered (Renard et al., 1997).
compare_fields() produces a wide-format year-by-year
comparison showing erosion depth and organic matter for each field, plus
difference columns.
comp <- compare_fields(
data = erosion_profile,
year_col = "year",
field_col = "field_id",
erosion_col = "erosion_depth_mm",
om_col = "organic_matter_pct"
)
print(comp)
#> year erosion_Field_A_mm om_Field_A_pct erosion_Field_B_mm om_Field_B_pct
#> 1 2018 4.8 2.8 2.9 3.2
#> 2 2019 4.5 2.6 2.6 3.3
#> 3 2020 3.9 2.5 2.3 3.5
#> 4 2021 3.2 2.6 2.0 3.7
#> 5 2022 2.4 2.8 1.6 3.9
#> 6 2023 1.8 3.1 1.3 4.2
#> erosion_diff_mm om_diff_pct
#> 1 1.9 -0.4
#> 2 1.9 -0.7
#> 3 1.6 -1.0
#> 4 1.2 -1.1
#> 5 0.8 -1.1
#> 6 0.5 -1.1The erosion_diff_mm column confirms Field_A consistently
has higher erosion than Field_B, with the gap narrowing from 1.9 mm in
2018 to 0.5 mm in 2023. The om_diff_pct column shows
Field_B maintaining higher organic matter throughout, with Field_A
recovering under conservation management.
plot_tillage_timeline(
data = tillage_operations,
year_col = "year",
field_col = "field_id",
depth_col = "depth_cm",
title = "Tillage Depth Transition: Conventional to Conservation"
)Mean annual tillage depth by field, 2018-2023.
plot_erosion_trend(
data = erosion_profile,
year_col = "year",
field_col = "field_id",
erosion_col = "erosion_depth_mm",
show_cumulative = TRUE,
title = "Erosion Depth and Cumulative Loss 2018-2023"
)Annual erosion depth and cumulative loss by field.
plot_om_trend(
data = erosion_profile,
year_col = "year",
field_col = "field_id",
om_col = "organic_matter_pct",
title = "Soil Organic Matter Recovery"
)Soil organic matter recovery under conservation tillage.
The dashed line at 3.5% represents a commonly cited threshold for adequate soil organic matter in agricultural soils. Field_A crosses this threshold by 2023 under conservation management.
plot_tillage_erosion(
tillage_data = tillage_operations,
erosion_data = erosion_profile,
year_col = "year",
field_col = "field_id",
depth_col = "depth_cm",
erosion_col = "erosion_depth_mm",
title = "Impact of Tillage Management on Soil Erosion"
)Dual-panel comparison of mean tillage depth and erosion depth.
The functions are designed to chain together naturally:
library(soiltillr)
data(tillage_operations)
data(erosion_profile)
# Step 1: validate
stopifnot(validate_soil_data(tillage_operations,
"year", "field_id", "depth_cm")$valid)
# Step 2: tillage
till_sum <- summarise_tillage(tillage_operations, "year", "field_id",
"depth_cm", op_col = "operation")
till_trend <- tillage_depth_trend(tillage_operations, "year",
"field_id", "depth_cm")
compact <- detect_compaction(tillage_operations, "year", "field_id",
"depth_cm", op_col = "operation")
# Step 3: erosion
eros_track <- track_erosion_depth(erosion_profile, "year",
"field_id", "erosion_depth_mm")
eros_loss <- estimate_soil_loss(erosion_profile, "year", "field_id",
"erosion_depth_mm", "bulk_density_g_cm3",
slope_col = "slope_pct")
field_comp <- compare_fields(erosion_profile, "year", "field_id",
"erosion_depth_mm", "organic_matter_pct")
# Step 4: visualise
plot_tillage_timeline(tillage_operations, "year", "field_id", "depth_cm")
plot_erosion_trend(erosion_profile, "year", "field_id",
"erosion_depth_mm", show_cumulative = TRUE)
plot_om_trend(erosion_profile, "year", "field_id", "organic_matter_pct")
plot_tillage_erosion(tillage_operations, erosion_profile,
"year", "field_id", "depth_cm", "erosion_depth_mm")Lal, R. (2001). Soil degradation by erosion. Land Degradation and Development, 12(6), 519–539. https://doi.org/10.1002/ldr.472
McCool, D. K., Brown, L. C., Foster, G. R., Mutchler, C. K., & Meyer, L. D. (1987). Revised slope steepness factor for the Universal Soil Loss Equation. Transactions of the ASAE, 30(5), 1387–1396. https://doi.org/10.13031/2013.30576
Renard, K. G., Foster, G. R., Weesies, G. A., McCool, D. K., & Yoder, D. C. (1997). Predicting Soil Erosion by Water: A Guide to Conservation Planning with the Revised Universal Soil Loss Equation (RUSLE). USDA Agriculture Handbook No. 703. https://ntrl.ntis.gov/NTRL/dashboard/searchResults/titleDetail/PB97153704.xhtml