Visualizing soil texture data using R
Understanding and visualizing soil texture is crucial. Soil texture, defined by the proportions of sand, silt, and clay, significantly influences soil behavior, including nutrient availability, water retention, and aeration.
Classification of Soil Texture:
Soil Texture Class
The USDA (United States Department of Agriculture) system classifies soil into 12 primary texture classes based on the percentages of sand, silt, and clay. These texture classes are a standard for understanding and communicating soil composition. Here are the different texture classes:
Using the Soil Texture Triangle:
Implications of Soil Texture:
Plotting Soil Texture Using R
We'll explore the soiltexture package for creating detailed soil texture triangles and ggtern, a ggplot2 extension, for ternary plots, providing a richer perspective on soil data.
install.packages("soiltexture")
install.packages("tcltk", dependencies = TRUE)
library(ggplot2)
library(soiltexture)
library(ggtern)
Here, soiltexture specializes in soil texture analysis, tcltk for handling advanced GUI elements [you don't need the tcltk, considering the OS you are using, it may be required, but for Windows, it should be fine], ggplot2 for foundational plotting, and ggtern for ternary plotting.
The soiltexture package's TT.plot function supports various classification systems, including USDA, which is extensively used globally for soil classification:
TT.plot(class.sys = "USDA.TT")
To illustrate the capabilities of R in soil texture analysis, let's simulate a dataset:
#Simulated data
data <- data.frame(
SAND = c(40, 30, 20, ...),
SILT = c(40, 30, 50, ...),
CLAY = c(20, 40, 30, ...),
Location = factor(c("Location1", "Location2", ...))
)
TT.plot(class.sys = "USDA.TT", tri.data = data)
To enrich the interpretability of our plots, we add axis labels and color-coding based on location.
Recommended by LinkedIn
colors <- c("red", "blue", "green", "yellow", "purple")
TT.plot(class.sys = "USDA.TT", tri.data = data, main = "Soil Triangle", col = colors[data$Location])
For a more refined and detailed ternary plotting, we can use ggtern package:
ggtern(data, aes(x = SAND, y = SILT, z = CLAY, color = Location)) + geom_point(size = 3) + labs(title = "Soil Texture by Location", x = "Sand", y = "Silt", z = "Clay") + theme(legend.position = "right") +
theme_arrowshort()
You can customize the plot more based on your requirements, such as increasing text size, adding a black border around the soil texture triangle, or let's say, you have a third category of data, "organic matter" and you want to add to the plot, specifically to each location, and also want to make the size of the points dynamic representing the low or high percentage of organic matter. We can use the ggtern to do this:
Simulated data:
data <- data.frame(
SAND = c(40, 30, 20, 45, 35, 25, 55, 45, 30, 50, 60, 40, 35, 25, 55),
SILT = c(40, 30, 50, 35, 45, 55, 25, 35, 40, 30, 20, 30, 45, 55, 25),
CLAY = c(20, 40, 30, 20, 20, 20, 20, 20, 30, 20, 20, 30, 20, 20, 20),
Location = factor(rep(c("Location1", "Location2", "Location3", "Location4", "Location5"), 3)),
OrganicMatter = c(5.2, 3.4, 6.1, 4.5, 5.6, 4.3, 6.0, 3.8, 5.1, 4.7, 5.8, 3.9, 6.2, 4.0, 5.4) # Example percentages
)
Plot:
ggtern(data, aes(x = SAND, y = SILT, z = CLAY, color = Location, size = OrganicMatter)) +
geom_point() +
labs(title = "Soil Texture by Location",
x = "Sand",
y = "Silt",
z = "Clay",
size = "Organic Matter (%)") +
theme(
legend.position = "right",
legend.text = element_text(size = 20),
legend.title = element_text(size = 20),
plot.margin = unit(c(1,1,1,1), "cm"), # Adjust plot margins
panel.border = element_rect(colour = "black", fill=NA, size=2) # Add border
) +
theme_arrowshort()
Function to Classify soil into texture classes [this is simplified representation, you can update and modify as required]
classify_soil <- function(sand, silt, clay) {
if (sand + silt + clay != 100) {
return("Invalid")
}
# Sands
if (sand >= 85 && (silt + 1.5 * clay) <= 15) {
return("Sand")
}
# Loamy Sands
if (sand >= 70 && sand < 85 && (silt + 2 * clay) <= 30) {
return("Loamy Sand")
}
# Sandy Loams
if (clay <= 20 && sand >= 43 && sand < 85 && (silt + 2 * clay) > 30) {
return("Sandy Loam")
}
# Loam
if (clay >= 7 && clay <= 27 && silt >= 28 && silt <= 50 && sand < 52) {
return("Loam")
}
# Silt Loam
if ((silt >= 50 && clay >= 12 && clay <= 27) || (silt >= 50 && silt < 80 && clay < 12)) {
return("Silt Loam")
}
# Silt
if (silt >= 80 && clay < 12) {
return("Silt")
}
# Sandy Clay Loam
if (clay >= 20 && clay < 35 && silt < 28 && sand >= 45) {
return("Sandy Clay Loam")
}
# Clay Loam
if (clay >= 27 && clay < 40 && sand >= 20 && sand <= 45) {
return("Clay Loam")
}
# Silty Clay Loam
if (clay >= 27 && clay < 40 && sand < 20) {
return("Silty Clay Loam")
}
# Sandy Clay
if (clay >= 35 && sand >= 45) {
return("Sandy Clay")
}
# Silty Clay
if (clay >= 40 && silt >= 40) {
return("Silty Clay")
}
# Clay
if (clay >= 40 && sand < 45 && silt < 40) {
return("Clay")
}
return("Other")
}
# Apply the function to your data
data$SoilType <- apply(data[, c('SAND', 'SILT', 'CLAY')], 1, function(row) classify_soil(row[1], row[2], row[3]))
Classifying the texture class on the current dataset:
# Apply the function to your data
data$SoilType <- apply(data[, c('SAND', 'SILT', 'CLAY')], 1, function(row) classify_soil(row[1], row[2], row[3]))
Now, you can use this to update the ternary plot to show the texture class in the plot:
#from ggtern package
data("USDA")
#usda_text for the plolygon label
USDA_text <- USDA %>% group_by(Label) %>%
summarise_if(is.numeric, mean, na.rm = TRUE)
# Begin a ggplot with data from the USDA dataset and map aesthetic attributes
# for ternary coordinates to Sand, Clay, and Silt percentages.
ggplot(data = USDA, aes(
y = Clay,
x = Sand,
z = Silt
)) +
# Define the ternary coordinate system with Sand, Clay, and Silt
# mapped to the appropriate axes.
coord_tern(L = "x", T = "y", R = "z") +
# Draw the boundaries of the USDA soil texture classes as polygons.
geom_polygon(
aes(fill = Label), # Use 'Label' to determine the fill of the polygons
alpha = 0.0, # Set the polygons to be fully transparent
size = 0.5, # Set the size of the border of the polygons
color = "black" # Set the color of the border of the polygons
) +
# Overlay text labels on the ternary plot, positioned at the mean coordinates
# of each soil texture class as calculated and stored in USDA_text.
geom_text(data = USDA_text,
aes(label = Label), # Map 'Label' for the text to display
color = 'black', # Set the color of the text to black
size = 2) # Set the size of the text
# Plot the individual points from the 'data' dataset on the ternary plot.
# The points represent soil samples, with aesthetic mappings for color
# and size based on the 'Location' and 'OrganicMatter' respectively.
geom_point(data = data,
aes(x = SAND, y = CLAY, z = SILT, color = Location, size = OrganicMatter)) +
# Enhance the plot with arrows to indicate the direction of increase
# for each component of the ternary plot (Sand, Clay, Silt).
theme_showarrows()
Here,
Let's try some practice questions, you can put answers as comments:
Reference and Readings:
PhD Student at SLU
11moThis was really helpful. Thank you for the rundown.
Soil science Graduate || Academic researcher on soil and plant ||Agricultural Sustainability|| Food security || Soil Health|| Social media Manager|| Data Entry|| Data analyst|| Risk Analyst || GIS Enthusiast
1yThank you so much for this wonderful piece👏🏾. It will be very helpful for beginners using soil textural triangle.