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