corrplotpy is a Python port of the R package corrplot 0.95 (Wei & Simko, 2024b) on matplotlib (Hunter, 2007), with the same functions, argument names, and defaults. The style builder below writes the Python code or the R code for a plot, and the sections after the builder cover installation, a first plot, reasons for the port, and the anatomy of a corrplot. The second tab shows the same examples in Python and in R.

Style builder

The controls set the parameters of corrplot(), the preview shows the result for the mtcars data (Henderson & Velleman, 1981), and the code under the preview draws that plot. Change the name of the CSV file, and the code is ready for other data. The type 'mixed' draws one symbol in the upper triangle and another in the lower triangle, with corrplot_mixed().

Correlation method, symbol and sequenceThe correlation method is not a parameter of corrplot(). The method selects the step that computes the matrix, df.corr(method=...). Pearson measures how near the data is to a straight line. Spearman and Kendall measure a relation between the ranks, so Spearman or Kendall fits ordered data and data with large outliers. The symbol sets the shape drawn in each cell. The type 'mixed' draws one symbol in the upper triangle and another in the lower triangle, with corrplot_mixed().
SYMBOL (method)
Color and numbersThe color scale runs from -1 to +1 in every correlation plot. The scale is not a min-max scale of the matrix and not a z-score. With n palette colors counted from 1 as in R, the cell for r takes color number floor((r + 1) / 2 * n) + 1, and r = 1 takes the last color, so the value 0 is the middle color in every plot. A matrix that is not a correlation matrix needs is_corr=False and a range in col_lim. The first row of palettes is the six diverging ColorBrewer palettes of COL2() and four perceptually uniform matplotlib colormaps, viridis, cividis, plasma, and magma. The second row is the ten sequential ColorBrewer palettes of COL1(). Every ColorBrewer palette that corrplot ships has the colorblind-safe flag in ColorBrewer (Harrower & Brewer, 2003), and cividis was designed for color vision deficiency (Nuñez et al., 2018). corrplot(col='viridis') takes a matplotlib colormap by name. The reverse switch turns the palette around, so the first color is at +1.
PALETTE
NUMBERS
COLOR
Text labels
Legend and significance
File output

corrplotpy computed the correlation matrix, p-values, reorderings, cluster sizes, and palettes that the preview uses. Circle radius, square side, and pie angle are the values that the package uses. Pearson, Spearman, and Kendall each produce a different matrix with different orderings and clusters, so the preview data includes all three matrices. A test runs the generated code through the package and checks that the code draws the matrix the preview shows.

Install

corrplotpy requires Python 3.10 or later, and it has four dependencies: numpy 1.24 or later, scipy 1.10 or later, pandas 2.0 or later, and matplotlib 3.7 or later. The tests run on Python 3.10 to 3.14 with the current releases of the dependencies and with those floors. The command below installs corrplotpy and the dependencies.

pip install corrplotpy

The first plot

corrplot() accepts a correlation matrix. A pandas DataFrame keeps the names of the variables, and corrplot() puts these names on the plot. The function returns an object, and save() writes the figure to a file.

import pandas as pd
from corrplotpy import corrplot

df = pd.read_csv('my_data.csv')
res = corrplot(df.corr())
res.save('corrplot.png', dpi=200)

Why corrplotpy

corrplot (Wei & Simko, 2024b) is an R package for displaying a correlation matrix. corrplot has approximately 50 parameters, seven symbols, four reordering algorithms, and a significance test in each cell. matplotlib and seaborn draw a heatmap of a matrix, but neither library draws the symbols, computes the orderings, or marks the cells that fail a test. Two Python implementations have the corrplot name. The Corrplot class of biokit (Cokelaer, 2021) draws eight symbols and orders by a SciPy linkage, and the corrplot function of Louridas (2013) draws ellipses and blanks cells by a p-value threshold. Neither implementation follows the R argument list or reports agreement with R output.

Switching an analysis pipeline from R to Python therefore requires writing the plot again, and the new plot does not have the same formatting. corrplotpy has a Python function for each function in corrplot 0.95, with the same argument names and defaults, and output that agrees with R within the tolerances that the second tab reports. The one change in syntax is that a dot in an R argument name is replaced with an underscore in the Python implementation.

Anatomy of a corrplot in corrplotpy

A corrplot with each part named

corrplot() produces one cell for each pair of variables. The symbol in the cell and the color of the symbol encode the correlation of that pair. The figure above numbers each part of one plot, and the key names the parameter that changes it. This plot uses the hierarchical sequence with two cluster rectangles, prints the correlation in each cell, and leaves a cell blank when its p-value is above 0.05.

The method parameter selects one of seven symbols: circle, square, ellipse, number, shade, color, and pie. The circle and the square encode the absolute value of the correlation as an area, the ellipse as a shape, and the number as text. The color fills the cell. The shade adds white lines to the negative cells, and the pie fills clockwise for a positive value.

New functions in the Python version

Nine functions are new in corrplotpy. The style builder generates the code, and the choice of coefficient in the builder is described with the builder above. The save() method writes the figure to a file and accepts a format, a resolution, dimensions, a transparent background, and a UTC time in the file name. With utc_name=True the file name is the UTC time to the microsecond; save() creates the file exclusively and moves to a counter when the name exists, so the call does not write over an earlier file.

The font_family parameter selects the font. When the requested font is not installed, corrplotpy substitutes a metric-compatible font where one exists, Liberation Sans for Arial, Carlito for Calibri, and Liberation Serif for Times New Roman. The label space then stays close to the original, although the exact text extent still depends on the installed font. Without a metric match, the fallback is the installed sans-serif font, and the layout can differ from R's. The pointsize parameter sets the base font size, and tl_cex, number_cex, and cl_cex each scale one group of text.

R rotates the column names with tl.srt and keeps the row names horizontal. corrplotpy adds tl_srt_row, so the row names can take a separate angle. The default of 0 degrees reproduces the R layout.

addCoef_col='auto' selects black or white for each printed number. The renderer takes the color under the number (the symbol color when the symbol is wider than the text, the cell background otherwise), computes the WCAG 2.1 contrast ratio against black and against white, and selects the higher. A number on a dark cell is then white, and a number on a pale cell is black. R accepts one color for every number.

With type='upper' or type='lower', R draws no cluster rectangles. corrplotpy draws the outline of the shown cells of each cluster, a stepped triangle, when addrect is set with either type. The function cluster_outline() returns the vertices of one outline. In corrplot_mixed(), where both triangles are shown, addrect outlines each cluster once with the rectangle over the whole block.

The col parameter accepts a matplotlib colormap by name or as an object, so col='cividis' selects the colormap designed for color vision deficiency (Nuñez et al., 2018), and the style builder offers viridis, cividis, plasma, and magma. The colormap is sampled at 200 colors, the length of the default palette. Every ColorBrewer palette in COL1() and COL2() has the colorblind-safe flag in ColorBrewer (Harrower & Brewer, 2003).

With insig='label_sig', R draws the significance marks at the cell center in one color, over any printed number. corrplotpy moves the marks up when a number is printed in the cell, and pch_col='auto' prints each mark in black or white by the same contrast rule as addCoef_col='auto'.

save() parameterDefaultFunction
format'png'The file format: 'png', 'svg', 'pdf', or another format of matplotlib. The extension of the path also sets it.
dpi200The dots for each inch. It changes the pixel count of a PNG. A vector format does not use it.
sizeNoneThe dimensions of the written image in inches as (width, height); the figure returns to its own size afterwards.
transparentFalseTrue writes the file with no background, in any format that stores transparency; False keeps the figure's background.
utc_nameFalseTrue adds the UTC time to the file name, to the microsecond, in the form corr_20260902T143005.123456Z.png, and adds a counter if that name is taken.
res = corrplot(df.corr(), method='ellipse', font_family='Times New Roman')

res.save('figure.png', dpi=300)              # a raster file
res.save('figure.pdf')                       # a vector file
res.save('figure.svg', transparent=True)     # no background
res.save('figure.png', size=(10, 8), utc_name=True)
# figure_20260902T143005.123456Z.png
Requested fontMetric-compatible substitute
HelveticaNimbus Sans, Arial, Liberation Sans, FreeSans
ArialLiberation Sans, Arimo, Helvetica, Nimbus Sans
CalibriCarlito, Liberation Sans
Times New RomanLiberation Serif, Tinos, Nimbus Roman, FreeSerif
Courier NewLiberation Mono, Cousine, Nimbus Mono PS, FreeMono
Most important parameters

corrplot() has approximately 50 parameters. The table lists the parameters that change the plot the most. The name of each parameter is the R name with an underscore in the place of each dot.

ParameterDefaultFunction
method'circle'The symbol in each cell: 'circle', 'square', 'ellipse', 'number', 'shade', 'color', or 'pie'.
type'full'The part of the matrix: 'full', 'upper', or 'lower'.
order'original'The sequence of the variables: 'original', 'AOE', 'FPC', 'hclust', or 'alphabet'.
diagTrueShow the diagonal, or hide it with False.
addrectNoneThe number of clusters to outline. It needs order='hclust'.
colNoneThe colors, from COL2() for a diverging palette or COL1() for a sequential palette, or the name of a matplotlib colormap such as 'viridis'.
is_corrTrueSet is_corr to False for a matrix that is not a correlation matrix, and set col_lim to the range of the values.
tl_col, tl_srt, tl_cex'red', 90, 1The color, angle, and size of the text labels. tl_srt turns the column names.
tl_pos, tl_srt_rowNone, 0Where the text labels go, and the angle of the row names. tl_pos of None selects 'lt' for a full plot, 'td' for an upper plot, and 'ld' for a lower plot, which puts one group of names on the diagonal. tl_srt_row is not in R.
cl_pos, cl_ratioNone, 0.15The position and the width of the color legend. 'r' is at the right, 'b' is below, and 'n' removes it.
addCoef_col, number_cexNone, 1The color and the size of the correlation number in each cell. 'auto' is not in R; 'auto' selects black or white for each number, whichever reads better on the color under the number.
col_limNoneThe range that the palette covers. The color scale is fixed from -1 to +1; the scale is not a min-max scale and not a z-score. For a matrix that is not a correlation matrix, set is_corr=False and set col_lim.
p_mat, sig_level, insigNone, 0.05, 'pch'The p-values, the level, and what to do with the cells above the level: 'pch', 'p-value', 'blank', 'n', or 'label_sig'.
plotCI, lowCI_mat, uppCI_mat'n', None, NoneShow the confidence interval in each cell as 'rect', 'circle', or 'square'.
na_label, na_label_col'?', 'black'The label for a cell that has no number, and its color.
mar, figsize, pointsize(0, 0, 0, 0), (7, 7), 12The margins in lines of text, the figure dimensions in inches, and the font size.
Complete examples

Each example below is complete code, and the plot beside the code is what that code drew when this page was built. All of the examples start from the setup block, in which df is a DataFrame with one variable in each column and tests is the p-value table for df.

import pandas as pd
from corrplotpy import corrplot, corrplot_mixed, cor_mtest, mtcars

df = mtcars()                # or pd.read_csv('my_data.csv')
tests = cor_mtest(df, conf_level=0.95)

Cluster the variables and put a rectangle around each cluster

res = corrplot(df.corr(), order='hclust', addrect=3,
               method='color', tl_col='black')
res.save('clusters.png', dpi=200)
The plot that the code on the left draws: cluster the variables and put a rectangle around each cluster

Hide the correlations that fail the significance test

res = corrplot(df.corr(), p_mat=tests['p'], sig_level=0.05,
               insig='blank', addCoef_col='black',
               number_cex=0.7, tl_col='black')
res.save('significant.png', dpi=200)
The plot that the code on the left draws: hide the correlations that fail the significance test

Put stars on the significant correlations

res = corrplot(df.corr(), p_mat=tests['p'], method='color',
               insig='label_sig', sig_level=[0.001, 0.01, 0.05],
               pch_col='white', pch_cex=0.9, tl_col='black')
res.save('stars.png', dpi=200)
The plot that the code on the left draws: put stars on the significant correlations

A different symbol in each half

res = corrplot_mixed(df.corr(), lower='ellipse', upper='number',
                     number_cex=0.7)
res.save('mixed.png', dpi=200)
The plot that the code on the left draws: a different symbol in each half

A matrix that is not a correlation matrix

from corrplotpy import COL1

# the share of cars in each gear count, by cylinder count
share = pd.crosstab(df['cyl'], df['gear'], normalize='index') * 100
res = corrplot(share.round(0), is_corr=False, col_lim=(0, 100),
               method='color', col=COL1('YlGn', 200),
               addCoef_col='grey50', tl_col='black')
res.save('share.png', dpi=200)
The plot that the code on the left draws: a matrix that is not a correlation matrix

Add text to the plot after corrplot draws it

res = corrplot(df.corr(), type='lower', diag=False)
cells = res.corrPos                 # one row for each cell
res.canvas.text(cells['x'], cells['y'],
                cells['corr'].round(2))
res.save('with_numbers.png', dpi=200)
The plot that the code on the left draws: add text to the plot after corrplot draws it

Use the same style for many datasets

STYLE = dict(method='ellipse', order='hclust', addrect=3,
             tl_col='black', cl_ratio=0.2)
cars = df.drop(columns='am')       # am is constant in each subset
datasets = {'all': cars, 'manual': cars[df['am'] == 1],
            'automatic': cars[df['am'] == 0]}

for name, frame in datasets.items():
    corrplot(frame.corr(), **STYLE).save(name + '.png', dpi=200)
The plot that the code on the left draws: use the same style for many datasets
Return value

corrplot() returns an object with the parts in the table. With the cell table, code can add text or symbols to the plot after corrplot() draws the plot.

PartContent
res.corrThe matrix in the sequence of the plot, as a DataFrame.
res.corrPosOne row for each cell: xName, yName, x, y, corr, and p.value when the plot has p-values.
res.argThe arguments that corrRect() needs.
res.fig, res.axThe matplotlib figure and axes.
res.canvasThe drawing surface, for text and symbols on top of the plot.