Skip to contents

ggPSE is a ggplot2-based toolkit for visualizing element-, group- or period-resolved data on the periodic table of elements. Every function returns a standard ggplot2 object - so plots can be extended with themes, annotations, faceting, and patchwork.


Installation

# Install from GitHub (requires remotes)
remotes::install_github("VJGeiger/ggPSE")

If you want to plot with country flag badges 🇺🇸 🇮🇹 🇬🇧, also install ggflags (not on CRAN, but GitHub):

remotes::install_github("rensa/ggflags")

Quick start

library(ggPSE)

# Blank periodic table
ggPSE()


Visualization modes

Numerated Tiles

The numeration can be switched on with show_atomic_number = TRUE, default is FALSEto avoid crowded tiles.

ggPSE(show_atomic_number = TRUE)

### Tile borders

Adapt the tile coloring as well as its borders for the whole PSE.

ggPSE(color_na = "white", tile_border = "gray40")

Show Selection

If you are interested ind displaying only a part of the PSE you can specify this via the periods and groups as a default lanthanides and actinides are always set to be displayed and have to be actively omitted by using lanthanides = FALSE.

# Periods 4-6 with lanthanides
ggPSE(periods = c(4, 6), groups = c(3,4), lanthanides= FALSE, actinides = FALSE)

Highlight elements

Color named subsets of elements with a legend entry per group.

ggPSE(highlight = list(
  list(symbols = c("Li", "Co", "Ni", "Mn", "Fe", "P"),
       color   = "#2166ac",
       label   = "Battery metals"),
  list(symbols = c("Nd", "Dy", "Tb", "Pr", "La", "Ce"),
       color   = "#e63946",
       label   = "Rare earth magnets")
))


Built-in element data

ggPSE ships with ggpse_elements, a data frame of 17 physical and chemical properties for all 118 elements (IUPAC 2021, NIST WebBook, Pauling 1960):

data(ggpse_elements)
ggPSE(ggpse_elements, value_col = "electronegativity",
      legend_title = "Electronegativity (Pauling)")


Data visualization

Color Gradient

Map any numeric variable to a fill color gradient across all elements.

battery <- data.frame(
  symbol     = c("Li", "Co", "Ni", "Mn", "Fe", "P", "C"),
  importance = c(0.95, 0.88, 0.75, 0.60, 0.45, 0.40, 0.35)
)

ggPSE(battery,
      value_col    = "importance",
      color_low    = "#f7fbff",
      color_high   = "#08306b",
      legend_title = "Battery relevance")

The mapping can also be applied to a selection of the PSE.

data(ggpse_elements)
ggPSE(ggpse_elements[ggpse_elements$block == "d", ],
      value_col = "density_g_cm3", legend_title = "Density (g/cm3)")

It is also possible to customize the ggplots to multigradient scales for better visualization.

data(ggpse_elements)

ggPSE(ggpse_elements,
      value_col    = "density_g_cm3",
      color_low    = "#2166ac",   
      color_high   = "#a50026",   
      legend_title = "Density (g/cm³)") +
  ggplot2::scale_fill_gradientn(
    colours  = c("#2166ac", "#1a9850", "#a50026"),
    na.value = "#f0f0f0",
    name     = "Density (g/cm³)"
  ) +
  ggplot2::labs(title = "Elemental density across the periodic table") +
  ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, face = "bold"))

Bubble plot

Map a numeric variable to bubble size.

supply_risk <- data.frame(
  symbol = c("Li", "Co", "Nd", "Dy", "Pt", "Rh", "In", "Ga"),
  risk   = c(0.62, 0.91, 0.84, 0.88, 0.52, 0.70, 0.78, 0.65)
)
ggPSE_bubble(supply_risk, size_col = "risk", legend_title = "Supply risk")

In-tile bar encoding

Draw a proportional bar inside each tile growing from a chosen edge ("bottom", "top", "left", or "right").

ggPSE_bar(supply_risk, value_col = "risk",
          bar_side  = "bottom",
          bar_color = "#2166ac",
          legend_title = "Supply risk")


Donut Charts

One can add donuts per field to display distributions with element resolution. With the plot_all =TRUE it is optional to display all elements even those without data.

supply <- data.frame(
   symbol   = c("Li","Li","Li", "Co","Co", "Nd","Nd", "Pt"),
   category = c("CL","AU","AR", "CD","AU", "CN","RU", "ZA"),
   share    = c(0.60, 0.30, 0.10, 0.70, 0.30, 0.80, 0.20, 1.00)
 )

ggPSE(groups = c(3,12)) |>
gPSE_donut(supply, category_col = "category", share_col = "share", plot_all = FALSE)


Period and group profiles

The display of selected periods and rows with corresponding data can be achieved via the ggPSE_profile() function. Elements with no values are omitted.

ggPSE_profile(ggpse_elements, value_col = "electronegativity", period = 6)

# or

ggPSE_profile(ggpse_elements, value_col = "electronegativity", group = 12)

# or

ggPSE_profile(ggpse_elements, value_col = "electronegativity", lanthanides = TRUE)


Country-of-origin badges

Overlay country flags (via ggflags) or ISO 3166-1 alpha-2 text codes. Up to three flags per tile are supported. These tiles can be displayed with a hierarchy (1st-2nd-3rd “place”). A legend is optional and can be positioned around the plot.

supply <- data.frame(
  symbol  = c("Li", "Co", "Nd", "Pt"),
  country = c("CL", "CD", "CN", "ZA"),
  value   = c(0.70, 0.91, 0.85, 0.60)
)
ggPSE(supply, value_col = "value", legend_title = "Criticality") |>
  ggPSE_badge(supply, country_col = "country")

supply_multi <- data.frame(
  symbol  = c("Li", "Li", "Co", "Co", "Co", "Nd", "Pt"),
  country = c("CL", "AU", "CD", "AU", "ZM", "CN", "ZA"),
  value   = c(0.70, 0.70, 0.91, 0.91, 0.91, 0.85, 0.60)
)

ggPSE(supply_multi, value_col = "value",
      color_high = "#b2182b", legend_title = "Criticality") |>
  ggPSE_badge(supply_multi, country_col = "country")
  
 ggPSE(supply_multi, value_col = "value") |>
     ggPSE_badge(supply_multi, country_col = "country",
                 hierarchy = TRUE, hierarchy_scale = c(1.0, 0.6, 0.5), show_legend = TRUE, legend_position = "bottom")

Interactive plots

The displayed values can be made into interactive plots with ggPSE_interactive() 👉 Interactive periodic table example

ggPSE_interactive(battery, value_col = "importance")

# or 

ggPSE_interactive(
  ggpse_elements,
  value_col    = "electronegativity",
  tooltip_cols = c("name", "atomic_weight", "block", "electronegativity"),
  legend_title = "Electronegativity (Pauling)"
)


Themes

There are four prebuilt themes available via ggPSE_theme(). | Theme | Description | |—|—| | "minimal" | White background, simple| | "dark" | Dark plot background | | "publication" | Minimal ink, transparent background, journal-ready | | "crazy" | Random color per tile, reproducible with seed |

# Minimal
ggPSE() |> ggPSE_theme("minimal")

# Publication
ggPSE(ggpse_elements, value_col = "electronegativity") |>
  ggPSE_theme("publication")

# Crazy — reproducible
ggPSE() |> ggPSE_theme("crazy", seed = 42)


Saving figures

Figures can be saved via the wrapper ggPSE_save() for ease. For more custom plots, ggsave() can be used as well.

data(ggpse_elemts)
p <- ggPSE(ggpse_elements, value_col = "electronegativity")

ggPSE_save(p, "electronegativity.png")   # PNG 18x14cm at 400dpi
ggPSE_save(p, "electronegativity.pdf")   # vector PDF
ggPSE_save(p, "electronegativity.svg")   # web SVG
ggPSE_save(p, "electronegativity.png", width = 24, height = 9, dpi = 600)

Experimental features

ggPSE_sparkline() and ggPSE_spark_bar() per-tile time series sparklines was deemed too unstable and in need of further development.

Available on the feature/sparkline branch:

remotes::install_github("VJGeiger/ggPSE@feature/sparkline")

Roadmap

See TODO for roadmap and what’s ahead.


Contributing

Issues and pull requests welcome. Please open an issue before starting significant work so we can discuss the approach first.


Alternatives

R alternatives

python alternatives

  • mendeelv: Rich-dataset, feature-rich and optional interactive visualization.

Citation

If you use ggPSE in a publication, please cite it as:

Valentin Geiger (2026). ggPSE: Periodic Table Visualization with ggplot2.
R package version 0.2.0. https://github.com/VJGeiger/ggPSE

Acknowledgement

This package was developed with assistance from Claude (Anthropic), including function design, debugging, documentation, and iterative development throughout the entire codebase.