knitr::opts_chunk$set( collapse = TRUE, warning = FALSE, message = FALSE, comment = "#>" )
#| echo: false library(prefviz) library(ggplot2) library(tourr) library(dplyr)
Adding paths between points in a ternary space can help illustrate transitions or sequences in the data.
In the example of instant-runoff voting in Australian Federal Election, these paths can be used to illustrate the change in preference distribution between rounds of voting in one electorate. This vignette shows how to add paths to both 2D and high-dimensional ternary plots.
Take the example of the 2022 Australian Federal Election, we would like to take a look at the changes in preference distribution across rounds in three electorates: Higgins, Monash, and Melbourne.
Input data is already in a ternable-friendly format, with each row representing the preference distribution in one round of voting in one electorate.
input_df <- prefviz::aecdop22_transformed |> filter(DivisionNm %in% c("Higgins", "Monash", "Melbourne")) head(input_df) # Create ternable object tern22 <- as_ternable(input_df, ALP:Other)
For 2D ternary plots, adding ordered paths is straightforward using the stat_ordered_path() function, and providing the order_by aesthetic. This is equivalent to geom_path() when you pre-order your data points in the order you want them to be connected.
In this case, we want to connect the points in the order of round (order_by = CountNumber) for each electorate (group = DivisionNm).
# The base plot p <- get_tern_data(tern22, plot_type = "2D") |> ggplot(aes(x = x1, y = x2)) + add_ternary_base() + geom_ternary_region( aes(fill = after_stat(vertex_labels)), vertex_labels = tern22$vertex_labels, alpha = 0.3, color = "grey50", show.legend = FALSE ) + geom_point(aes(color = ElectedParty)) + add_vertex_labels(tern22$simplex_vertices) + scale_color_manual( values = c("ALP" = "red", "LNP" = "blue", "Other" = "grey70"), aesthetics = c("fill", "colour") ) # Add ordered paths p + stat_ordered_path( aes(group = DivisionNm, order_by = CountNumber, color = ElectedParty), size = 0.5 )
It's interesting to see such a close win of ALP in Higgins (red line) since the result only flipped in the last round. Meanwhile, wins in Monash and Melbourne were consistent with first preference.
Let's take a look at Monash and Melbourne in 2025 election, but this time we will use a high-dimensional ternary plot with 5 parties: ALP, LNP, GRN, IND, and Other.
Unlike 2D ternary plots, we need to group and order the data before plotting by providing the group and order_by arguments when creating ternable object. Since this data is already ordered by round in the input data frame, we just need to provide the grouping variable (DivisionNm).
input_df2 <- prefviz::aecdop25_transformed |> filter(DivisionNm %in% c("Monash", "Melbourne")) head(input_df2) # Create ternable object tern25 <- as_ternable(input_df2, ALP:IND, group = DivisionNm)
Adding paths in a high-dimensional ternary plot is made possible by the edges arguments in display_xy(). Therefore, besides simplex edges, we need to provide the data edges to the edges argument. This can be done by setting include_data = TRUE in get_tern_edges().
# Add colors party_colors <- c( "ALP" = "#E13940", # Red "LNP" = "#1C4F9C", # Blue "GRN" = "#10C25B", # Green "IND" = "#F39C12", # Orange "Other" = "#95A5A6" # Gray ) color_vector <- c(rep("black", 5), party_colors[input_df2$ElectedParty]) edges_color <- c(rep("black", nrow(tern25$simplex_edges)), party_colors[input_df2$ElectedParty]) # Animate the tour animate_xy( get_tern_data(tern25, plot_type = "HD"), col = color_vector, edges = get_tern_edges(tern25, include_data = TRUE), edges.col = edges_color, obs_labels = get_tern_labels(tern25), axes = "bottomleft" )
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.