Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 256da7c

Browse files
Merge branch 'whereistejas-post-pyplot-vs-object-oriented-interface'
2 parents 5a51cdf + 79c8694 commit 256da7c

File tree

7 files changed

+209
-0
lines changed

7 files changed

+209
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: "Pyplot vs Object Oriented Interface"
3+
date: 2020-05-27T20:21:30+05:30
4+
draft: false
5+
description: "This post describes the difference between the pyplot and object oriented interface to make plots."
6+
author: Tejas Sanap
7+
displayInList: true
8+
resources:
9+
- name: featuredImage
10+
src: "figure/distance-and-velocity-different-axes-finished.png"
11+
params:
12+
description: "Finished graph"
13+
showOnTop: true
14+
---
15+
16+
## Generating the data points
17+
18+
To get acquainted with the basics of plotting with `matplotlib`, let's try plotting how much distance an object under free-fall travels with respect to time and also, its velocity at each time step.
19+
20+
If, you have ever studied physics, you can tell that is a classic case of Newton's equations of motion, where
21+
22+
$$ v = a \times t $$
23+
24+
$$ S = 0.5 \times a \times t^{2} $$
25+
26+
We will assume an initial velocity of zero.
27+
28+
```python
29+
import numpy as np
30+
31+
time = np.arange(0., 10., 0.2)
32+
velocity = np.zeros_like(time, dtype=float)
33+
distance = np.zeros_like(time, dtype=float)
34+
```
35+
36+
We know that under free-fall, all objects move with the constant acceleration of $$g = 9.8~m/s^2$$
37+
38+
```python
39+
g = 9.8 # m/s^2
40+
41+
velocity = g * time
42+
distance = 0.5 * g * np.power(time, 2)
43+
```
44+
45+
The above code gives us two `numpy` arrays populated with the distance and velocity data points.
46+
47+
## Pyplot vs. Object-Oriented interface
48+
49+
When using `matplotlib` we have two approaches:
50+
1. `pyplot` interface / functional interface.
51+
2. Object-Oriented interface (OO).
52+
53+
### Pyplot Interface
54+
55+
`matplotlib` on the surface is made to imitate MATLAB's method of generating plots, which is called `pyplot`. All the `pyplot` commands make changes and modify the same figure. This is a state-based interface, where the state (i.e., the figure) is preserved through various function calls (i.e., the methods that modify the figure). This interface allows us to quickly and easily generate plots. The state-based nature of the interface allows us to add elements and/or modify the plot as we need, when we need it.
56+
57+
This interface shares a lot of similarities in syntax and methodology with MATLAB. For example, if we want to plot a blue line where each data point is marked with a circle, we can use the string `'bo-'`.
58+
59+
```python
60+
import matplotlib.pyplot as plt
61+
62+
plt.figure(figsize=(9,7), dpi=100)
63+
plt.plot(distance,'bo-')
64+
plt.xlabel("Time")
65+
plt.ylabel("Distance")
66+
plt.legend(["Distance"])
67+
plt.grid(True)
68+
```
69+
70+
The plot shows how much distance was covered by the free-falling object with each passing second.
71+
72+
![png](figure/just-distance.png)
73+
<div class="image-caption">
74+
<b>Fig. 1.1</b> The amount of distance travelled in each second is increasing, which is a direct result of increasing velocity due to the gravitational acceleration.
75+
</div>
76+
77+
```python
78+
plt.figure(figsize=(9,7), dpi=100)
79+
plt.plot(velocity,'go-')
80+
plt.xlabel("Time")
81+
plt.ylabel("Velocity")
82+
plt.legend(["Velocity"])
83+
plt.grid(True)
84+
```
85+
86+
The plot below shows us how the velocity is increasing.
87+
88+
![png](figure/just-velocity.png)
89+
<div class="image-caption">
90+
<b>Fig. 1.2</b> Velocity is increasing in fixed steps, due to a "constant" acceleration.
91+
</div>
92+
93+
Let's try to see what kind of plot we get when we plot both distance and velocity in the same plot.
94+
95+
```python
96+
plt.figure(figsize=(9,7), dpi=100)
97+
plt.plot(velocity,'g-')
98+
plt.plot(distance,'b-')
99+
plt.ylabel("Distance and Velocity")
100+
plt.xlabel("Time")
101+
plt.legend(["Distance", "Velocity"])
102+
plt.grid(True)
103+
```
104+
105+
![png](figure/distance-and-velocity-same-axes.png)
106+
107+
Here, we run into some obvious and serious issues. We can see that since both the quantities share the same axis but have very different magnitudes, the graph looks disproportionate. What we need to do is separate the two quantities on two different axes. This is where the second approach to making plot comes into play.
108+
109+
Also, the `pyplot` approach doesn't really scale when we are required to make multiple plots or when we have to make intricate plots that require a lot of customisation. However, internally `matplotlib` has an Object-Oriented interface that can be accessed just as easily, which allows to reuse objects.
110+
111+
### Object-Oriented Interface
112+
113+
When using the OO interface, it helps to know how the `matplotlib` structures its plots. The final plot that we see as the output is a 'Figure' object. The `Figure` object is the top level container for all the other elements that make up the graphic image. These "other" elements are called `Artists`. The `Figure` object can be thought of as a canvas, upon which different artists act to create the final graphic image. This `Figure` can contain any number of various artists.
114+
115+
![png](figure/anatomy-of-a-figure.png)
116+
117+
Things to note about the anatomy of a figure are:
118+
1. All of the items labelled in *blue* are `Artists`. `Artists` are basically all the elements that are rendered onto the figure. This can include text, patches (like arrows and shapes), etc. Thus, all the following `Figure`, `Axes` and `Axis` objects are also Artists.
119+
2. Each plot that we see in a figure, is an `Axes` object. The `Axes` object holds the actual data that we are going to display. It will also contain X- and Y-axis labels, a title. Each `Axes` object will contain two or more `Axis` objects.
120+
3. The `Axis` objects set the data limits. It also contains the ticks and ticks labels. `ticks` are the marks that we see on a axis.
121+
122+
Understanding this hierarchy of `Figure`, `Artist`, `Axes` and `Axis` is immensely important, because it plays a crucial role in how me make an animation in `matplotlib`.
123+
124+
Now that we understand how plots are generated, we can easily solve the problem we faced earlier. To make Velocity and Distance plot to make more sense, we need to plot each data item against a seperate axis, with a different scale. Thus, we will need one parent `Figure` object and two `Axes` objects.
125+
126+
```python
127+
fig, ax1 = plt.subplots()
128+
129+
ax1.set_ylabel("distance (m)")
130+
ax1.set_xlabel("time")
131+
ax1.plot(time, distance, "blue")
132+
133+
ax2 = ax1.twinx() # create another y-axis sharing a common x-axis
134+
135+
ax2.set_ylabel("velocity (m/s)")
136+
ax2.set_xlabel("time")
137+
ax2.plot(time, velocity, "green")
138+
139+
fig.set_size_inches(7,5)
140+
fig.set_dpi(100)
141+
142+
plt.show()
143+
```
144+
145+
![png](figure/distance-and-velocity-different-axes-unfinished.png)
146+
147+
This plot is still not very intuitive. We should add a grid and a legend. Perhaps, we can also change the color of the axis labels and tick labels to the color of the lines.
148+
149+
But, something very weird happens when we try to turn on the grid, which you can see [here](https://github.com/whereistejas/whereistejas.github.io/blob/master/assets/jupyter-nb/double-pendulum-part-1-basics-of-plotting.ipynb) at Cell 8. The grid lines don't align with the tick labels on the both the Y-axes. We can see that tick values `matplotlib` is calculating on its own are not suitable to our needs and, thus, we will have to calculate them ourselves.
150+
151+
```python
152+
fig, ax1 = plt.subplots()
153+
154+
ax1.set_ylabel("distance (m)", color="blue")
155+
ax1.set_xlabel("time")
156+
ax1.plot(time, distance, "blue")
157+
ax1.set_yticks(np.linspace(*ax1.get_ybound(), 10))
158+
ax1.tick_params(axis="y", labelcolor="blue")
159+
ax1.xaxis.grid()
160+
ax1.yaxis.grid()
161+
162+
ax2 = ax1.twinx() # create another y-axis sharing a common x-axis
163+
164+
ax2.set_ylabel("velocity (m/s)", color="green")
165+
ax2.set_xlabel("time")
166+
167+
ax2.tick_params(axis="y", labelcolor="green")
168+
ax2.plot(time, velocity, "green")
169+
ax2.set_yticks(np.linspace(*ax2.get_ybound(), 10))
170+
171+
fig.set_size_inches(7,5)
172+
fig.set_dpi(100)
173+
fig.legend(["Distance", "Velocity"])
174+
plt.show()
175+
```
176+
177+
The command `ax1.set_yticks(np.linspace(*ax1.get_ybound(), 10))` calculates the tick values for us. Let's break this down to see what is happening:
178+
1. The `np.linspace` command will create a set of `n` no. of partitions between a specified upper and lower limit.
179+
2. The method `ax1.get_ybound()` returns a list which contains the maximum and minimum limits for that particular axis (which in our case is the Y-axis).
180+
3. In python, the operator `*` acts as an unpacking operator when prepended before a `list` or `tuple`. Thus, it will convert a list `[1, 2, 3, 4]` into seperate values `1, 2, 3, 4`. This is an immensely powerful feature.
181+
4. Thus, we are asking the `np.linspace` method to divide the interval between the maximum and minimum tick values into 10 equal parts.
182+
5. We provide this array to the `set_yticks` method.
183+
184+
The same process is repeated for the second axis.
185+
186+
![png](figure/distance-and-velocity-different-axes-finished.png)
187+
188+
## Conclusion
189+
190+
In this part, we covered some basics of `matplotlib` plotting, covering the basic two approaches of how to make plots. In the next part, we will cover how to make simple animations. If you like the content of this blog post, or you have any suggestions or comments, drop me an email or tweet or ping me on IRC. Nowadays, you will find me hanging around #matplotlib on Freenode. Thanks!
191+
192+
## After-thoughts
193+
This post is part of a series I'm doing on my personal [blog](http://whereistejas.me). This series is basically going to be about how to animate stuff using python's `matplotlib` library. `matplotlib` has an excellent [documentation](https://matplotlib.org/3.2.1/contents.html) where you can find a detailed documentation on each of the methods I have used in this blog post. Also, I will be publishing each part of this series in the form of a jupyter notebook, which can be found [here](https://github.com/whereistejas/whereistejas.github.io/blob/master/assets/jupyter-nb/Part-1-basics-of-plotting.ipynb).
194+
195+
The series will have three posts which will cover:
196+
1. Part 1 - How to make plots using `matplotlib`.
197+
2. Part 2 - Basic animation using `FuncAnimation`.
198+
3. Part 3 - Optimizations to make animations faster (blitting).
199+
200+
I would like to say a few words about the methodology of these series:
201+
1. Each part will have a list of references at the end of the post, mostly leading to appropriate pages of the documentation and helpful blogs written by other people. **THIS IS THE MOST IMPORTANT PART**. The sooner you get used to reading the documentation, the better.
202+
2. The code written here, is meant to show you how you can piece everything together. I will try my best to describe the nuances of my implementations and the tiny lessons I learned.
203+
204+
## References
205+
206+
1. [Python Generators (YouTube)](https://youtu.be/bD05uGo_sVI)
207+
1. [Matplotlib: An Introduction to its Object-Oriented Interface](https://medium.com/@kapil.mathur1987/matplotlib-an-introduction-to-its-object-oriented-interface-a318b1530aed)
208+
2. [Lifecycle of a Plot](https://matplotlib.org/3.2.1/tutorials/introductory/lifecycle.html)
209+
3. [Basic Concepts](https://matplotlib.org/faq/usage_faq.html)

0 commit comments

Comments
 (0)