Introduction to ggcorrplot

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  message = FALSE,
  warning = FALSE,
  dpi = 150,
  fig.align = "center",
  out.width = "80%"
)

ggcorrplot draws a correlation matrix as a ggplot2 plot. Because the result is an ordinary ggplot object, you keep the full ggplot2 vocabulary: restyle it with a theme, add layers or annotations, and compose it with other plots using +.

This vignette walks through the package end to end: computing the inputs, choosing a glyph and layout, reordering by clustering, adding the coefficients, marking statistical significance, and restyling the result.

library(ggcorrplot)

The inputs: a correlation matrix and its p-values

Every plot starts from a correlation matrix. The examples use the built-in mtcars data set.

data(mtcars)
corr <- round(cor(mtcars), 1)
corr[1:5, 1:5]

To mark significance later, you also need the matrix of correlation p-values. cor_pmat() computes it with stats::cor.test():

p.mat <- cor_pmat(mtcars)
p.mat[1:5, 1:5]

ggcorrplot() never computes correlations itself — it takes a matrix you already have. That means it works equally well with a partial correlation matrix, a distance-derived similarity, or any other square matrix in [-1, 1] (and, with legend.limit = NULL, matrices outside that range such as a covariance matrix).

Basic correlogram

The default encodes each correlation as a colored square. method = "circle" encodes the value with the circle's area instead, which reads well when you want the magnitude to pop out.

ggcorrplot(corr)
ggcorrplot(corr, method = "circle")

When method = "circle", circle.scale tunes the circle sizes for your output device.

ggcorrplot(corr, method = "circle", circle.scale = 0.6)

Reorder by clustering

Ordering the variables by hierarchical clustering brings correlated variables next to each other, so the block structure becomes visible. Set hc.order = TRUE; hc.method chooses the linkage (as in hclust()).

ggcorrplot(corr, hc.order = TRUE, outline.color = "white")

hc.rect then draws rectangles around the clusters obtained by cutting the tree into k groups — a quick way to highlight the blocks.

ggcorrplot(corr, hc.order = TRUE, hc.rect = 3, outline.color = "white")

Layout: triangles and mixed glyphs

For a symmetric matrix the two triangles carry the same information, so you can show just one with type = "lower" or type = "upper".

ggcorrplot(corr, hc.order = TRUE, type = "lower", outline.color = "white")
ggcorrplot(corr, hc.order = TRUE, type = "upper", outline.color = "white")

A mixed layout draws a different glyph in each triangle. Set lower.method and/or upper.method to "square", "circle", or "number"; the variable names are placed on the diagonal. A common choice is the coefficients as numbers on one side and circles on the other. Adding cell.grid = TRUE boxes every cell so the glyphs sit in a tidy grid:

ggcorrplot(corr,
  lower.method = "number", upper.method = "circle",
  cell.grid = TRUE, show.legend = FALSE
)

With "number", the coefficient text is colored by its value on the fill ramp, darkened over a light background so the polarity still reads at a glance while a near-zero coefficient stays legible. Over a dark background the ramp is used as given, since its pale middle is then the readable end. The background is read from ggtheme, so pass a dark theme there rather than adding it afterwards with +.

Add the coefficients

lab = TRUE prints the correlation coefficient in each cell. lab_size, lab_col, and lab_fontface control its appearance.

ggcorrplot(corr, hc.order = TRUE, type = "lower", lab = TRUE)

Two options match common conventions for correlation tables:

ggcorrplot(cor(mtcars[, 1:6]),
  lab = TRUE, lab_size = 3.5,
  leading.zero = FALSE, nsmall = 2,
  type = "lower"
)

Mark statistical significance

Supplying p.mat lets the plot convey which correlations are significant at sig.level (default 0.05). There are three styles, chosen with insig. sig.level sets the cut-off used by the first two; the third labels each cell with stars at the conventional fixed thresholds instead.

insig = "pch" (default) crosses out the non-significant cells:

ggcorrplot(corr, hc.order = TRUE, type = "lower", p.mat = p.mat)

insig = "blank" hides them entirely:

ggcorrplot(corr, hc.order = TRUE, type = "lower", p.mat = p.mat, insig = "blank")

insig = "stars" flips the emphasis: rather than crossing out the non-significant cells, it marks the significant ones with significance stars (***, **, * for p <= 0.001, 0.01, 0.05 -- these thresholds are fixed and do not follow sig.level). With the default lab = FALSE this is a standalone significance map:

ggcorrplot(corr, p.mat = p.mat, insig = "stars")

With lab = TRUE, the stars are appended to the coefficients instead (-0.85***), so one plot shows both the value and its significance:

ggcorrplot(corr,
  type = "lower", p.mat = p.mat,
  lab = TRUE, insig = "stars"
)

Colors and theme

colors sets the diverging gradient. A length-3 vector maps to low / mid / high; any longer vector (for example an 11-color palette) is spread evenly across the scale.

ggcorrplot(corr,
  hc.order = TRUE, type = "lower", outline.color = "white",
  colors = c("#6D9EC1", "white", "#E46726")
)

ggtheme swaps the base ggplot2 theme, and tl.cex, tl.col, tl.srt style the variable-name labels. legend.limit controls the color-scale range — set it to NULL to use the data range, which is what you want for a covariance matrix.

ggcorrplot(corr,
  hc.order = TRUE, type = "lower",
  ggtheme = ggplot2::theme_minimal,
  tl.col = "gray30", tl.srt = 90,
  colors = c("#003C67", "white", "#8F2727")
)

It is a ggplot: compose freely

Because ggcorrplot() returns a ggplot object, you refine it with the usual ggplot2 grammar — titles, captions, and any additional layer:

library(ggplot2)

ggcorrplot(corr, hc.order = TRUE, type = "lower", outline.color = "white") +
  labs(
    title = "Correlations among mtcars variables",
    caption = "Hierarchically clustered"
  ) +
  theme(plot.title = element_text(face = "bold"))

The fixed 1:1 aspect ratio comes from coord_fixed(). To let the cells fill the plotting area — useful with many long variable names — use coord.fixed = FALSE (or add your own + coord_*, since the last coordinate system wins).

Saving works the same as any ggplot:

p <- ggcorrplot(corr, hc.order = TRUE, lab = TRUE)
ggplot2::ggsave("correlogram.png", p, width = 7, height = 6, dpi = 300)

Computing p-values with cor_pmat()

cor_pmat(x, ...) returns the symmetric matrix of p-values from stats::cor.test(), and passes ... through to it — so you can, for example, request a Spearman test:

cor_pmat(mtcars, method = "spearman")

The use argument controls how missing values are handled when deciding which cells are NA, mirroring stats::cor(): the default "pairwise.complete.obs" tests every pair with enough overlapping observations, while "everything" sets a pair to NA as soon as either variable has a missing value (so its NA pattern lines up with cor(x)).

Session information

sessionInfo()


Try the ggcorrplot package in your browser

Any scripts or data that you put into this service are public.

ggcorrplot documentation built on July 25, 2026, 1:07 a.m.