Introduction to ggcorrplot

Alboukadel Kassambara

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]
#>       mpg  cyl disp   hp drat
#> mpg   1.0 -0.9 -0.8 -0.8  0.7
#> cyl  -0.9  1.0  0.9  0.8 -0.7
#> disp -0.8  0.9  1.0  0.8 -0.7
#> hp   -0.8  0.8  0.8  1.0 -0.4
#> drat  0.7 -0.7 -0.7 -0.4  1.0

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]
#>               mpg          cyl         disp           hp         drat
#> mpg  0.000000e+00 6.112687e-10 9.380327e-10 1.787835e-07 1.776240e-05
#> cyl  6.112687e-10 0.000000e+00 1.802838e-12 3.477861e-09 8.244636e-06
#> disp 9.380327e-10 1.802838e-12 0.000000e+00 7.142679e-08 5.282022e-06
#> hp   1.787835e-07 3.477861e-09 7.142679e-08 0.000000e+00 9.988772e-03
#> drat 1.776240e-05 8.244636e-06 5.282022e-06 9.988772e-03 0.000000e+00

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()
#> R version 4.5.1 (2025-06-13)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.6.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] C/fr_FR.UTF-8/fr_FR.UTF-8/C/fr_FR.UTF-8/fr_FR.UTF-8
#> 
#> time zone: Europe/Paris
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggcorrplot_0.3.0 ggplot2_4.0.3   
#> 
#> loaded via a namespace (and not attached):
#>  [1] gtable_0.3.6       jsonlite_2.0.0     dplyr_1.2.1        compiler_4.5.1    
#>  [5] tidyselect_1.2.1   Rcpp_1.1.1         stringr_1.6.0      jquerylib_0.1.4   
#>  [9] scales_1.4.0       yaml_2.3.12        fastmap_1.2.0      R6_2.6.1          
#> [13] plyr_1.8.9         labeling_0.4.3     generics_0.1.4     knitr_1.51        
#> [17] tibble_3.3.1       bslib_0.10.0       pillar_1.11.1      RColorBrewer_1.1-3
#> [21] rlang_1.2.0        cachem_1.1.0       stringi_1.8.7      xfun_0.57         
#> [25] sass_0.4.10        S7_0.2.1           otel_0.2.0         cli_3.6.6         
#> [29] withr_3.0.2        magrittr_2.0.4     digest_0.6.39      grid_4.5.1        
#> [33] lifecycle_1.0.5    vctrs_0.7.2        evaluate_1.0.5     glue_1.8.0        
#> [37] farver_2.1.2       reshape2_1.4.5     rmarkdown_2.31     tools_4.5.1       
#> [41] pkgconfig_2.0.3    htmltools_0.5.9