AQP Demo: Applying a Function to Each Soil Profile in a Collection with profileApply()
Jun 19, 2012 metroadminHere is a quick demonstration of how functionality from the AQP package can be used to answer complex soils-related questions. In these examples the profileApply() function is used to iterate over a collection of soil profiles, and compute several metrics of soil development:
- depth of maximum clay content
- clay/RF content within the PCS
- clay content within the argillic horizon
- thickness of argillic horizon
- depth to argillic horizon
Example Session
# libraries: you may have to install theselibrary(soilDB)library(Hmisc)library(ape)library(lattice)library(cluster)library(reshape)# load example datasetdata(gopheridge)## aggregates of horizon data, moved into @site# soil depth using an in-line function, note that max(SPC) returns the bottom-most depth# this may not be the same as 'soil depth' when Cr or R horizons are presentgopheridge$soil.depth <- profileApply(gopheridge, function(x) max(x))# horizon-thickness weighted mean (beware of NA)f.wt.prop <- function(x, prop) {h <- horizons(x); w <- with(h, hzdepb-hzdept); wtd.mean(h[[prop]], weights=w)}# get hz mid-point of hz with max clay contentf.max.clay.depth <- function(x) {h <- horizons(x)max.clay <- which.max(h$clay)with(h[max.clay, ], (hzdept+hzdepb) / 2)}# apply by-profile, returning a vector, assigned to new column in @sitegopheridge$wt.clay <- profileApply(gopheridge, f.wt.prop, prop='clay')gopheridge$wt.rf <- profileApply(gopheridge, f.wt.prop, prop='total_frags_pct')gopheridge$max.clay.depth <- profileApply(gopheridge, f.max.clay.depth)# compute aggregate (wt.mean) clay within particle-size control section# note that this requires conditional eval of data that may contain NA# see ?slab and ?soil.slot for details on the syntaxf.pcs.prop <- function(x, prop) {# these are accessed from @sitesv <- c(x$psctopdepth, x$pscbotdepth)# test for missing PCS dataif(any(is.na(sv)))return(NA)# create formula from named propertyfm <- as.formula(paste('~', prop))# return just the (weighted) mean, accessed from @horizonss <- slab(x, fm, slab.structure=sv, slab.fun=mean, na.rm=TRUE)return(s$value)}# compute the weighted-mean of some property within a given diagnostic horizon# note that this requires conditional eval of data that may contain NA# see ?slab and ?soil.slot for details on the syntax# note that function expects certain columns within 'x'f.diag.wt.prop <- function(x, d.hz, prop) {# extract diagnostic horizon datad <- diagnostic_hz(x)# subset to the requested diagnostic hzd <- d[d$diag_kind == d.hz, ]# if missing return NAif(nrow(d) == 0)return(NA)# extract depths and check for missingsv <- c(d$featdept, d$featdepb)if(any(is.na(sv)))return(NA)# create formula from named propertyfm <- as.formula(paste('~', prop))# return just the (weighted) mean, accessed from @horizonss <- slab(x, fm, slab.structure=sv, slab.fun=mean, na.rm=TRUE)return(s$value)}# conditional eval of thickness of some diagnostic feature or horizon# will return a vector of length(x), you can save to @sitef.diag.thickness <- function(x, d.hz) {# extract diagnostic horizon datad <- diagnostic_hz(x)# subset to the requested diagnostic hzd <- d[d$diag_kind == d.hz, ]# if missing return NAif(nrow(d) == 0)return(NA)# compute thicknessthick <- d$featdepb - d$featdeptreturn(thick)}# conditional eval of top hz depth of diagnostic feature or horizon# will return a vector of length(x), you can save to @sitef.diag.top <- function(x, d.hz) {# extract diagnostic horizon datad <- diagnostic_hz(x)# subset to the requested diagnostic hzd <- d[d$diag_kind == d.hz, ]# if missing return NAif(nrow(d) == 0)return(NA)# return top depthreturn(d$featdept)}# compute wt.mean clay within PCS, save to @sitegopheridge$pcs.clay <- profileApply(gopheridge, f.pcs.prop, prop='clay')# compute wt.mean clay within argillic horizon, save to @sitegopheridge$argillic.clay <- profileApply(gopheridge, f.diag.wt.prop, d.hz='argillic horizon', prop='clay')# compute argillic hz thickness, save to @sitegopheridge$argillic.thick <- profileApply(gopheridge, f.diag.thickness, d.hz='argillic horizon')# compute depth to paralithic contact, if present, save to @sitegopheridge$paralithic.contact.depth <- profileApply(gopheridge, f.diag.top, d.hz='paralithic contact')# order by presence of paralithic contactnew.order <- order(gopheridge$paralithic.contact)par(mar=c(0,0,0,0))plot(gopheridge, name='hzname', plot.order=new.order)# annotate paralithic contact with linesx.pos <- 1:length(gopheridge)lines(x.pos, gopheridge$paralithic.contact.depth[new.order], lty=2, col='black')# plot and annotate with max clay depthspar(mar=c(0,0,0,0))plot(gopheridge, name='hzname')x.pos <- 1:length(gopheridge)points(x.pos, gopheridge$max.clay.depth, col='white', pch=21, cex=0.75, bg='black')lines(x.pos, gopheridge$max.clay.depth, col='black', lty=2)X