Correlation Matrix in R: Compute, Visualize & P-values
Every pairwise correlation at once — rstatix the tidy way, base R for the classics
Biostatistics
Learn to compute and visualize a correlation matrix in R. Use rstatix cor_mat() for a tidy matrix with p-values, base R cor() + Hmisc rcorr() for the classic route, reshape to a long table, mark the significant correlations, and draw a correlogram with corrplot.
Published
June 22, 2026
Modified
July 7, 2026
TipKey takeaways
A correlation matrix shows the correlation coefficient for every pair of numeric variables in one table — the quick way to spot relationships across a whole dataset.
Compute it with cor_mat() from rstatix (tidy, carries p-values) or base R cor() (coefficients only) + Hmisc::rcorr() for the significance levels.
Reshape the square matrix to a long table (cor_gather()), keep just one triangle, and mark the significant correlations (cor_mark_significant()).
Visualize it as a correlogram with corrplot() — colour + size encode strength and direction, and insignificant cells can be blanked out.
Translate prose only across the engines: cor(), cor_mat(), rcorr() and corrplot() all describe the same matrix.
Introduction
A correlation test answers the relationship between two variables. A correlation matrix scales that up: it computes the coefficient for every pair of numeric variables at once, returning a square table you can scan or plot to find the strongest relationships across an entire dataset.
This lesson builds the matrix two ways — the tidy rstatixcor_mat() workflow (which carries the p-values along) and the classic base-R cor() + Hmisc::rcorr() — then reshapes, filters and visualizes it as a correlogram. We use six numeric columns of the built-in mtcars data throughout.
Both cor() and cor_mat() default to Pearson correlation. For the rank-based, non-parametric coefficients — when the relationship is monotonic but not linear, or the data are ordinal or non-normal — pass method = "spearman" or method = "kendall" (cor(mydata, method = "spearman"), cor_mat(method = "spearman")). The choice mirrors the correlation test: Pearson for linear, Spearman/Kendall for ranks.
With rstatix (recommended)
cor_mat() returns a tidy correlation matrix — a data frame you can pipe onward, and one that remembers the p-values for later steps:
Read it like the coefficient matrix: each cell is the p-value for that pair, and anything below 0.05 flags a correlation that is statistically significant at the 5% level. The diagonal is the exception — a variable correlates perfectly with itself, so its significance is undefined.
The classic base-R route is Hmisc::rcorr(), which returns the coefficients ($r), the sample sizes ($n) and the p-values ($P) in one object:
mpg disp hp drat wt
mpg NA 9.380328e-10 1.787835e-07 1.776240e-05 1.293958e-10
disp 9.380328e-10 NA 7.142679e-08 5.282022e-06 1.222311e-11
hp 1.787835e-07 7.142679e-08 NA 9.988772e-03 4.145827e-05
drat 1.776240e-05 5.282022e-06 9.988772e-03 NA 4.784260e-06
wt 1.293958e-10 1.222311e-11 4.145827e-05 4.784260e-06 NA
qsec 1.708199e-02 1.314404e-02 5.766253e-06 6.195826e-01 3.388683e-01
qsec
mpg 1.708199e-02
disp 1.314404e-02
hp 5.766253e-06
drat 6.195826e-01
wt 3.388683e-01
qsec NA
rcorr() leaves the diagonal as NA (a variable’s correlation with itself isn’t tested) — which is why, before handing the p-value matrix to corrplot() below, we set diag(p.mat) <- 0 so the diagonal counts as significant and isn’t blanked out.
Reshape to a tidy table
A square matrix is hard to read once it grows. cor_gather() collapses it to a long table — one row per pair, with the coefficient and p-value side by side (it’s the tidy replacement for the hand-rolled flattenCorrMatrix()-style helpers you may have seen). Pull a single triangle first to drop the duplicate and diagonal entries:
A correlogram turns the matrix into a picture: colour and size encode the coefficient. Reorder by clustering so related variables sit together, and show just the upper triangle:
Positive correlations are blue, negative red; bigger, darker circles indicate stronger relationships. To blank out the insignificant cells, pass the p-value matrix:
The most information-dense view pairs every variable with every other one. GGally::ggpairs() draws the distribution of each variable on the diagonal, the bivariate scatter plots below it, and the correlation coefficient with significance stars above it — the whole matrix as one picture:
Bigger coefficients (and more stars) above the diagonal mark the stronger, more significant relationships, and the scatter below lets you eyeball the linearity Pearson assumes before you trust the coefficient. psych::pairs.panels() and PerformanceAnalytics::chart.Correlation() draw the same diagonal-distribution / scatter / coefficient layout in base graphics.
Other quick views
rstatix::cor_plot() is a tidy wrapper around corrplot() that takes a cor_mat() result directly — no need to drop down to base cor():
The legend shows the cutoffs: a blank for |r|<0.3, . to B for progressively stronger correlations. For deep correlogram customization (ordering, mixed methods, insignificance handling), see the dedicated correlogram lesson.
Try it live
Build and plot a correlation matrix on a different set of mtcars columns — add carb and gear, or swap the palette. The sandbox boots on first Run.
🟢 With an AI agent
Ask Prova“build a correlation matrix of my numeric columns and show me which ones are significant” — it answers with rstatix code you can run on your own data, then helps you read the correlogram. The runtime is the judge.Ask Prova →
Common issues
cor() errors on a data frame with text columns. A correlation matrix needs numeric input — select only numeric columns first (mydata[, sapply(mydata, is.numeric)]) or dplyr::select() them.
The matrix is full of NA. Missing values propagate. Add use = "complete.obs" to cor() (case-wise deletion) or clean the data before computing.
corrplot can’t blank insignificant cells.corrplot() needs a p-value matrix via p.mat =; cor() doesn’t produce one — get it from ggpubr::cor_pmat(), rstatix::cor_mat() |> cor_get_pval(), or Hmisc::rcorr()$P.
Frequently asked questions
NoteHow do I create a correlation matrix in R?
Use cor() from base R for the coefficients — cor(mydata) on a numeric data frame — or mydata %>% cor_mat() from rstatix for a tidy matrix that also carries the p-values. Round base-R output with round(cor(mydata), 2) for readability.
NoteHow do I get p-values for a correlation matrix?
Base cor() returns only coefficients. Use Hmisc::rcorr(as.matrix(mydata)) (its $P element is the p-value matrix), ggpubr::cor_pmat(mydata), or rstatix cor_mat() %>% cor_get_pval(). To combine coefficients and significance in one table, use rstatix::cor_mark_significant().
NoteWhat is a correlogram in R?
A correlogram is a graphical display of a correlation matrix where colour — and, with corrplot, circle size — encodes each pair’s correlation, so you can scan for the strongest and most significant relationships at a glance instead of reading a table of numbers. corrplot::corrplot() is the standard tool; rstatix::cor_plot() is a tidy wrapper that draws one straight from a cor_mat() result.
NoteHow do I visualize a correlation matrix in R?
The most popular way is a correlogram with corrplot::corrplot(cor(mydata), type = "upper") — colour and circle size encode the correlations. Other options are rstatix::cor_plot() (a tidy wrapper), base heatmap(), and symnum() for a compact symbol view.
NoteWhat is the difference between a correlation test and a correlation matrix?
A correlation test evaluates the relationship between two specific variables (coefficient + p-value + confidence interval). A correlation matrix computes the coefficient for every pair of numeric variables at once, giving a square table you scan or plot to compare many relationships.
NoteHow do I compute a Spearman correlation matrix in R?
Add the method argument: cor(mydata, method = "spearman") in base R, or mydata %>% cor_mat(method = "spearman") with rstatix (use "kendall" for Kendall’s tau). Spearman and Kendall work on ranks, so they capture monotonic relationships and are robust to non-normal or ordinal data — the same parametric-vs-rank choice as the two-variable correlation test.
NoteHow do I reorder or cluster a correlation matrix?
Pass order = "hclust" to corrplot() to reorder the variables by hierarchical clustering, so strongly related variables sit next to each other. For the table form, rstatix::cor_reorder() reorders a cor_mat() result the same way.
Test your understanding
ImportantPractice
Run it. In the live cell, compute a correlation matrix of mpg, wt, hp and qsec, then mark the significant correlations. Which pair is the strongest?
Conceptual. Your correlogram shows a strong blue circle between two variables, but blanking by sig.level = 0.05 leaves that cell empty. How can a large coefficient be non-significant?
NoteHint
Fill the four blanks with mpg, wt, hp, qsec. The strongest correlation has the coefficient closest to ±1 and the most stars. For question 2, think about sample size — significance depends on both the coefficient and how many observations you have.
NoteSolution
library(rstatix)mtcars %>%select(mpg, wt, hp, qsec) %>%cor_mat() %>%cor_mark_significant()#> mpg–wt is the strongest (r ≈ -0.87, ***).
For question 2: significance depends on the sample size, not just the coefficient. With few observations, even a large r can have a p-value above 0.05 (the confidence interval is wide), so corrplot blanks it. Collect more data, or report the coefficient with its interval rather than relying on the star alone.
Conclusion
You can now compute a correlation matrix in R the tidy rstatix way (cor_mat(), with p-values, reshaping and significance marks) and the classic base way (cor() + Hmisc::rcorr()), then turn it into a correlogram with corrplot() that highlights the strongest, significant relationships.
This closes the loop on the correlation test: test one pair, then scan them all. Next, revisit the assumption behind Pearson in the normality test lesson.
Every result on this page was produced by the code shown, run at build time against a pinned R environment — edit any block and Run to reproduce it yourself.