Rainflow Counting Matrix for Fatigue Analysis — ggplot2

A rainflow counting matrix visualizes the results of rainflow cycle counting from a load or stress time history. The matrix displays cycle counts as a 2D heatmap where one axis represents cycle amplitude (half-range), the other represents cycle mean value, and color intensity represents the frequency of each cycle combination. This is a fundamental tool in fatigue analysis and durability engineering, used to characterize variable-amplitude loading for fatigue life prediction.

Rainflow Counting Matrix for Fatigue Analysis rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' heatmap-rainflow: Rainflow Counting Matrix for Fatigue Analysis
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 87/100 | Created: 2026-06-02

library(ggplot2)
library(dplyr)
library(scales)
library(ragg)

set.seed(42)

# Theme tokens (Imprint palette, theme-adaptive chrome)
THEME       <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG     <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420"
INK         <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT    <- if (THEME == "light") "#4A4A44" else "#B8B7B0"

# Data: synthetic rainflow counting results for a structural steel component
# Amplitude = half stress range (MPa), Mean = mean stress (MPa)
n_bins       <- 20
amp_centers  <- seq(5, 100, length.out = n_bins)
mean_centers <- seq(-40, 140, length.out = n_bins)

df <- expand.grid(amp_mpa = amp_centers, mean_mpa = mean_centers) %>%
  mutate(
    # Steeper amplitude decay + tighter Gaussian → concentrated hot-spot with
    # large zero-count regions at high amplitude and extreme mean values
    raw        = 3000 * exp(-amp_mpa / 15) * exp(-0.5 * ((mean_mpa - 60) / 25)^2),
    count      = as.integer(pmax(0, round(raw + abs(rnorm(n(), 0, raw * 0.1 + 5))))),
    count_plot = if_else(count <= 2L, NA_real_, as.double(count))
  ) %>%
  select(-raw)

# Contour threshold at 25% of peak to outline the dominant fatigue cycle zone
contour_at <- max(df$count, na.rm = TRUE) * 0.25

# Plot
title_str <- "heatmap-rainflow · r · ggplot2 · anyplot.ai"

p <- ggplot(df, aes(x = mean_mpa, y = amp_mpa, fill = count_plot)) +
  geom_tile() +
  geom_contour(aes(z = count), color = INK_SOFT, linewidth = 0.4,
               breaks = contour_at, alpha = 0.7) +
  scale_fill_gradient(
    low            = "#009E73",
    high           = "#4467A3",
    na.value       = PAGE_BG,
    name           = "Cycle Count",
    trans          = "sqrt",
    labels         = scales::comma,
    guide          = guide_colorbar(
      barheight      = unit(10, "lines"),
      title.position = "top"
    )
  ) +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 6)) +
  scale_y_continuous(breaks = scales::pretty_breaks(n = 6)) +
  labs(
    title = title_str,
    x     = "Mean Stress (MPa)",
    y     = "Stress Amplitude (MPa)"
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background   = element_rect(fill = PAGE_BG,     color = PAGE_BG),
    panel.background  = element_rect(fill = PAGE_BG,     color = NA),
    panel.grid        = element_blank(),
    panel.border      = element_blank(),
    axis.line         = element_line(color = INK_SOFT,   linewidth = 0.5),
    axis.title        = element_text(color = INK,        size = 10),
    axis.text         = element_text(color = INK_SOFT,   size = 8),
    plot.title        = element_text(color = INK,        size = 12, hjust = 0.5),
    legend.background = element_rect(fill = ELEVATED_BG, color = NA),
    legend.text       = element_text(color = INK_SOFT,   size = 8),
    legend.title      = element_text(color = INK,        size = 10),
    plot.margin       = margin(20, 20, 20, 20)
  )

# Save
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot     = p,
  device   = ragg::agg_png,
  width    = 6,
  height   = 6,
  units    = "in",
  dpi      = 400
)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/heatmap-rainflow/ggplot2/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "heatmap-rainflow",
  "language": "r",
  "library": "ggplot2",
  "page": "https://anyplot.ai/heatmap-rainflow/r/ggplot2",
  "hub": "https://anyplot.ai/heatmap-rainflow",
  "code_json": "https://api.anyplot.ai/specs/heatmap-rainflow/ggplot2/code",
  "spec_json": "https://api.anyplot.ai/specs/heatmap-rainflow",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-rainflow/r/ggplot2/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/heatmap-rainflow/r/ggplot2/plot-dark.png",
  "quality_score": 87.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Rainflow Counting Matrix for Fatigue Analysis on anyplot.ai.

Other implementations