polygonPlot

Davide Corso

University of Naples Federico II
davidecrs92@gmail.com

Fang Huang

CSIRO Mineral Resources, Australia
fanghuang007@gmail.com

Anirudh Prabhu

Carnegie Institution for Science
aprabhu@carnegiescience.edu

Donato Giovannelli

University of Naples Federico II
donato.giovannelli@unina.it

2025-10-24


Introduction

polygonPlot is an R package for visualizing multivariate ranges using polygon-based representations. Unlike traditional scatter or radar plots that display individual data points, polygonPlot allows users to display min–max intervals and mean values across 3 to 6 variables simultaneously.

Installation

You can install polygonPlot with the following code:

devtools::install_github("giovannellilab/polygonPlot")

Usage

Libraries

library(polygonPlot)
library(ggplot2)

Data Input Formats

There are two types of input accepted by the polygonPlot function.

  1. The expected data.frame format of the function
  2. Classic data.frame format.

Just be sure that the input object is data.frame type.

In both cases, the function returns a ggplot2 object, which includes the mean points of each axis connected by lines.

1. Expected data.frame format structure

the input data.frame should be composed as follows:

  • The first column should contain the strings axis_min, axis_max and data in that row order.
  • All the columns, starting from the second, must be dedicated to the variables of interest. In particular, the first two rows should contain the values for the axis_min and axis_max respectively.
    • If left empty, the axis_min/max will be automatically calculated based on data given and extension will be decided based on the Axes range extension (extra parameter).
  • Starting from the third row, each variable column must contain all the data values.
  • As default, column names will be used as labels of the axis (starting from the second one). Change them in order to change the labels, but don’t leave them empty.

Examples of an input data.frame

df <- read.csv(system.file(file.path("extdata", "example.csv"), package="polygonPlot"))
df
#>        info data1 data2 data3 data4 data5 data6
#> 1  axis_min  -300    -4     0     0    NA  -0.5
#> 2  axis_max   500    14  1000    50    NA   0.5
#> 3      data   300    -2    10    20     5   0.1
#> 4             320    12    50    40    10   0.3
#> 5             340    NA    90    NA    NA    NA
#> 6             360    NA   130    NA    NA    NA
#> 7             380    NA   170    NA    NA    NA
#> 8             400    NA   210    NA    NA    NA
#> 9             420    NA    NA    NA    NA    NA
#> 10            440    NA    NA    NA    NA    NA
#> 11            460    NA    NA    NA    NA    NA

a minimal version


df <- read.csv(
  system.file(
    file.path(
      "extdata",
      "minimal_example.csv"
    ), 
    package="polygonPlot"
  )
)

df
#>       info data1 data2 data3 data4 data5 data6
#> 1 axis_min  -300    -4     0     0    NA  -0.5
#> 2 axis_max   500    14  1000    50    NA   0.5
#> 3     data   300    -2    10    20     5   0.1
#> 4            460    12   270    40    10   0.3

2. Classic data.frame structure

If you have a classic data.frame, you can prepare it with the function that we have created

classic_df <- data.frame(
  data1=c(-300, 500, 300, 350),
  data2=c(4, 14, -2, 12),
  data3=c(0, 1000, 10, 50),
  data4=c(0, 50, 20, 40)
)

formatted_df <- polygonPlot::prepare_dataframe(classic_df)

# Through this function it becomes the first type of input
formatted_df
#>       info data1 data2 data3 data4
#> 1 axis_min    NA    NA    NA    NA
#> 2 axis_max    NA    NA    NA    NA
#> 3     data  -300     4     0     0
#> 4     <NA>   500    14  1000    50
#> 5     <NA>   300    -2    10    20
#> 6     <NA>   350    12    50    40

Polygons

Input Parameters

Required

The polygonPlot function requires two mandatory parameters:

  • df: the input data.frame that should be composed as follows:
    • The first column should contain the strings axis_min, axis_max and data in that row order.
    • All the columns, starting from the second, must be dedicated to the variables of interest. In particular, the first two rows should contain the values for the axis_min and axis_max respectively.
      • If left empty, the axis_min/max will be automatically calculated based on data given and extension will be decided based on the Axes range extension (extra parameter).
    • Starting from the third row, each variable column must contain all the data values.
    • As default, column names will be used as labels of the axis (starting from the second one). Change them in order to change the labels, but don’t leave them empty.
  • shape: the integer value indicating the shape of the polygon. Available numbers:
    • 3 for Triangle
    • 4 for Square
    • 5 for Pentagon
    • 6 for Hexagon

Optional

  • extra: axis range extension
  • fillcolor fill color of the polygon
  • alpha alpha value of the fill color
  • linecolor line color of the polygon border
  • linetype line type of the polygon border
  • lwd line width of the polygon border
  • labels_axis vector with the desired labels of the axis
  • mean_line_color color of the mean line inside the polygon
  • annotation_tick_size size of tick marks along the polygon axes
  • annotation_label_size size of the labels displayed along the polygon axes
  • title title of the plot
  • fix_aspect_ratio boolean flag to fix the aspect ratio of the plot as 1. It is strongly recommended to leave it as default value TRUE.
    • NOTE: If you are going to change the theme of the returned ggplot object, remember to put in the theme function the following code aspect.ratio = 1 in order to keep the text and the relative ticks aligned on the axis.

See the section About the aspect.ratio for a practical example.

Shape: 3 - Triangle

plot_triangle <- polygonPlot(df, shape = 3, fillcolor = "#e56b6f", linecolor = "#b56576",
                             labels_axis = c("earth", "moon", "sun"))
#> Perimeter: 24.76
#> Area:      52.96
plot_triangle

Shape: 4 - Square

plot_square <- polygonPlot(df, shape = 4, fillcolor = "#57cc99", linecolor = "#38a3a5", annotation_label_size = 8)
#> Perimeter: 32.76
#> Area:      76.36
plot_square

Shape: 5 - Pentagon

plot_pentagon <- polygonPlot(df, shape = 5, fillcolor = "#c89f9c", linecolor = "#b36a5e")
#> Perimeter: 42.76
#> Area:      109.70
plot_pentagon

Shape: 6 - Hexagon

plot_hexagon <- polygonPlot(df, shape = 6, fillcolor = "#0077b6", linecolor = "#023e8a", 
                            annotation_label_size = 8)
#> Perimeter: 46.76
#> Area:      180.02
plot_hexagon

About the aspect.ratio

Since polygonPlot returns a ggplot2 object, the aspect ratio must be fixed to preserve the geometry of the polygon.
When applying a custom theme, always include theme(aspect.ratio = 1) to maintain alignment between axes and labels.

plot_pentagon <- polygonPlot(df, shape = 5, fillcolor = "#c89f9c", linecolor = "#b36a5e")
#> Perimeter: 42.76
#> Area:      109.70
plot_pentagon + theme_bw() + theme(aspect.ratio = 1)

Overlay multiple polygons in one figure

To perform this task, the polygons must be pre-generated, each sharing the same minimum and maximum values of the axes. This ensures that all polygons are drawn on a consistent scale, allowing for direct and meaningful visual comparison between datasets.

axis_min <- c(-300, -4, 0, 0, NA, -0.5)
axis_max <- c(500, 14, 1000, 50, NA, 0.5)

df1 <- data.frame(
  info   = c("axis_min", "axis_max", "data", "data", "data"),
  data1  = c(axis_min[1], axis_max[1], 300, 460, 120),
  data2  = c(axis_min[2], axis_max[2], -2, 12, 4),
  data3  = c(axis_min[3], axis_max[3], 10, 270, 80),
  data4  = c(axis_min[4], axis_max[4], 20, 40, 15),
  data5  = c(axis_min[5], axis_max[5], 5, 10, 3),
  data6  = c(axis_min[6], axis_max[6], 0.1, 0.3, -0.2)
)

df2 <- data.frame(
  info   = c("axis_min", "axis_max", "data", "data", "data"),
  data1  = c(axis_min[1], axis_max[1], 250, 380, 490),
  data2  = c(axis_min[2], axis_max[2], -3, 8, 10),
  data3  = c(axis_min[3], axis_max[3], 30, 200, 850),
  data4  = c(axis_min[4], axis_max[4], 10, 30, 45),
  data5  = c(axis_min[5], axis_max[5], 2, 6, 8),
  data6  = c(axis_min[6], axis_max[6], -0.1, 0.25, 0.4)
)

df1
#>       info data1 data2 data3 data4 data5 data6
#> 1 axis_min  -300    -4     0     0    NA  -0.5
#> 2 axis_max   500    14  1000    50    NA   0.5
#> 3     data   300    -2    10    20     5   0.1
#> 4     data   460    12   270    40    10   0.3
#> 5     data   120     4    80    15     3  -0.2

df2
#>       info data1 data2 data3 data4 data5 data6
#> 1 axis_min  -300    -4     0     0    NA -0.50
#> 2 axis_max   500    14  1000    50    NA  0.50
#> 3     data   250    -3    30    10     2 -0.10
#> 4     data   380     8   200    30     6  0.25
#> 5     data   490    10   850    45     8  0.40
p1 <- polygonPlot(df1, shape=4, fillcolor = "darkgreen", alpha=0.5, linecolor = "darkgreen", mean_line_color = "blue")
#> Perimeter: 39.26
#> Area:      90.06

p1

p2 <- polygonPlot(df2, shape=4, fillcolor = "orange", alpha=0.5, linecolor = "orange", mean_line_color = "red")
#> Perimeter: 50.84
#> Area:      234.58

p2

Now that we have created the two plots, we can overlay them in a single chart. To do this, we need to define: - a list containing the plots - the corresponding labels

The parameters for the overlay_polygons function are:

plt_list <- list(p1,p2)
plt_labels <- c("Example1", "Example2")

overlay_polygons(plt_list, plt_labels, include_mean_lines = T)

Even with three plot we can see the differences. However, too many plots may provide a difficult visualization.

axis_min <- c(-300, -4, 0, 0, NA, -0.5)
axis_max <- c(500, 14, 1000, 50, NA, 0.5)

# Terzo dataframe
df3 <- data.frame(
  info   = c("axis_min", "axis_max", "data", "data", "data"),
  data1  = c(axis_min[1], axis_max[1], 100, 250, 400),
  data2  = c(axis_min[2], axis_max[2], -1, 6, 10),
  data3  = c(axis_min[3], axis_max[3], 50, 300, 700),
  data4  = c(axis_min[4], axis_max[4], 5, 25, 35),
  data5  = c(axis_min[5], axis_max[5], 4, 8, 9),
  data6  = c(axis_min[6], axis_max[6], 0.0, 0.2, 0.45)
)

p3 <- polygonPlot(df3,   shape=4, fillcolor = "dodgerblue", alpha = 0.5, linecolor = "dodgerblue", mean_line_color = "yellow")
#> Perimeter: 44.72
#> Area:      158.28
p3


plt_list <- list(p1,p2,p3)
plt_labels <- c("Example1", "Example2", "Example3")

overlay_polygons(plt_list, plt_labels, include_mean_lines = T)

Citation

Paper in preparation…

sessionInfo

Session info
#> [1] "2025-10-24 17:22:59 CEST"
#> [1] "2025-10-24"
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Tahoe 26.0.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: Europe/Rome
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggplot2_3.5.2     polygonPlot_1.1.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] vctrs_0.6.5        cli_3.6.5          knitr_1.50         rlang_1.1.6       
#>  [5] xfun_0.52          generics_0.1.4     jsonlite_2.0.0     labeling_0.4.3    
#>  [9] glue_1.8.0         backports_1.5.0    htmltools_0.5.8.1  pracma_2.4.4      
#> [13] sass_0.4.10        scales_1.4.0       rmarkdown_2.29     grid_4.5.1        
#> [17] evaluate_1.0.4     jquerylib_0.1.4    tibble_3.3.0       fastmap_1.2.0     
#> [21] yaml_2.3.10        lifecycle_1.0.4    compiler_4.5.1     dplyr_1.1.4       
#> [25] RColorBrewer_1.1-3 pkgconfig_2.0.3    rstudioapi_0.17.1  farver_2.1.2      
#> [29] digest_0.6.37      R6_2.6.1           dichromat_2.0-0.1  tidyselect_1.2.1  
#> [33] pillar_1.10.2      magrittr_2.0.3     bslib_0.9.0        checkmate_2.3.3   
#> [37] withr_3.0.2        gtable_0.3.6       tools_4.5.1        cachem_1.1.0