-7

I want to make a triangle plot represents the response surface for all possible combinations of X, Y and Z factors and the gradient area inside the triangle expresses the response variable Gi. The dots represent the ten combinations of X, Y and Z in data frame dt. The correlation between Gi and (X, Y, Z) is defined as mdl <- lm (Gi ~ X*Y*Z). Here is the data and what I have tried:

X <- rep(c(.45,.4,.55,.4,.43,.5,.43,.5,.43,.48), each = 3)
Y <- rep(c(.15, .12,.22,.14,.14,.19,.12, .17,.17,.12 ), each = 3)
Z <- rep(c(.15,.22,.12,.12,.19,.14,.14,.17,.12,.17), each = 3)
Gi <- c(353,381,320,312,335,265,394,350,374,320,299,316,300,304,295,360,331,395,351,280,342,299,303,279,374,364,419,306,290,315)

dt <- data.frame (X, Y, Z, Gi)
ggtern(data = dt, aes(x = X, y = Y, z = Z, value = Gi)) +
  stat_interpolate_tern(geom="polygon",
                        formula = value ~ x+y, 
                        method = lm,
                        aes(fill = ..level..), expand = 1) +
  scale_fill_gradient(low="green", high="blue") +
  geom_point (fill = "white", size = 3, shape = 21, color = "white") +
  theme_gray () +
  theme ( tern.axis.arrow.show = T)

However, the output is not what I am looking for. I found an exciting example of a ternary plot heatmap using Python, which is like what I want. However, I am only familiar with R, and I'd like to make something similar. How could I do this in R?

Please find the link for the Python code that inspired me.

enter image description here

Hoang Le
  • 308
  • 1
  • 3
  • 14
  • 1
    I suggest looking at the `ggtern` package as a nice way to make ternary plots in R. – Jon Spring Oct 26 '21 at 20:20
  • 4
    Hi Hoang Le. The answer is "yes" - we can _help_ you to translate the code into R. Where did you get stuck in your own attempt? Please remember that Stack Overflow is a Q&A site where folk can come to search for an answer to a specific coding problem - it is not a bespoke code-writing service for individual projects. – Allan Cameron Oct 26 '21 at 20:23
  • Dear @Allan, thanks for your very kind reminder! As mentioned in the answer to Michael Shomsky, I have studied ggtern and a lot of search to to make a plot like that. I get stuck at the principle under the code and want to understand it and can reproduce in R on my own data. If the community doesn’t serve coding service, please help me to understand the principle inside the code. Thanks and regards! – Hoang Le Oct 26 '21 at 20:40
  • I'm voting to reopen now that there's an example and a specific question, but you should spell out exactly what makes your code differ from what you want – camille Nov 11 '21 at 15:59

2 Answers2

2

There is the following R project that can make Ternary plots: https://cran.r-project.org/web/packages/Ternary/vignettes/Ternary.html

It is possible asking the question differently like "Is there an R library that can help me plot like this python library?" Would be better received, as converting a library from one language to another is intense.

michael shomsky
  • 130
  • 1
  • 10
  • 1
    Thanks for your answer and I have spent three days to use ggtern and a lots of searching, and worked on my data to make a plot like that but it has not worked. It is not I am lazy to self-study. It is just I am not able to figure out the principle behind the task in the give link YET. I want to understand the principle how the response variable was calculated/converted based on the three parameters x, y, and z from an example. I believe it is also a way to learn a new thing, isn’t it?. Is that worth to get help from the community? Thanks! – Hoang Le Oct 26 '21 at 20:33
  • Yes, rephrasing the question by sharing what doesn't work is the best way to get help for your question. I would suggest doing one of 2 things: 1.) share your non-working ggtern plot with code and some sample data and let the community help .. 2.) share your exploration in trying to intepret the python example and see where the community can help. – michael shomsky Oct 26 '21 at 20:47
  • 1
    If you want to understand the math behind mapping 3-way mixture data into ternary coordinates, the wikipedia article explains it: https://en.wikipedia.org/wiki/Ternary_plot#Plotting_a_ternary_plot. if a at the origin, b at (1,0) and c at (0.5, 0.877), then x = 0.5 * (2b+c)/total, y = (sqrt(3)/2) * c/total, which might make more sense looking at the diagrams and thinking about the geometry. – Jon Spring Oct 26 '21 at 23:45
  • 1
    This question is closed now but I can give some more examples in the comments. Here's some useful libraries and sample data to plot: `library(tidyverse) set.seed(0); df <- data.frame(a = runif(20, 0, 1), b = runif(20, 0, 1), c = runif(20, 0, 1)) %>% mutate(across(a:c, ~round(.x, 2))); edges <- data.frame(x = c(0, 1, 0.5, 0), y = c(0, 0, sqrt(3)/2, 0))` – Jon Spring Oct 27 '21 at 00:02
  • 1
    Here's a little function to take a data frame with a, b, c columns and convert them to x and y, using the a/b/c order used in the wikipedia article above. `ternary_translate <- function(df) { df %>% mutate(total = a + b + c, x = 0.5 * (2*b + c) / total, y = sqrt(3)/2 * c / total) }` – Jon Spring Oct 27 '21 at 00:04
  • And finally, here's some code to apply the translation to the sample data and to plot it. `df %>% ternary_translate() %>% mutate(label = paste(a,b,c, sep = ",")) %>% ggplot(aes(x, y)) + geom_path(data = edges) + geom_point() + geom_text(aes(label = label), size = 2, vjust = -0.5, check_overlap = TRUE)` – Jon Spring Oct 27 '21 at 00:04
  • Is it specifically the "heatmap" part that you are struggling with, or how a ternary plot maps mixtures to coordinates, or something else? It will be easier to help if you can be very specific about what you have tried and what you want. – Jon Spring Oct 27 '21 at 00:33
0

Here's an example script using the Ternary package, following its interpolation example:

X <- rep(c(.45,.4,.55,.4,.43,.5,.43,.5,.43,.48), each = 3)
Y <- rep(c(.15, .12,.22,.14,.14,.19,.12, .17,.17,.12 ), each = 3)
Z <- rep(c(.15,.22,.12,.12,.19,.14,.14,.17,.12,.17), each = 3)
Gi <- c(353,381,320,312,335,265,394,350,374,320,299,316,300,304,295,360,331,395,351,280,342,299,303,279,374,364,419,306,290,315)

library("Ternary")

# Start a plot, to define the coordinate system
par(mar = rep(0.2, 4))
TernaryPlot(alab = "fat", blab = "lactose", clab = "protein")

abc <- rbind(X, Y, Z)
# Convert measured points to XY
xy <- TernaryToXY(abc)

# Use an inverse distance weighting to interpolate between measured points
Predict <- function(predXY) {
  Distance <- function(a, b) {
    apply(a, 2, function(pt) sqrt(colSums((pt - b) ^ 2)))
  }
  dists <- Distance(xy, predXY)
  id <- 1 / dists
  idw <- id / rowSums(id)
  
  # Return:
  colSums(Gi * t(idw))
}

# Predict at triangle centres
tri <- TriangleCentres(resolution = 12L)
# Adjust the resolution to suit your dataset

# Now we interpolate between our known values to generate a colour for each
# of our tiles
predicted <- Predict(tri[1:2, ])
map <- rbind(x = tri["x", ], y = tri["y", ], z = predicted,
             down = tri["triDown", ])

# Place a semitransparent colour fill over grid lines:
ColourTernary(map)

# Print legend for colour scale
TreeTools::SpectrumLegend(
  x0 = 0.85,
  y0 = 0.6,
  title = "Gi",
  legend = signif(seq(min(Gi), max(Gi), length.out = 4), 3),
  palette = viridisLite::viridis(256L, alpha = 0.6),
  xpd = NA # Do not clip at edge of figure
)

# Calculate contours
PredictABC <- function(a, b, c) Predict(TernaryToXY(rbind(a, b, c)))
TernaryContour(PredictABC, resolution = 36L)

# Mark the location of our data points
TernaryPoints(abc, pch = 3, col = "#cc3333")

Ternary output

Martin Smith
  • 3,687
  • 1
  • 24
  • 51