关于ggtern中线型(geom_segment)与平滑数据(geom_smooth_tern)交点的技术问询
geom_segment and geom_smooth_tern in ggtern Hey there! Let's walk through how to calculate the intersection point between your ternary segment (geom_segment) and the smoothed curve from geom_smooth_tern. Here's a step-by-step approach tailored to your setup:
1. Extract the Smoothed Curve Data First
The geom_smooth_tern with loess doesn't just plot your raw data—it generates a set of fitted points that form the smooth line. To work with this curve, we first need to extract these fitted points from your ggtern plot object:
# Assume your complete ggtern plot is stored in 'tern_plot' tern_plot <- ggtern() + geom_smooth_tern(data = dataTernNaCl, aes(x = NaCl, y = H2O, z = Na2SO4), method = loess, se = FALSE, color = "blue") + geom_segment(data = your_segment_data, # Replace with your segment's dataframe aes(x = x_start, y = y_start, z = z_start, xend = x_end, yend = y_end, zend = z_end)) # Extract all plot data layers plot_build <- ggplot_build(tern_plot) plot_data <- plot_build$data # Locate the smooth curve data layer (look for "smooth_tern" geom type) smooth_layer_idx <- which(sapply(plot_data, function(layer) layer$geom) == "smooth_tern") smooth_curve_data <- plot_data[[smooth_layer_idx]]
Now smooth_curve_data contains the x/y/z coordinates of every point on your blue smoothed line.
2. Handle Ternary Coordinate Constraints
In ternary plots, the three coordinates always sum to a constant (e.g., 1 for normalized ratios, 100 for percentages). This means we can reduce the problem to 2D by expressing one variable in terms of the other two (e.g., z = total - x - y, where total is your sum constant).
3. Parameterize Both the Curve and Segment
To find the intersection, we'll define continuous functions for both the smooth curve and your segment using parameterization:
Parameterize the Smooth Curve
We'll use a parameter s (ranging from 0 to 1) to represent positions along the smooth curve, then create interpolation functions for x and y:
# Create a sequence of parameter values for the smooth curve s_vals <- seq(0, 1, length.out = nrow(smooth_curve_data)) # Build interpolation functions for x and y as functions of s x_smooth <- approxfun(s_vals, smooth_curve_data$x) y_smooth <- approxfun(s_vals, smooth_curve_data$y)
Parameterize the Segment
For your segment, use a parameter t (ranging from 0 to 1, where 0 is the start and 1 is the end) to define its x/y coordinates:
# Replace these with your actual segment start/end values x_start <- your_segment_data$x_start y_start <- your_segment_data$y_start x_end <- your_segment_data$x_end y_end <- your_segment_data$y_end # Segment parameterization functions x_segment <- function(t) x_start + t * (x_end - x_start) y_segment <- function(t) y_start + t * (y_end - y_start)
4. Solve for the Intersection
We need to find values of s and t where the x/y coordinates of the curve and segment match. We'll use a root-finding function from the rootSolve package to solve this system of equations:
library(rootSolve) # Define an error function that returns the difference between curve and segment coordinates error_function <- function(st_params) { s <- st_params[1] t <- st_params[2] c( x_smooth(s) - x_segment(t), y_smooth(s) - y_segment(t) ) } # Initial guess for s and t (adjust if needed for your specific plot) initial_guess <- c(0.5, 0.5) # Solve for the intersection parameters solution <- multiroot(error_function, start = initial_guess) # Extract the solution values s_solution <- solution$root[1] t_solution <- solution$root[2]
5. Calculate and Validate the Intersection Point
Finally, compute the actual intersection coordinates and check if it lies within your segment's bounds (t must be between 0 and 1):
# Calculate intersection coordinates total <- 100 # Replace with your coordinate sum (e.g., 1 if normalized) intersect_x <- x_segment(t_solution) intersect_y <- y_segment(t_solution) intersect_z <- total - intersect_x - intersect_y # Validate if the intersection is on the segment if (t_solution >= 0 & t_solution <= 1) { cat("Intersection found:\n") cat(sprintf("NaCl: %.4f, H2O: %.4f, Na2SO4: %.4f\n", intersect_x, intersect_y, intersect_z)) } else { cat("No valid intersection within the segment's range.\n") }
Key Notes
- Normalization: Ensure your ternary coordinates sum to a consistent constant before starting—this simplifies the math drastically.
- Multiple Intersections: If your segment crosses the smooth curve more than once, you may need to test different initial guesses for
sandtto find all solutions. - Approximation Alternative: For a simpler (but slightly less precise) method, you can treat the smooth curve as a series of straight lines between its fitted points, then calculate intersections between each of those lines and your segment.
内容的提问来源于stack exchange,提问作者Christian S.

