Skip to content

Commit ca178ee

Browse files
Refactor Jupyter Book to use literalinclude for Python code
- Replaced all inline Python code blocks with literalinclude directives - Now references external .py files from ../../python/ directory - Benefits: - Single source of truth for all Python code - Code changes automatically reflected in the book - Consistent with LaTeX approach using \pyfile{} - Easier testing and maintenance - Updated all 10 chapters (100+ code blocks converted) - Successfully tested build with literalinclude working properly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e148f2a commit ca178ee

15 files changed

Lines changed: 266 additions & 2065 deletions

File tree

.DS_Store

2 KB
Binary file not shown.

figures/.DS_Store

2 KB
Binary file not shown.
5.79 KB
Binary file not shown.

jupyterbook/chapter1/index.md

Lines changed: 14 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,14 @@ Matplotlib offers two interfaces: a MATLAB-style interface and the more cumberso
44

55
The MATLAB-style interface looks like the following.
66

7-
```python
8-
import matplotlib.pyplot as plt
9-
x = 1,0
10-
y = 0,1
11-
12-
plt.plot(x,y)
13-
plt.title("My Plot")
7+
```{literalinclude} ../../python/matlab-plot.py
8+
:language: python
149
```
1510

1611
The object-oriented interface looks like this.
1712

18-
```python
19-
fig, ax = plt.figure(), plt.axes()
20-
ax.plot(x,y)
21-
ax.set_title("My Plot")
13+
```{literalinclude} ../../python/oop-plot.py
14+
:language: python
2215
```
2316

2417
There is no such thing as a free lunch, so you will observe this interface requires more code to do the same exact thing. Its virtues will be more apparent later. Object-oriented programming (OOP) also requires some new vocabulary. OOP might be contrasted with procedural programming as another common method of programming. In procedural programming, the MATLAB-style interface being an example, the data and code are separate and the programmer creates procedures that operate on the program's data. OOP instead focuses on the creation of *objects* which encapsulate both data and procedures.
@@ -29,10 +22,8 @@ An object's data are called its *attributes* and the procedures or functions are
2922

3023
A plot requires a figure object and an axes object, typically defined as `fig` and `ax`. The figure object is the top level container. In many cases like in the above, you'll define it at the beginning of your code and never need to reference it again, as plotting is usually done with axes methods. A commonly used figure parameter is `figsize`, to which you can pass a sequence to alter the size of the figure. Both the figure and axes objects have a `facecolor` parameter which might help to illustrate the difference between the axes and figure.
3124

32-
```python
33-
fig = plt.figure(figsize = (2,3),
34-
facecolor = 'gray')
35-
ax = plt.axes(facecolor = 'lightyellow')
25+
```{literalinclude} ../../python/figparams.py
26+
:language: python
3627
```
3728

3829
![Figure and axes with different face colors](images/figparams.png)
@@ -41,16 +32,8 @@ The axes object, named `ax` by convention, gets more use in most programs. In pl
4132

4233
This wishful coding won't take you everywhere though. For example, `plt.xlim()` is replaced by `ax.set_xlim()` to set the x-axis view limits. To modify the title, `plt.title()` is replaced with `ax.set_title()` and there is `ax.get_title()` simply to get the title. The axes object also happens to have a `title` attribute, which is only used to access the title, similar to the `get_title()` method. Many matplotlib methods can be classified as *getters* or *setters* like for these title methods. The plot method and its logic is different. Later calls of `ax.plot()` don't overwrite earlier calls and there is not the same getter and setter form. There's a `plot()` method but no single `plot` attribute being mutated. Whatever has been plotted can be retrieved, or gotten (getter'd?), but it's more complicated and rarely necessary. Use the code below to see what happens with two calls of `plot()` and two calls of `set_title()`. The second print statement demonstrates that the second call of `set_title()` overwrites the title attribute, but a second plot does not nullify the first.
4334

44-
```python
45-
x = np.linspace(0,1,2)
46-
fig, ax = plt.figure(figsize = (8,4)), plt.axes()
47-
ax.plot(x, x)
48-
ax.plot(x, 1 - x)
49-
ax.set_title("My Chart")
50-
print(ax.title)
51-
print(ax.get_title()) # Similar to above line
52-
ax.set_title("My Wholesome Chart")
53-
print(ax.get_title()) # long
35+
```{literalinclude} ../../python/gettersetter.py
36+
:language: python
5437
```
5538

5639
![Getters and setters example](images/gettersetter.png)
@@ -63,59 +46,24 @@ Axes methods `set_xlim()` and `get_xlim()` behave just like `set_title()` and `g
6346

6447
You can also mix the interfaces. Use `plt.gca()` to *g*et the *c*urrent *a*xis. Use `plt.gcf()` to *g*et the *c*urrent *f*igure.
6548

66-
```python
67-
x = np.linspace(0,1,2)
68-
plt.plot(x,x)
69-
plt.title("My Chart")
70-
71-
ax = plt.gca()
72-
print(ax.title)
73-
74-
ax.plot(x, 1 - x)
75-
ax.set_title('My Wholesome Chart')
76-
print(ax.title)
49+
```{literalinclude} ../../python/chart.py
50+
:language: python
7751
```
7852

7953
![Mixing interfaces example](images/chart.png)
8054

8155
In the above, we started with MATLAB and then converted to object-oriented. We can also go in the opposite direction, though it's not always ideal, especially when working with subplots. Below, we start with our figure and axes objects, and then revert back to the MATLAB style with the `axvline()` functions (producing vertical lines across the axes), toggling off the axis lines and labels, and then saving the figure. This graph would appear unchanged if you replaced `plt.axvline()` with `ax.axvline()`, `plt.axis()` with `ax.axis()`, and `fig.savefig()` would do the same as `plt.savefig()`.
8256

83-
```python
84-
# OOP Start
85-
fig, ax = plt.figure(figsize = (8,5)), plt.axes()
86-
87-
x = np.linspace(0,100,2)
88-
ax.plot(x, x, color = 'gray')
89-
90-
ax.set_xlim([0,100])
91-
ax.set_ylim([0,100])
92-
93-
# Back to pyplot functions
94-
for i in range(101):
95-
plt.axvline(i,0, i / 100, color = 'C' + str(i))
96-
plt.axvline(i, i/100, 1, color = 'C' + str(i+5))
97-
98-
plt.axis('off')
99-
plt.savefig('colorful.pdf')
57+
```{literalinclude} ../../python/colorful.py
58+
:language: python
10059
```
10160

10261
![Colorful vertical lines](images/colorful.png)
10362

10463
Matplotlib is also integrated into pandas, with a `plot()` method for both Series and DataFrame objects, among other functionalities. There is excellent documentation [available](https://pandas.pydata.org/pandas-docs/stable/user_guide/visualization.html). These plots can be mixed with the object-oriented interface. You can use a plot method and specify the appropriate axes object as an argument. Below we import the iris dataset and make a boxplot with a mix of axes methods and then pyplot functions.
10564

106-
```python
107-
from sklearn.datasets import load_iris
108-
data = load_iris()['data']
109-
df = pd.DataFrame(data)
110-
111-
fig, ax = plt.figure(), plt.axes()
112-
113-
df.plot.box(ax = ax)
114-
ax.yaxis.grid(True)
115-
ax.xaxis.grid(False)
116-
117-
plt.tight_layout()
118-
plt.savefig('irisbox.pdf')
65+
```{literalinclude} ../../python/irisbox.py
66+
:language: python
11967
```
12068

12169
![Iris dataset boxplot](images/irisbox.png)

0 commit comments

Comments
 (0)