1""" 2======================= 3The Lifecycle of a Plot 4======================= 5 6This tutorial aims to show the beginning, middle, and end of a single 7visualization using Matplotlib. We'll begin with some raw data and 8end by saving a figure of a customized visualization. Along the way we'll try 9to highlight some neat features and best-practices using Matplotlib. 10 11.. currentmodule:: matplotlib 12 13.. note:: 14 15 This tutorial is based off of 16 `this excellent blog post <http://pbpython.com/effective-matplotlib.html>`_ 17 by Chris Moffitt. It was transformed into this tutorial by Chris Holdgraf. 18 19A note on the Object-Oriented API vs Pyplot 20=========================================== 21 22Matplotlib has two interfaces. The first is an object-oriented (OO) 23interface. In this case, we utilize an instance of :class:`axes.Axes` 24in order to render visualizations on an instance of :class:`figure.Figure`. 25 26The second is based on MATLAB and uses a state-based interface. This is 27encapsulated in the :mod:`pyplot` module. See the :doc:`pyplot tutorials 28</tutorials/introductory/pyplot>` for a more in-depth look at the pyplot 29interface. 30 31Most of the terms are straightforward but the main thing to remember 32is that: 33 34* The Figure is the final image that may contain 1 or more Axes. 35* The Axes represent an individual plot (don't confuse this with the word 36 "axis", which refers to the x/y axis of a plot). 37 38We call methods that do the plotting directly from the Axes, which gives 39us much more flexibility and power in customizing our plot. See the 40:ref:`object-oriented examples <api_examples>` for many examples of how 41this approach is used. 42 43.. note:: 44 45 In general, try to use the object-oriented interface over the pyplot 46 interface. 47 48Our data 49======== 50 51We'll use the data from the post from which this tutorial was derived. 52It contains sales information for a number of companies. 53""" 54 55# sphinx_gallery_thumbnail_number = 10 56import numpy as np 57import matplotlib.pyplot as plt 58from matplotlib.ticker import FuncFormatter 59 60data = {'Barton LLC': 109438.50, 61 'Frami, Hills and Schmidt': 103569.59, 62 'Fritsch, Russel and Anderson': 112214.71, 63 'Jerde-Hilpert': 112591.43, 64 'Keeling LLC': 100934.30, 65 'Koepp Ltd': 103660.54, 66 'Kulas Inc': 137351.96, 67 'Trantow-Barrows': 123381.38, 68 'White-Trantow': 135841.99, 69 'Will LLC': 104437.60} 70group_data = list(data.values()) 71group_names = list(data.keys()) 72group_mean = np.mean(group_data) 73 74############################################################################### 75# Getting started 76# =============== 77# 78# This data is naturally visualized as a barplot, with one bar per 79# group. To do this with the object-oriented approach, we'll first generate 80# an instance of :class:`figure.Figure` and 81# :class:`axes.Axes`. The Figure is like a canvas, and the Axes 82# is a part of that canvas on which we will make a particular visualization. 83# 84# .. note:: 85# 86# Figures can have multiple axes on them. For information on how to do this, 87# see the :doc:`Tight Layout tutorial 88# </tutorials/intermediate/tight_layout_guide>`. 89 90fig, ax = plt.subplots() 91 92############################################################################### 93# Now that we have an Axes instance, we can plot on top of it. 94 95fig, ax = plt.subplots() 96ax.barh(group_names, group_data) 97 98############################################################################### 99# Controlling the style 100# ===================== 101# 102# There are many styles available in Matplotlib in order to let you tailor 103# your visualization to your needs. To see a list of styles, we can use 104# :mod:`pyplot.style`. 105 106print(plt.style.available) 107 108############################################################################### 109# You can activate a style with the following: 110 111plt.style.use('fivethirtyeight') 112 113############################################################################### 114# Now let's remake the above plot to see how it looks: 115 116fig, ax = plt.subplots() 117ax.barh(group_names, group_data) 118 119############################################################################### 120# The style controls many things, such as color, linewidths, backgrounds, 121# etc. 122# 123# Customizing the plot 124# ==================== 125# 126# Now we've got a plot with the general look that we want, so let's fine-tune 127# it so that it's ready for print. First let's rotate the labels on the x-axis 128# so that they show up more clearly. We can gain access to these labels 129# with the :meth:`axes.Axes.get_xticklabels` method: 130 131fig, ax = plt.subplots() 132ax.barh(group_names, group_data) 133labels = ax.get_xticklabels() 134 135############################################################################### 136# If we'd like to set the property of many items at once, it's useful to use 137# the :func:`pyplot.setp` function. This will take a list (or many lists) of 138# Matplotlib objects, and attempt to set some style element of each one. 139 140fig, ax = plt.subplots() 141ax.barh(group_names, group_data) 142labels = ax.get_xticklabels() 143plt.setp(labels, rotation=45, horizontalalignment='right') 144 145############################################################################### 146# It looks like this cut off some of the labels on the bottom. We can 147# tell Matplotlib to automatically make room for elements in the figures 148# that we create. To do this we'll set the ``autolayout`` value of our 149# rcParams. For more information on controlling the style, layout, and 150# other features of plots with rcParams, see 151# :doc:`/tutorials/introductory/customizing`. 152 153plt.rcParams.update({'figure.autolayout': True}) 154 155fig, ax = plt.subplots() 156ax.barh(group_names, group_data) 157labels = ax.get_xticklabels() 158plt.setp(labels, rotation=45, horizontalalignment='right') 159 160############################################################################### 161# Next, we'll add labels to the plot. To do this with the OO interface, 162# we can use the :meth:`axes.Axes.set` method to set properties of this 163# Axes object. 164 165fig, ax = plt.subplots() 166ax.barh(group_names, group_data) 167labels = ax.get_xticklabels() 168plt.setp(labels, rotation=45, horizontalalignment='right') 169ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company', 170 title='Company Revenue') 171 172############################################################################### 173# We can also adjust the size of this plot using the :func:`pyplot.subplots` 174# function. We can do this with the ``figsize`` kwarg. 175# 176# .. note:: 177# 178# While indexing in NumPy follows the form (row, column), the figsize 179# kwarg follows the form (width, height). This follows conventions in 180# visualization, which unfortunately are different from those of linear 181# algebra. 182 183fig, ax = plt.subplots(figsize=(8, 4)) 184ax.barh(group_names, group_data) 185labels = ax.get_xticklabels() 186plt.setp(labels, rotation=45, horizontalalignment='right') 187ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company', 188 title='Company Revenue') 189 190############################################################################### 191# For labels, we can specify custom formatting guidelines in the form of 192# functions by using the :class:`ticker.FuncFormatter` class. Below we'll 193# define a function that takes an integer as input, and returns a string 194# as an output. 195 196 197def currency(x, pos): 198 """The two args are the value and tick position""" 199 if x >= 1e6: 200 s = '${:1.1f}M'.format(x*1e-6) 201 else: 202 s = '${:1.0f}K'.format(x*1e-3) 203 return s 204 205formatter = FuncFormatter(currency) 206 207############################################################################### 208# We can then apply this formatter to the labels on our plot. To do this, 209# we'll use the ``xaxis`` attribute of our axis. This lets you perform 210# actions on a specific axis on our plot. 211 212fig, ax = plt.subplots(figsize=(6, 8)) 213ax.barh(group_names, group_data) 214labels = ax.get_xticklabels() 215plt.setp(labels, rotation=45, horizontalalignment='right') 216 217ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company', 218 title='Company Revenue') 219ax.xaxis.set_major_formatter(formatter) 220 221############################################################################### 222# Combining multiple visualizations 223# ================================= 224# 225# It is possible to draw multiple plot elements on the same instance of 226# :class:`axes.Axes`. To do this we simply need to call another one of 227# the plot methods on that axes object. 228 229fig, ax = plt.subplots(figsize=(8, 8)) 230ax.barh(group_names, group_data) 231labels = ax.get_xticklabels() 232plt.setp(labels, rotation=45, horizontalalignment='right') 233 234# Add a vertical line, here we set the style in the function call 235ax.axvline(group_mean, ls='--', color='r') 236 237# Annotate new companies 238for group in [3, 5, 8]: 239 ax.text(145000, group, "New Company", fontsize=10, 240 verticalalignment="center") 241 242# Now we'll move our title up since it's getting a little cramped 243ax.title.set(y=1.05) 244 245ax.set(xlim=[-10000, 140000], xlabel='Total Revenue', ylabel='Company', 246 title='Company Revenue') 247ax.xaxis.set_major_formatter(formatter) 248ax.set_xticks([0, 25e3, 50e3, 75e3, 100e3, 125e3]) 249fig.subplots_adjust(right=.1) 250 251plt.show() 252 253############################################################################### 254# Saving our plot 255# =============== 256# 257# Now that we're happy with the outcome of our plot, we want to save it to 258# disk. There are many file formats we can save to in Matplotlib. To see 259# a list of available options, use: 260 261print(fig.canvas.get_supported_filetypes()) 262 263############################################################################### 264# We can then use the :meth:`figure.Figure.savefig` in order to save the figure 265# to disk. Note that there are several useful flags we'll show below: 266# 267# * ``transparent=True`` makes the background of the saved figure transparent 268# if the format supports it. 269# * ``dpi=80`` controls the resolution (dots per square inch) of the output. 270# * ``bbox_inches="tight"`` fits the bounds of the figure to our plot. 271 272# Uncomment this line to save the figure. 273# fig.savefig('sales.png', transparent=False, dpi=80, bbox_inches="tight") 274