This is just a showcase of some cool matplotlib features, and also a visual treat 😎
We will, in various fashions, visualize a system of three ordinary differential equations that define the so-called Lorenz attractor:
Our figures will be defined by various parameters:
Attributions: The Lorenz function code is derived from the Matplotlib project website. The temporal animation code is entirely based on work by Ruben Esteche.
(Click blue links to navigate)
Part 1: Nested Plots
Part 2: Spatial Animation
Part 3: Temporal Animation
Appendix A: Offline code for spatial animation
Appendix B: Offline code for temporal animation
"From the beginning of chaos research until today, the unpredictability of chaos has been a central theme. It is widely believed and claimed by philosophers, mathematicians and physicists alike that chaos has a new implication for unpredictability, meaning that chaotic systems are unpredictable in a way that other deterministic systems are not. [...] However, this is not the case. [...] I will propose that the sought-after new implication of chaos for unpredictability is the following: for predicting any event, all sufficiently past events are approximately probabilistically irrelevant. [...] Chaotic behaviour is multi-faceted and takes various forms. Yet if the aim is to identify a general new implication of chaos for unpredictability, I think this is the best we can get."
Charlotte Werndl: What Are the New Implications of Chaos for Unpredictability? Brit. J. Phil. Sci. 60 (2009), 195–220)
Displayed below are 12 sets of respectively 3 solutions each.
Parameters are randomized for:
For non-mathematicians, this may facilitate developing an intuition for the interplay of the equations' parameters, and for the fundamental insight into the proximity of order and chaos.
# Import libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
display(HTML("<style>div.output_scroll { height: 999 }</style>"))
# Figure properties
fig = plt.figure(figsize=(10,120))
print("Lorenz Attractors (sigma, rho, beta )")
print("[ 3 partial solutions per subplot ]")
# Iterators and counters
plot_index = 0
for outer in range(12):
plot_index = plot_index + 1
# Projection parameters
is_pitch = 20
is_roll = 0
is_yaw = np.random.uniform(0, 90)
# Subplot properties
ax = fig.add_subplot(12, 1, plot_index, projection='3d')
ax.view_init(elev=is_pitch, azim=is_yaw, roll=is_roll)
ax.set(xticklabels=[])
ax.set(yticklabels=[])
ax.set(zticklabels=[])
ax.set_xlabel("X Axis", fontsize=7)
ax.set_ylabel("Y Axis", fontsize=7)
s_param = np.empty(3)
r_param = np.empty(3)
b_param = np.empty(3)
subplot_label = ("[" + (str(outer)) + "]: " + "Azimuth " + (str(int(is_yaw)) +"° ("))
color_list = ['blue', 'red', 'green']
series_label = ["", "", ""]
for inner in range (3):
s_param[inner]= np.random.uniform(5, 25)
r_param[inner] = np.random.uniform(5, 25)
b_param[inner] = np.random.uniform(0.1, 5)
# Lorenz function
def lorenz(xyz, *, s=s_param[inner], r=r_param[inner], b=b_param[inner]):
x, y, z = xyz
x_dot = s*(y - x)
y_dot = r*x - y - x*z
z_dot = x*y - b*z
return np.array([x_dot, y_dot, z_dot])
dt = 0.01
num_steps = 3000
xyzs = np.empty((num_steps + 1, 3)) # Need one more for the initial values
xyzs[0] = (0., 1., 1.05) # Set initial values
# Step through "time", calculating the partial derivatives at the current point
# and using them to estimate the next point
for i in range(num_steps):
xyzs[i + 1] = xyzs[i] + lorenz(xyzs[i]) * dt
# Plot partial solution
ax.plot(*xyzs.T, lw=0.5, color=color_list[inner])
# Append parameters for labeling
series_label[inner] = (color_list[inner] + ": " + str(int(s_param[inner])) + ", " +
str(int(r_param[inner])) + ", " + str(round(b_param[inner],1)) + " | ")
subplot_label = subplot_label + series_label[inner]
subplot_label = subplot_label[:-2] + ")"
ax.set_title(label=subplot_label, fontsize=10, color='DarkGrey')
#ax.plot(*xyzs.T, lw=0.5, color=(np.random.uniform(0, 0.7), np.random.uniform(0, 0.7), np.random.uniform(0, 0.7)))
plt.show()
Lorenz Attractors (sigma, rho, beta ) [ 3 partial solutions per subplot ]