从一个视频中重建 Veritasium 的情节

Reconstruct Veritasium's plots from one the videos

灵感来自 Veritasium Youtube 视频之一,他在其中解释了混沌分叉图(逻辑图)。数学方程很简单:X[i+1]=R*X[i](1-X[i])

他绘制的第一张图:这个X[i]值(y轴值,在0到1的范围内)与迭代时间i(x轴值,计算次数)具有特定的 R 值(例如,R_init=2,我想在我的代码中加入一个滑块来更改 R 的值)

他绘制的第二张图(分叉图):值 R(x 轴值)与 X[i] 的平衡总体,即:它在 (根据 R 值,在 certain/many 次迭代 i 之后,X[i] 可以在有限数和无限数之间振荡——混沌!)

最终,在代码中,我想要两个并排的子图,左边一个是“第一张图”,带有用于这两个变量的滑块,右边是“第二张图”。 下面我粘贴了我的入门代码,我只尝试用 R 的滑块和 X[0] 的初始值来绘制“第一张图”(但是,当然,到目前为止它没有显示任何东西..) 如果有人能帮我完成这个项目,或者给我一些有问题的代码的建议,我将不胜感激。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider

R = 1.5
x = np.linspace(0, 100, 1)
k_init = 0.4       # initial y value

fig = plt.figure(figsize=(8, 8))
ax = plt.axes([0.125, 0.15, 0.775, 0.80])

myplot, = plt.plot(0, 0, c="royalblue")     # not really ploting anything first
plt.xlim(0, 100)
plt.ylim(0, 1)

# create slider panel & values sets
slider_r = plt.axes([0.125, 0.03, 0.775, 0.03])
slider_k = plt.axes([0.125, 0.07, 0.775, 0.03])
r_slider = Slider(slider_r, "R", 1, 20, valinit=R, valstep=0.1)
k_slider = Slider(slider_k, "k_init", 0, 1, valinit=k_init, valstep=0.05)

k = []     # y-values starts with List items
def update(*args):
    k.append(k_init)
    for i in np.arange(1,100,1):
        k_new = R*(k[i-1])*(1-(k[i-1]))    
        k.append(k_new)
    y = np.array(k)
    myplot.set_xdata(x)
    myplot.set_ydata(y)

r_slider.on_changed(update)
k_slider.on_changed(update)

update()
plt.show()

简答

  • x = np.linspace(0, 100, 1)替换为x = np.arange(100)
  • 移除k = []
  • update函数的开头添加k0, R = k_slider.val, r_slider.val
  • k.append(k_init)替换为k = [k0]

长答案

1。获取运行的模拟(无动画)

首先,让我们尝试获得一个没有滑块的工作图。以下是一些提示和一些需要解决的问题:

  • x = np.linspace(0, 100, 1)只包含一个元素,而y将包含100个值!
  • 由于 y 的最终大小已知,因此将其初始化为空数组,而不是将项目添加到列表 k。这将使代码更具可读性和更快。
  • 使用变量来存储常量值,例如轮数。
  • 在问题的描述中,您使用变量 x,而在实现中它是 y ans k:定义有意义的符号,并坚持使用它们。
  • 定义像 r_sliderslider_r 这样相似的名称是相当混乱的,特别是因为这两个变量存储完全不同的对象。
## define constants
R = 2.6
X0 = 0.5  # initial value
N = 75  # number of epochs

## logistic map iterates
def get_logistic_map(x0, R, N):
    x = np.empty(N)
    x[0] = x0
    for i in range(1, N):
        x[i] = R * x[i-1] * (1 - x[i-1])
    return x

x = get_logistic_map(X0, R, N)

## plot
plt.figure(figsize=(7, 4))
plt.plot(x, 'o-', c="royalblue", ms=2)
plt.ylim(0, 1)
plt.margins(x=.01)
plt.grid(c="lightgray")
plt.xlabel(r"$n$")
plt.ylabel(r"$x_n$")
plt.show()

2。让我们引入与滑块的交互

函数中update:

  • 您应该使用滑块的新值更新显示的数据。通常当更新滑块时,它会将其新值作为参数提供给回调函数(此处为 update 函数)。由于此处两个滑块链接到同一个更新函数,因此您不能使用此输入参数。但是,您可以使用每个滑块的属性 val 访问这些值。 (并且由于此类回调函数需要一个参数,因此您可以适当地使用 *args ;)。
  • 我不确定这是否是有意的,但 k 是一个全局列表。所以每次 update 被调用 100 新的 值被添加。
## Prepare figure layout
fig = plt.figure(figsize=(7, 5))
ax = plt.axes([0.125, 0.15, 0.775, 0.80])
line, = plt.plot(range(N), np.zeros(N), 'o-', c="royalblue", ms=2)
# set the layout of the main axes before defining the axes of the sliders
plt.ylim(0, 1)
plt.xlim(0, N-1)

## Create sliders
ax_slider_x0 = plt.axes([0.125, 0.03, 0.775, 0.03])
ax_slider_r = plt.axes([0.125, 0.07, 0.775, 0.03])

slider_x0 = Slider(ax_slider_x0, r"$x_0$", 0, 1, valinit=X0)
slider_r = Slider(ax_slider_r, r"$R$", 0, 4, valinit=R)
# plt.sca(ax) # uncomment to set the main axes as the current one

def update(*args):
    x0_val, r_val = slider_x0.val, slider_r.val
    x = get_logistic_map(x0_val, r_val, N)
    line.set_ydata(x)
    # Set the title on the main axes (plt.title would have added a
    # title on the current axes (by default the last one to be defined)
    ax.set_title(rf"Logistic map with $R$={r_val:.3f} and $x_0={x0_val:.3f}$")

slider_x0.on_changed(update)
slider_r.on_changed(update)

update() # initialize the plot
plt.show()

A personal tip – To debug a callback function with sliders, it is tempting to scatter some print. But as this turns out to be fruitless, you can instead display the debugging text inside the title of the plot!

3。接下来是什么?

现在第一个情节是互动的。对于分叉图,我没有看到可以添加的交互性。