All of these functions will be simplified / documented in the near future, stay tuned.
Pending.
Source colors are in sRGB (D65) colorspace, as converted from Munsell
notation using aqp::munsell2rgb()
.
Trivial example using some of the aqp
built-in data.
library(aqp)
library(farver)
library(cluster)
library(ape)
library(colorspace)
library(soilDB)
library(sharpshootR)
# example data
data(sp5)
# semi-random subset
set.seed(10101)
idx <- sample(length(sp5), size = 15, replace = FALSE)
# truncate to 150cm for simplicity
x <- trunc(sp5[idx, ], 0, 150)
# quick check
par(mar = c(0, 0, 0, 1))
plotSPC(x, width = 0.35)
# convert HEX color notation -> sRGB [0,1]
rgb.data <- t(col2rgb(x$soil_color)) / 255
# copy to horizon level attributes of SPC
x$r <- rgb.data[, 1]
x$g <- rgb.data[, 2]
x$b <- rgb.data[, 3]
# develop color signature
pig <- soilColorSignature(x, method = 'depthSlices', RescaleLightnessBy = 5)
knitr::kable(pig, digits = 3, row.names = FALSE)
soil | A.0.1 | A.0.5 | A.0.9 | B.0.1 | B.0.5 | B.0.9 | L.0.1 | L.0.5 | L.0.9 |
---|---|---|---|---|---|---|---|---|---|
soil100 | 5.447 | 8.769 | 9.510 | 15.982 | 22.157 | 21.267 | 6.177 | 10.191 | 12.184 |
soil102 | 7.275 | 13.824 | 11.642 | 8.138 | 16.434 | 20.356 | 6.235 | 10.186 | 8.212 |
soil107 | 4.794 | 7.275 | 7.049 | 9.798 | 8.138 | 21.026 | 6.296 | 6.235 | 6.274 |
soil155 | 7.275 | 7.275 | 7.275 | 8.138 | 8.138 | 8.138 | 6.235 | 6.235 | 6.235 |
soil156 | 2.037 | 2.037 | 13.220 | 5.446 | 5.446 | 19.206 | 6.148 | 6.148 | 10.165 |
soil167 | 4.794 | 4.794 | 15.597 | 9.798 | 9.798 | 17.731 | 6.296 | 6.296 | 8.250 |
soil168 | 7.275 | 7.275 | 9.453 | 8.138 | 8.138 | 5.188 | 6.235 | 6.235 | 16.139 |
soil189 | 7.275 | 12.172 | 16.673 | 8.138 | 19.396 | 31.254 | 6.235 | 12.308 | 10.235 |
soil226 | 2.037 | 2.037 | 2.037 | 5.446 | 5.446 | 5.446 | 6.148 | 6.148 | 6.148 |
soil332 | 8.630 | 7.275 | 1.346 | 8.687 | 8.138 | 6.864 | 6.306 | 6.235 | 8.321 |
soil340 | 7.275 | 11.642 | 13.220 | 8.138 | 20.356 | 19.206 | 6.235 | 8.212 | 10.165 |
soil341 | 7.275 | 8.826 | 8.558 | 8.138 | 14.803 | 13.285 | 6.235 | 8.294 | 6.323 |
soil40 | 2.037 | 4.794 | 5.447 | 5.446 | 9.798 | 15.982 | 6.148 | 6.296 | 6.177 |
soil419 | 7.275 | 19.442 | 5.293 | 8.138 | 26.882 | 24.202 | 6.235 | 8.259 | 16.177 |
soil79 | 7.275 | 6.103 | 8.096 | 8.138 | 7.305 | 8.186 | 6.235 | 4.293 | 8.229 |
Plot results as dendrogram.
# copy ID to rownames
row.names(pig) <- pig[, 1]
# euclidean distance, leaving out the ID
d <- daisy(pig[, -1], stand = FALSE, metric = 'euclidean')
dd <- diana(d)
plotProfileDendrogram(x, clust = dd, scaling.factor = 0.33, y.offset = 4, width = 0.35)
s.list <- c('amador', 'redding', 'pentz', 'willows', 'pardee', 'yolo', 'hanford', 'cecil', 'sycamore', 'KLAMATH', 'MOGLIA', 'vleck', 'drummer', 'CANEYHEAD', 'musick', 'sierra', 'HAYNER', 'zook', 'argonaut', 'PALAU')
# get these soil series
s <- fetchOSD(s.list)
# manually convert Munsell -> sRGB
rgb.data <- munsell2rgb(s$hue, s$value, s$chroma, return_triplets = TRUE)
s$r <- rgb.data$r
s$g <- rgb.data$g
s$b <- rgb.data$b
# check
par(mar=c(0, 0, 0, 1))
plotSPC(s, name.style = 'center-center', width = 0.33)
Look at full range of OSD colors in this set.
Colors, arranged by ΔE00.
previewColors(s$soil_color)
rgb.colors <- munsell2rgb(s$hue, s$value, s$chroma, return_triplets = TRUE)
lab.colors <- as(sRGB(rgb.colors[['r']], rgb.colors[['g']], rgb.colors[['b']]), 'LAB')@coords
cols <- cbind(rgb.colors, lab.colors)
cols <- na.omit(cols)
cols <- as.data.frame(cols)
pairs(~ L + A + B, data=cols, pch=16, cex=2, col=rgb(cols$r, cols$g, cols$b))
Compute proportions of white, red, green, yellow, and blue “pigments” based on the CIE LAB representation of soil colors distributed over horizons, weighted by horizon thickness. Pigment proportions are used so that shallow soils can be compared with deep soils. Each row is an observation, columns describe the multivariate soil color signature.
# what is appropriate rescaling of L?
pig <- soilColorSignature(s, RescaleLightnessBy = 5, method = 'colorBucket')
knitr::kable(head(pig), digits = 3, row.names = FALSE)
id | .white.pigment | .red.pigment | .green.pigment | .yellow.pigment | .blue.pigment |
---|---|---|---|---|---|
AMADOR | 0.311 | 0.124 | 0.000 | 0.564 | 0.000 |
ARGONAUT | 0.159 | 0.328 | 0.000 | 0.513 | 0.000 |
CANEYHEAD | 0.560 | 0.061 | 0.050 | 0.330 | 0.000 |
CECIL | 0.120 | 0.413 | 0.000 | 0.467 | 0.000 |
DRUMMER | 0.473 | 0.103 | 0.004 | 0.417 | 0.003 |
HANFORD | 0.244 | 0.162 | 0.000 | 0.594 | 0.000 |
# move row names over for distance matrix
row.names(pig) <- pig[, 1]
d <- daisy(pig[, -1], stand = FALSE)
dd.colorBucket <- diana(d)
par(mar=c(0,0,1,1))
plotProfileDendrogram(s, dd.colorBucket, dend.y.scale = 1.18, scaling.factor = 0.0037, y.offset = 0.06, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
Extract CIE LAB coordinates at the 10th, 50th, and 90th percentiles of horizon mid-points. Each row is an observation, columns describe the multivariate soil color signature.
pig <- soilColorSignature(s, RescaleLightnessBy = 5, method = 'depthSlices')
knitr::kable(head(pig), digits = 3, row.names = FALSE)
id | A.0.1 | A.0.5 | A.0.9 | B.0.1 | B.0.5 | B.0.9 | L.0.1 | L.0.5 | L.0.9 |
---|---|---|---|---|---|---|---|---|---|
AMADOR | 3.660 | 4.855 | 1.917 | 13.220 | 18.895 | 27.799 | 8.183 | 10.281 | 14.314 |
ARGONAUT | 13.394 | 19.091 | 15.783 | 19.325 | 29.870 | 17.510 | 6.052 | 6.054 | 8.183 |
CANEYHEAD | 3.227 | 2.057 | -4.910 | 12.658 | 7.395 | 4.144 | 10.278 | 12.311 | 10.275 |
CECIL | 7.335 | 32.703 | 28.555 | 25.848 | 29.325 | 35.598 | 8.187 | 8.184 | 8.187 |
DRUMMER | 2.134 | 1.469 | 1.469 | 5.232 | 6.140 | 6.140 | 3.927 | 8.180 | 8.180 |
HANFORD | 5.596 | 5.596 | 6.590 | 19.731 | 19.731 | 25.398 | 8.185 | 8.185 | 10.283 |
# move row names over for distance matrix
row.names(pig) <- pig[, 1]
d <- daisy(pig[, -1], stand = FALSE)
dd.depthSlices <- diana(d)
par(mar=c(0,0,1,1))
plotProfileDendrogram(s, dd.depthSlices, dend.y.scale = 122, scaling.factor = 0.39, y.offset = 5.5, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
Select k-medoids (partitioning around medoids algorithm) from all possible colors within the profile or depth interval and use those CIE LAB coordinates as the signature. Each row is an observation, columns describe the multivariate soil color signature.
pig <- soilColorSignature(s, method = 'pam', RescaleLightnessBy = 5, pam.k = 3)
knitr::kable(head(pig), digits = 3, row.names = FALSE)
id | A.1 | A.2 | A.3 | B.1 | B.2 | B.3 | L.1 | L.2 | L.3 |
---|---|---|---|---|---|---|---|---|---|
AMADOR | 3.660 | 4.855 | 1.917 | 13.220 | 18.895 | 27.799 | 8.183 | 10.281 | 14.314 |
ARGONAUT | 15.783 | 19.091 | 9.847 | 17.510 | 29.870 | 22.591 | 8.183 | 6.054 | 10.281 |
CANEYHEAD | 2.057 | 0.861 | -4.910 | 7.395 | 7.413 | 4.144 | 12.311 | 10.276 | 10.275 |
CECIL | 7.335 | 32.703 | 28.555 | 25.848 | 29.325 | 35.598 | 8.187 | 8.184 | 8.187 |
DRUMMER | 2.134 | 1.469 | -0.326 | 5.232 | 6.140 | -0.316 | 3.927 | 8.180 | 4.178 |
HANFORD | 5.596 | 6.590 | 6.590 | 19.731 | 25.398 | 25.398 | 8.185 | 10.283 | 10.283 |
# move row names over for distance matrix
row.names(pig) <- pig[, 1]
d <- daisy(pig[, -1], stand = FALSE)
dd.pam <- diana(d)
par(mar=c(0,0,1,1))
plotProfileDendrogram(s, dd.pam, dend.y.scale = 128, scaling.factor = 0.41, y.offset = 5.5, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
par(mfrow = c(3, 1), mar=c(0,0,1,1))
plotProfileDendrogram(s, dd.colorBucket, dend.y.scale = 1.18, scaling.factor = 0.0038, y.offset = 0.06, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
mtext('colorBucket', side = 3, at = 1, line = -2, cex = 0.85, font = 2)
plotProfileDendrogram(s, dd.depthSlices, dend.y.scale = 122, scaling.factor = 0.38, y.offset = 5.5, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
mtext('depthSlices', side = 3, at = 1, line = -2, cex = 0.85, font = 2)
plotProfileDendrogram(s, dd.pam, dend.y.scale = 128, scaling.factor = 0.38, y.offset = 5.5, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
mtext('PAM', side = 3, at = 1, line = -2, cex = 0.85, font = 2)
Select k-medoids (partitioning around medoids algorithm) from all possible colors within the profile or depth interval and use those CIE LAB coordinates as the signature. Each row is an observation, columns describe the multivariate soil color signature. Distances are computed via delta-E (CIE2000), ΔE00 within each cluster and summed.
k <- 4
pig <- soilColorSignature(s, RescaleLightnessBy = 5, method = 'pam', pam.k = k)
# iterate over clusters, result is a distance matrix (delta-E00)
delta.E00 <- lapply(1:k, function(i) {
# LAB coordinates are named by cluster 1:k
v.names <- paste(c('L', 'A', 'B'), i, sep = '.')
# pair-wise delta-E00
d.i <- farver::compare_colour(pig[, v.names], pig[, v.names], from_space='lab', white_from = 'D65', method='cie2000')
# copy over SPC ids
dimnames(d.i) <- list(pig[, 1], pig[, 1])
# convert to dist object
d.i <- as.dist(d.i)
return(d.i)
})
# sum distance matrices
d <- Reduce('+', delta.E00)
# divisive clustering
dd <- diana(d)
par(mar=c(0,0,1,1))
plotProfileDendrogram(s, dd, dend.y.scale = max(d) * 2, scaling.factor = 0.55, y.offset = 7, width = 0.33, cex.names = 0.45, shrink = TRUE, name.style = 'center-center', max.depth = 210)
Using the distance matrix / clustering from above.
# adjust margins and default colors
par(mar=c(0,0,0,0), bg='black', fg='white', lend = 2)
# hang profiles from dendrogram
# note customization via arguments passed to plotSPC
plotProfileDendrogram(s, dd, dend.y.scale = max(d) * 2, scaling.factor = 0.55, y.offset = 2, width=0.4, name = NA, dend.color = 'white', dend.width = 2, divide.hz=FALSE, print.id=FALSE, depth.axis = FALSE, hz.distinctness.offset = 'hzd', max.depth = 210)
# adjust margins and default colors
par(mar=c(0,0,0,0), lend = 2)
# hang profiles from dendrogram
# note customization via arguments passed to plotSPC
plotProfileDendrogram(s, dd, dend.y.scale = max(d) * 1.5, scaling.factor = 0.38, y.offset = 2, width = 0.4, name = NA, dend.type = 'cladogram', dend.width = 2, divide.hz=FALSE, print.id=FALSE, depth.axis = FALSE, hz.distinctness.offset = 'hzd', max.depth = 210)
Arrange OSD sketches according to locations defined by non-metric multidimensional scaling (ordination) of pair-wise distances. Needs work.
library(MASS)
# add tiny amount for 0-distance pairs (duplicate profiles)
mds <- sammon(d + 0.001)$points
## Initial stress : 0.05849
## stress after 10 iters: 0.02385, magic = 0.500
## stress after 20 iters: 0.02378, magic = 0.500
# ensure ordering is preserved:
# profile IDs -> distance matrix -> nMDS
all(row.names(mds) == profile_id(s))
## [1] TRUE
# re-scale nMDS axis 1 to the typical horizontal scale used by plotSPC
xoff <- aqp:::.rescaleRange(mds[, 1], x0 = 1, x1 = length(s))
# adjust to reduce overlap
# with an expansion of the x-axis out to length(s) + 5
set.seed(10110)
xoff.fixed <- fixOverlap(xoff, thresh = 0.65, min.x = 1, max.x = length(s) + 5)
# re-scale nMDS axis 2 to the typical vertical scale used by plotSPC
yoff <- aqp:::.rescaleRange(mds[, 2], x0 = -10, x1 = max(s))
par(mar = c(0.25, 0.25, 0.25, 0.25), bg='black', fg='white')
plotSPC(s, y.offset = yoff, relative.pos = xoff.fixed, width = 0.25, name.style = 'center-center', hz.depths = FALSE, col.label = 'Generalized Horizon Label', print.id = TRUE, name = NA, scaling.factor = 0.8, shrink = TRUE)
Arrange OSD sketches according to (approximate) locations defined by non-metric multidimensional scaling (ordination) of pair-wise distances.
# use re-scaled nMDS coordinates as virtual transect
# adjust to reduce overlap
set.seed(10110)
pos <- alignTransect(xoff, x.min = 1, x.max = length(s) + 4, thresh = 0.7)
par(mar = c(0.25, 0.25, 0.25, 0.25))
plotSPC(s, n = length(s) + 4, y.offset = yoff[pos$order], plot.order = pos$order, relative.pos = pos$relative.pos, width = 0.25, name.style = 'center-center', hz.depths = FALSE, col.label = 'Generalized Horizon Label', print.id = TRUE, scaling.factor = 1.5, shrink = TRUE)
box()
This document is based on aqp
version 2.01.