bilha analytics

Matplotlib styles

This entry assumes that you’re already familiar with python, matplotlib and seaborn and are looking to be more productive when using these tools for your research work. If you’re looking for introductory coding material, there are a few links at the end of the article to get you started. All the same, this entry should still be able to frame things and you can go into the specific coding tutorials.

The idea here is to set up a reusable theme/style and find suitable settings for publication-quality plots. That way, you have consistent styling in your plots and, of course, by scripting your process, it is easier to update your report as your experiments or your output media change.

Summary

The context

Matplotlib is a massive and powerful library and there’s loads you can do to customize your plots. Seaborn builds on matplotlib, providing easier-to-use functions and additional design-related implementations for nice-looking plots; it is a higher-level plotting library. Below is an overview of how a figure is structured in matplotlib. You have the figure, which is the general chart area, and the axes, which contains the plot and its components. You can style the figure or the axes directly in code or through a configuration file (pretty much a style sheet). Seaborn can take as input a matplotlib axes object and usually returns an axes object. Therefore, whether using matplotlib or seaborn, you can plan out your themes/styles similarly.


Main components of a plot on matplotlib. Source: Real Python

The figure object is the top-level container for all plot elements (the underlying canvas), while the axes object is specific to a given plot. For instance, therefore, you can contain multiple axes objects (e.g. two line graphs) in the same figure. The figure object determines traits such as figure size, background color, spacing between plots, general line and font colors, and characteristics of figures when saved to file. The axes object contains the elements of an individual plot and, therefore, directs the characteristics of individual plot components such as title, axis labels, tick marks and legends, as well as color and line/edge properties. Aside from the object oriented interfaces, matplotlib also avails procedural interfaces, which automatically instantiate the relevant figure and axes objects and avail convenient methods for common types of charts. For instance, any time you use plt.plot. At any point, you can also access the current figure and axes object by calling plt.gcf() and plt.gca() respectively. Additionally, the wrapper or convenience methods will often fetch the current figure or axes first and delegate accordingly. Bottom line, you want the figure or axes object for any manipulation or styling and you can do it all consistently from one place irrespective of how you eventually create your plots (PS: If you’re familiar with pandas, it provides similar plotting interfaces as plt.plot).

Customizing plot style

There are four main ways to style your plots. In order of prioritization, from highest to lowest (higher priority settings will override lower priority definitions), the approaches are

  1. Axes elements and their lower level rendering objects.
  2. Runtime style update via rcParams, which is a dictionary-like variable containing global settings.
  3. Using a style sheet, which is an rcParams configuration file that is limited to plot properties only.
  4. Using matplitlibrc file, an rcParams configuration file that includes all kinds of properties.

1. Axes: Axes object has setter and related functions for the different elements that make up a plot. For instance, Axes.set_title, Axes.set_yticks, Axes.set_frame_on, etc. It also has a lower level object called Artist, which knows how to draw on the figure canvas. There are several tutorials out there on how to manipulate the axes object so I’ll just present the gist of it and what I’ve learnt so far then leave you to it. As you can imagine, the easiest way to set the style of axes elements is through the setter methods and additional axes helper functions. Most often, manipulating the axes object will suffice.

Axes setter properties are also stored in rcParams. Therefore, instead of having it in code, you can move your styling definitions to a configuration or style document and simply import the file for use in different works. So far, I’ve seen that styling through the Axes object seems most suited for dynamically created elements with styles that are highly dependent on some properties of the data, or for more advanced cases where you have to create some custom Artist objects.

2. Runtime rcParams: rcParams is a matplotlib dictionary-like variable that holds package-wide or global variable settings. Therefore, you can modify it directly by updating the values of individual keys or updating it with another dictionary. There’s also a convenience method, matplotlib.rc, which can update multiple settings of the same element.

## Runtime rcParams updates 
import numpy as np 
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib as mpl

## i. directly updating an individual setting in rcParams
mpl.rcParams['lines.linestyle'] = '--' 
mpl.rcParams['lines.linewidth'] = 2.8
mpl.rcParams['lines.color'] = 'red' 

## ii. convenience function for updating rcParams  
mpl.rc('lines', linestyle='--', linewidth=2.8, color='red') 

## iii. updating from another dict 
line_props = dict(ls='--', lw=2.8, c='r')    ## using aliases e.g. lw=linewidth
mpl.rc( 'lines', **line_props)
## OR
line_props = {'lines.linestyle':'--', 'lines.linewidth':2.8, 'lines.color':'red'}
mpl.rcParams.update( line_props )            ## cannot use aliases 

Runtime rcParams updates can also be constrained to some context. In the above cases, the changes are global and affect all plots made afterward. The default settings can be restored by matplotlib.rcdefaults(). A context manager is available for when you want to temporarily change the default or global style.

## this will only apply the line_props to plots made within the context manager
with mpl.rc_context( line_props):
    plt.plot( x, y ) 

3. Style Sheets: A style is an rcParams configuration file that only includes plot-related settings. Remember, rcParams is a package-wide configuration variable and includes all sorts of settings. A style sheet is imported using matplotlib.style.use and any non-plot related settings in it will be ignored. There are several predefined styles provided by matplotlib and seaborn, which can be applied globally or using a context manager. You can also define your own styles (we’ll see how to do that in a bit) and import them in the same way. Furthermore, you can chain/cascade multiple style sheets. Prioritization of the updates, in that case, would be in reverse - The first style sheet listed has the lowest priority and the last one has the highest priority. Think of a cascade of dictionary update calls, the last call will update any prior updates. Additionally, you can add your style sheet to matplotlib’s config directory (check location using matplotlib.get_configdir and the default location is usually ~/.config/matplotlib, where ~ is your home/user directory) and name it as <some-name>.mplsyle. Afterwards use it by calling plt.style.use('<some-name>').

## i. list all available styles 
print( plt.style.available )

## ii. use a style - global setting 
plt.style.use( 'ggplot' )                    ## predefined in matplotlib 
plt.style.use( '../res/mypaper_style.txt')   ## custom  

## iii. use a style - context constrained
with plt.style.context('ggplot'):
    plt.plot(x,y)

## iv. use multiple styles - simply pass them as a list 
plt.style.use(['ggplot','whitegrid','mystyle'] ) 

## v. use sns context for differentiated sizing/scaling on different media
sns.set_context('notebook') 

Seaborn also provides styles that are meant to scale your graphics/plots for different presentation/reporting media. seaborn.set_context is an abstraction that works through matplotlib’s rcParams. Scaling affects only size-related elements such as font sizes, line sizes (width) and label sizes. There are four contexts - notebook, paper, talk and poster. Therefore, you don’t need to change your style sheet for different media if your primary concern is legibility due to scaling.

4. Matplotlibrc file: This is the rcParams configuration file, which contains all sorts of properties. It is loaded at startup and there are set locations where matplotlib will look for it. Changing this file can change the default properties of matplotlib for a given installation/environment depending on where the file is saved. That also means that anytime matplotlib is installed the config file will be overwritten if it is stored in the installation folder. For customizations that are specific to some use, save the configuration file in the current working directory. To avoid overwriting an updated installation config file, save it in your user profile, which depends on your platform e.g. .config/matplotlib/matplotlibrc. See documentation for specifics. Generally, the order of the search is from the most custom setup (in the current working directory) to the most generic/system-wide (installation folder) and once a matplotlibrc file is found, the search stops (only one file can be active).

I think the sweet spot is working with the style sheets - you can reuse components and abstract some of your styling code from your scripts, you can chain/cascade/compose styles by mixing them, you can have your own global defaults (provided as a .mplstyle file), and you can use context managers to constrain usage. You can have one large style sheet, or make it modular (for composing/cascading).

Example style sheet

Alright, let’s create a style sheet. When starting off, you’ll often want to use a predefined style sheet from Matplotlib and Seaborn as your core style and then pair it with whatever customizations you’ll need, by composing/cascading styles. Generally, any Axes element that can be styled in code using setters can be styled using rcParams and, therefore, you may have it in your style sheet. The full list of available rcParams can be viewed here; matplotlib’s documenetation.

You may also go to the matplotlib installation folder and copy a style sheet file (files with extension .mplstyle), and then make edits as desired or only borrow those properties that you’d like customized. As rcParams is a dictionary-like variable, the entries in the style sheet are also key-value pairs per like in the general form <key-name>: <value>. The style sheet can be saved with say ‘.txt’ file extension or ‘.mplstyle’ (or whatever you like really, just be consistent and clear of its intentions) in any location you prefer. Here’s matplotlib’s stylesheets reference.

## Example style sheet entries
## 1. figure properties 
figure.figsize : 3.5, 2.625
figure.facecolor : white 
figure.dpi : 100 
figure.titlesize : 12

## 2. legend properties
legend.fontsize : 10
legend.frameon : False

## 3. fonts 
font.family : sans-serif
font.weight : bold 

## 4. Plot axes 
axes.grid : False
axes.fontcolor : white
axes.titlesize : 12
axes.spines.top : False

## lines
lines.linewidth : 1.3
lines.linestyle : - 

Color

Dealing with color and colormaps is a large topic and this entry is already rather long. Matplotlib and Seaborn provide predefined colormaps or color palettes and there are different principles for using color in plots. The key thing to remember is that your choice of color should have sufficient contrast for your audience to distinguish between different elements. Aside from the matplotlib and seaborn palettes, you can also define your own and there are various tools out there for building colorblind-friendly palettes. Defining your color palette can be as simple as listing the color values in descending priority order in code and then using Seaborn to create and set it (see code snippet below). When using a style sheet, matplotlib’s cycler interface is used to set the color palette.

## i. set global color palette
sns.set_palette( "Set2" ) 

## ii. set color palette for a give plot during construction
sns.countplot(y, palette="Set2") 

## iii.a. define your own color palette
my_colors = ["#FF0B04", "#4374B3"]

## iii.b. preview your color palette
sns.palplot( my_colors )

## iii.c. use it 
sns.set_palette( sns.color_palette(my_colors) )
## setting up color palette in style sheet using rcParams
axes.prop_cycle: cycler(
    'color' : ["#FF0B04", "#4374B3"]
)

Tips

Somethings I’ve learnt so far on publication-quality plots that you may include in your style sheets

References and Resources

Appendix

Note book with sample code

styling boxplots

Can achieve similar results in both matplotlib and seaborn by updating styles