Changing every chart at once, instead of styling each one by hand.
Overview
rcParams
matplotlib's default appearance is functional but plain. Grey backgrounds, thin lines, small text. For a quick check of data, the defaults are fine. For a chart that will be shown to others, you almost always want to change several things: remove the top and right spines, increase the font size, choose a colour palette, widen the lines.
Changing these one figure at a time is tedious. rcParams lets you change the defaults once and have every subsequent figure pick them up. Set the font size to 12, the line width to 2, the spines to off, and every figure you create from that point on inherits those settings.
Style sheets bundle many rcParams changes into a single file. matplotlib ships with several built-in styles: ggplot, seaborn-v0_8, bmh, fivethirtyeight. Applying one is a single call. You can also write your own style sheet and share it across projects. That gives you a consistent look without repeating the same settings in every script.
The distinction between rcParams and per-artist settings is worth understanding. rcParams are defaults. They apply when an artist is created. Changing an rcParam after a figure is drawn does not change that figure. Per-artist settings, like the colour you pass to plot, override the defaults for that one call.
This page covers rcParams, built-in style sheets, custom styles, the context manager for temporary styles, font configuration, DPI settings and the common patterns that produce publication-ready figures.
The context manager plt.style.context("style_name") applies a style for the duration of a with block. That is useful in notebooks where you want one figure in a particular style without changing the defaults for the rest of the notebook.
Fonts deserve special attention. matplotlib defaults to DejaVu Sans, which is readable but uncommon in publications. Switching to a system font like Arial or Helvetica, or to a serif font for academic papers, is an rcParams change. The page covers how to list available fonts, set a family, and handle missing fonts.
plt.rcParams is a dict of each default matplotlib uses. A few hundred settings covering figure size, fonts, colours, line widths, spines, grids, ticks and saving.
Changing one changes each figure created later:
plt.rcParams["axes.spines.top"] = False
The keys are dotted paths mirroring the object structure: lines.linewidth, axes.titlesize, xtick.direction, savefig.dpi.
plt.rcdefaults() restores all, which is worth knowing in a notebook where settings build up across cells.
The key property is that these are defaults, applied when an artist is created. Changing a setting doesn't affect figures already drawn.
Worth knowing
plt.rcParams holds every default; changing one affects every later figure, and plt.rcdefaults() restores them.
Style sheets are named bundles — plt.style.use applies one globally, plt.style.context for a block only.
A list of styles applies left to right, and a dict in the list overrides individual settings.
plt.rc_context(HOUSE) with a module-level dict is the whole mechanism for a consistent project style.
Styles control fonts, colours, spines and grids — they cannot choose a chart type or pick an alpha appropriate to your data.
A missing font warns and falls back rather than failing, so a figure can render in a different typeface elsewhere.
Styles and rcParams
Changing each chart at once, instead of styling each one by hand.
rcParams is the settings dictionary
Every default lives in it, and changing one changes every later figure.
example_01.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
print("some current defaults:")
for k in ["figure.figsize", "lines.linewidth", "axes.grid",
"font.size", "axes.spines.top"]:
print(" %-20s %s" % (k, plt.rcParams[k]))
print()
print("total settings:", len(plt.rcParams))
plt.rcParams["lines.linewidth"] = 3
plt.rcParams["axes.spines.top"] = False
plt.rcParams["axes.spines.right"] = False
fig, ax = plt.subplots(figsize=(6, 2.5))
ax.plot(np.linspace(0, 10, 50), np.sin(np.linspace(0, 10, 50)))
ax.set_title("thick line, no top/right spines - set globally")
plt.rcdefaults()
print()
print("plt.rcdefaults() puts everything back.")
Output
Style sheets are named bundles of settings
One line instead of twenty.
example_02.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
print("a few available styles:")
for s in sorted(plt.style.available)[:12]:
print(" ", s)
print(" ... %d in total" % len(plt.style.available))
x = np.linspace(0, 10, 60)
for name in ["default", "ggplot", "seaborn-v0_8-darkgrid"]:
if name not in plt.style.available and name != "default":
continue
with plt.style.context(name):
fig, ax = plt.subplots(figsize=(4.5, 2))
for k in range(3):
ax.plot(x, np.sin(x + k))
ax.set_title(name, fontsize=10)
print()
print("plt.style.use(name) applies it from then on.")
print("plt.style.context(name) is a context manager, so it applies to")
print("one block and leaves everything else alone - which is what you")
print("want in a script that also draws other charts.")
Output
Combining styles
A list applies them in order, so later ones override.
example_03.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 60)
with plt.style.context(["ggplot", {"lines.linewidth": 3,
"axes.facecolor": "white",
"figure.figsize": (6, 2.5)}]):
fig, ax = plt.subplots()
for k in range(3):
ax.plot(x, np.sin(x + k))
ax.set_title("ggplot, with three settings overridden")
print("A list is applied left to right.")
print("A dict in the list is treated as a set of rcParams, so you can")
print("take a style and change a few things without copying the file.")
print()
print("This is the practical form: a published style for the look, a")
print("dict for the two things it gets wrong for your use.")
Output
A house style, in one place
Define it once and every chart in the project matches.
example_04.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
from cycler import cycler
HOUSE = {
"figure.figsize": (6.5, 3.5),
"figure.dpi": 110,
"font.size": 10,
"axes.titlesize": 12,
"axes.titleweight": "bold",
"axes.titlelocation": "left",
"axes.spines.top": False,
"axes.spines.right": False,
"axes.grid": True,
"grid.alpha": 0.3,
"axes.axisbelow": True,
"lines.linewidth": 2,
"legend.frameon": False,
"axes.prop_cycle": cycler(color=["#264653", "#e76f51", "#2a9d8f",
"#e9c46a", "#8ab17d"]),
}
with plt.rc_context(HOUSE):
x = np.linspace(0, 10, 80)
fig, ax = plt.subplots()
for k, name in enumerate(["north", "south", "east"]):
ax.plot(x, np.sin(x + k) + k * 0.3, label=name)
ax.set_title("Every chart in the project looks like this")
ax.set_xlabel("x")
ax.set_ylabel("value")
ax.legend()
print("rc_context takes a dict directly, and restores the old settings")
print("on exit. A module-level HOUSE dict plus rc_context is the whole")
print("mechanism for a consistent set of charts.")
Output
What a style cannot fix
Settings control defaults, not the decisions.
example_05.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(0)
x = rng.normal(size=3000)
y = x * 0.6 + rng.normal(size=3000)
with plt.style.context("ggplot"):
fig, (a, b) = plt.subplots(1, 2, figsize=(9, 3))
a.scatter(x, y)
a.set_title("styled, and still unreadable")
b.scatter(x, y, s=6, alpha=0.15)
b.set_title("alpha and marker size fixed by hand")
print("Both panels use the same style sheet.")
print("The left one is 3000 opaque markers; no style setting rescues it.")
print()
print("Styles handle fonts, colours, spines and grids. They cannot")
print("choose a chart type, set an alpha appropriate to your n, or")
print("decide what the title should say.")
Output
Fonts
Setting a family, and what happens when it is missing.
example_06.pymatplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
available = sorted({f.name for f in fm.fontManager.ttflist})
print("fonts matplotlib can see here:", len(available))
for name in available[:8]:
print(" ", name)
print()
print("current family:", plt.rcParams["font.family"],
"->", plt.rcParams["font.sans-serif"][:3])
print()
print("font.family takes a generic name; font.sans-serif is the list")
print("tried in order. A missing font produces a warning and a")
print("fallback, not an error - so a figure can silently render in a")
print("different typeface on another machine.")
fig, ax = plt.subplots(figsize=(6, 2))
ax.text(0.5, 0.5, "whatever font is available here",
ha="center", va="center", fontsize=14)
ax.set_xticks([]); ax.set_yticks([])
Output
font.family takes a plain name. sans-serif, serif, monospace — and font.sans-serif is the sorted list of actual fonts tried for it.
A font that isn't installed makes a warning and a fallback, not an error. The figure renders in what was on hand next. This Means the same script can give other-looking output on a other machine. The only sign is a warning that is easy to miss in a log.
For output that must look same all over, either restrict yourself to fonts you know are present, or embed the font by saving as PDF or SVG with pdf.fonttype = 42. This Stores the glyphs in the file.
mathtext handles LaTeX-style maths in labels without needing LaTeX installed: r"$\sigma^2$" in any text arg.
Style sheets
A style sheet is a named bundle of rcParams.
plt.style.on hand lists them — ggplot, bmh, fivethirtyeight, grayscale, the seaborn-v0_8-* family, and a few others.
plt.style.use("ggplot") applies one from that point on. with plt.style.context("ggplot"): applies it to a block and restores the previous settings later. This Is what you want in a script that also makes charts in a other style.
Writing your own is a plain text file of key. Value lines with a .mplstyle extension, placed where matplotlib looks for it. For a project, a dict in a module is often simpler.
Combining
plt.style.use(["ggplot", {"lines.linewidth": 3}]) applies items left to right, so later ones win.
A dict in the list is treated as rcParams. That is the hands-on form for real work. Take a published style for its typography and colours, then replace the two or three things it gets wrong for your case, without copying and keeping a whole style file.
rc_context takes a dict right and restores the previous settings on exit.
The settings worth putting in almost any house style are the ones that fix matplotlib's weakest defaults. The top and right spines, a left-aligned bold title, a faint grid drawn below the data, a legend without a frame. A colour cycle you chose.
That is six lines, and it's the gap between charts that look like matplotlib defaults and charts that look designed.
What styles cannot do
A style sets defaults. It can't make choices.
It won't choose between a bar chart and a line chart. It won't set an alpha right to your number of points. The right value for 100 points is wrong for 100,000. It won't decide the axis should start at zero, or that the title should state a finding, or that three of the seven series should be greyed out.
The fifth editor shows a styled scatter that is still unreadable. The problem was overplotting and no setting handles that.
Styling is the last 20% of a chart. The choices are the rest. They're the subject of most of this track.
Where settings come from
matplotlib reads its defaults in a fixed order, each replacing the last:
The built-in defaults.
A matplotlibrc file, found in the current folder, the user's config folder, or the installation.
plt.style.use(...).
Direct set to plt.rcParams.
args passed to the plotting call itself.
That sorting shows a common confusion: a setting applied in a style sheet is overridden by anything passed by name. A matplotlibrc in the working folder silently changes each chart in that project. When a figure looks other on another machine, a matplotlibrc is a pick.
Settings worth knowing
Beyond the obvious ones, a handful appear in most house styles:
figure.autolayout: True — applies tight_layout to each figure on its own.
axes.titlelocation: left — headline-style titles.
axes.axisbelow: True — grid behind the data.
legend.frameon: False.
savefig.bbox: tight — makes each save behave as if bbox_inches="tight" had been passed. This Drops a whole class of cropping bug.
figure.constrained_layout.use: True — the newer option to autolayout.
errorbar.capsize: 3 — caps by default, since the bare-line default is rarely what anyone wants.
Writing a style file
A .mplstyle file is plain text, one key: value per line, with # comments and no quotes:
Note the colours have no # prefix. # starts a comment in this format. A small trap that makes a confusing parse error.
Placed in ~/.config/matplotlib/stylelib/, it can be used by name from anywhere.
Per-project versus per-figure
A project-wide style is right for agreement and wrong when one figure needs to differ.
plt.rc_context(...) as a context manager is the middle ground. A project default applied broadly. A block that replaces it for one figure without leaking.
The failure mode of global settings in a notebook is that they build up invisibly across cells. A figure depends on which cells have been run. plt.rcdefaults() at the top of a notebook, followed by the project style, makes it fixed.
Style is not design
A style sheet gets the typography, spacing and palette steady. This Is truly valuable and is roughly the last fifth of making a chart good.
The other four fifths — which chart, what to compare, what to leave out, what the title claims, whether the axis starts at zero — are choices no setting can make.
That is worth saying by name because a good style sheet makes a bad chart look professional. This Isn't an gain.
A minimal house style
The settings that fix matplotlib's weakest defaults, in about ten lines:
Nothing exotic, and each chart in a project drawn under it looks intentional. savefig.bbox alone drops the most common cropping complaint.
Styles and reproducibility
A figure's appearance depends on settings that aren't in the plotting code. This Is a reproducibility problem.
Three sources of drift: a matplotlibrc in the working folder, built up rcParams changes in a long notebook session. A style sheet that differs between machines.
Two habits address it.
Reset by name at the start: plt.rcdefaults() followed by the project style. The figure then doesn't depend on what ran before.
Apply the style in a context manager rather than globally. A figure carries its own appearance rather than taking from what is current.
For a figure that will be regenerated months later, the style belongs in the store alongside the code, not in a user config folder.
Style as a decision record
A style sheet is a place to write down choices once. That is worth more than the agreement it makes.
each setting in it codes a judgement: that titles are left-aligned headlines, that grids are faint and behind the data, that legends have no frame, that this is the palette. Written in a style file, those choices are visible, reviewable and changeable in one place.
Written into each chart, they're re-decided each time, inconsistently, by whoever is writing that chart.
The hands-on gain shows up when a thing has to change. A new brand colour, a journal's font need, a switch to dark backgrounds for a presentation. With a style, that is one edit. Without it, it's a search through each plotting call in the project.
That is the same arg as any other config. It applies here more than people expect. Chart styling is just the kind of thing that gets copy-pasted.
Styles and rcParams, in summary
rcParams holds each default, and changes affect figures created later.
Style sheets bundle them. plt.style.context applies one to a block without leaking.
A list applies styles in order. A dict in the list replaces single settings. The hands-on way to take a published style and fix the two things it gets wrong.
rc_context with a module-level dict is the whole method for a project style.
A missing font warns and falls back silently. So Figures can look other elsewhere.
And a style is the last fifth of a good chart. It can't choose the chart type, set an alpha suited to your sample size, or decide what the title should claim. And it will make a bad chart look professional. This Isn't the same as making it better.
Dark backgrounds
A dark theme isn't a colour inversion, and doing it by hand often misses a thing.
The elements that need changing: the figure background, the axes background, the text colour, the tick colours, the tick label colours, the spine colours, the grid colour. The property cycle. Because a palette tuned for white is often too dark on black.
plt.style.use("dark_background") does all of it. This Is the arg for using a style rather than setting six things.
Two further points. Saturated colours that look right on white are often too intense on black. Desaturating them slightly helps. And savefig still writes a white background unless told else. A dark figure saved with default settings comes out with a white border around a dark plot. facecolor=fig.get_facecolor() fixes it, or setting savefig.facecolor in the style.
Where to keep a style
A project style needs to live one spot both the code and the people can find.
A module in the store. style.py exporting a dict — is version-controlled, reviewable. Importable. It's the option that behaves like the rest of the code.
**A .mplstyle file in the store**, applied with a scaled path, is equal and uses matplotlib's own format.
A file in the user's config folder is convenient and invisible to all else. This Makes figures irreproducible on another machine. It's the wrong place for anything shared.
The store options also mean the style is part of the diff when it changes. A figure that suddenly looks other has a commit showing why.
For a single analysis, a dict at the top of the notebook applied with rc_context is enough. Still better than settings scattered through the plotting calls.
One more thing
plt.style.use accepts a URL or a file path and a name. A style can be shared without installing it.
For a team, a style file in a shared store referenced by scaled path gives all the same figures without anyone configuring their setting. And it appears in code review when it changes.
Styles and rcParams: the short version
A style is a set of choices written down once. This Is worth more than the agreement it makes.
It handles typography, spacing and palette. Roughly the last fifth of a good chart. The other four fifths are choices no setting can make. A good style applied to a bad chart makes a professional-looking bad chart.
Questions people ask about Styles and rcParams
What is rcParams? A dictionary of default settings, such as font size, line width and colours, that every new figure reads.
How do I use a built-in style? Call plt.style.use('ggplot') or another name from plt.style.available. Later styles override earlier ones.
How do I change style for one figure only? Wrap the code in with plt.style.context('name'): or with plt.rc_context({...}):.
Where does matplotlib read defaults from? Style sheets and the matplotlibrc file. Run matplotlib.matplotlib_fname() to find which one is loaded.
How do I make my own style? Write a .mplstyle file with one key: value per line and load it with plt.style.use('path/to/file.mplstyle').
Why do my settings not apply to existing figures? Most rcParams are read when an artist is created. Set them before drawing.
Reading the code back
A style is a dict applied in a context manager. Its contents are choices rather than settings. The ones that matter most are the ones fixing matplotlib's weakest defaults. Spines, title alignment, grid weight, legend frame and the colour cycle. Six entries covers it. Each chart in the project then starts from a better place.
Check yourself
0 of 4
Answer without scrolling back up.
What does changing `plt.rcParams` affect?
They are defaults, applied when an artist is created. plt.rcdefaults() restores them.
What is the difference between `plt.style.use` and `plt.style.context`?
Which is what you want in a script that also produces charts in another style.
How do you take a style but change two of its settings?
A dict in the list is treated as rcParams, so you avoid maintaining a whole style file for two overrides.
What happens when a font named in rcParams is not installed?
The same script can produce different-looking output on a different machine, with only an easily-missed warning.
Cheat sheet
Styles and rcParams
Apply built-in styles and set rcParams to restyle every matplotlib chart at once. Create a consistent look without repeating style code.
Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.