Professional Documents
Culture Documents
Vẽ đồ thị ảnh động trong Python với thư viện Animation trong Matplotlib.odt
Vẽ đồ thị ảnh động trong Python với thư viện Animation trong Matplotlib.odt
Thật vậy, mỗi khi chúng ta thực hiện một thuật toán, nếu các bước trong thuật toán có thể tạo thành một hình ảnh, đặc biệt là hình ảnh động nó
giúp chúng ta hiểu thuật toán đó nhanh chóng. Bài viết này hướng dẫn các bạn thực hiện vẽ các đồ thị, biểu đồ dạng hình ảnh động có thể lưu
thành file ảnh động gif hoặc dưới dạng video mp4 khi sử dụng thư viện Matplotlib trong Python. Ví dụ ảnh động dưới đây là đồ thị động của một
sóng hình sin có phương trình dạng y=sin2π(x−c)y=sin2π(x−c)
Bài viết có nhiều kiến thức cơ bản liên quan đến thư viện Matplotlib, nếu bạn chưa biết nhiều về thư viện này, tham khảo bài viết Vẽ đồ thị, biểu đồ
với thư viện Matplotlib.
Ảnh động, video hay hoạt hình bản chất là việc hiển thị các ảnh tĩnh, mỗi ảnh có nội dung khác nhau được hiển thị trong một khoảng thời gian nhất
định. Nguyên lý này cũng được áp dụng khi vẽ đồ thị động trong Matplotlib, nó vẽ ra màn hình đồ thị tại các thời điểm, lưu tạm thành các frame và
cuối cùng là lưu thành chuỗi các frame dưới dạng ảnh động GIF hoặc video MP4. Tiếp theo chúng ta sẽ tìm hiểu các bước thực hiện:
%matplotlib inline
Trong phần này chúng ta import hai thư viện rất phổ biến của Python là Numpy và Matplotlib, một thư viện chuyên xử lý về mảng và một thư viện
chuyên vẽ đồ thị biểu đồ từ dữ liệu.
Câu lệnh %matplotlib inline để hiển thị các đồ thị ngay trong cell của Jupyter Notebook. Tham khảo Hướng dẫn sử dụng Jupyter Notebook công cụ
không thể thiếu khi học Python.
Bước 2: Thiết lập trục tọa độ của đồ thị
fig = plt.figure()
ax = plt.axes(xlim=(0, 4), ylim=(-2, 2))
line, = ax.plot([], [], lw=3)
Như đã tìm hiểu trong bài Vẽ đồ thị cơ bản với Matplotlib, các đối tượng trong Matplotlib được phân cấp theo hình cây. Với câu lệnh đầu tiên,
chúng ta có biến fig chứa đối tượng Figure là một khung chứa, ảnh (container) chứa đựng toàn bộ các nội dung đồ thị, biểu đồ trong đó.
Tiếp đó, biến ax chứa nhiều các đối tượng Axes, đối tượng chứa trục tọa độ và đồ thị thị, ở đây chúng ta sẽ tạo ra một trục tọa độ có trục x trong
dải [0,4] trục y trong dải [-2,2] và line là một đối tượng đồ thị là một đường nối các chuỗi điểm liên tiếp.
Bước 3: Tạo function vẽ đồ thị cho từng tập dữ liệu được gọi đến khi tạo frame.
def animate(i):
x = np.linspace(0, 4, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
Function này sẽ được gọi đến khi tạo ra các frame trong ảnh động hoặc video. Cấu trúc function sẽ được mô tả trong bước 4. Chúng ta xem nội
dung function này, chúng ta sẽ vẽ lên đồ thị hàm số y=sin2π(x−c)y=sin2π(x−c) với dữ liệu xx được tạo ra bởi thư viện Numpy là một mảng gồm
1000 phần tử cách đều nhau trong dải [0,4].
Bước 4: Gọi FuncAnimation, save ra file hoặc hiển thị
anim = FuncAnimation(fig, animate, frames=200, interval=20, blit=True)
anim.save('sin_wave.gif', writer='imagemagick')
plt.show()
Hàm FuncAnimation tạo ra các hình ảnh động bằng cách gọi lặp đi lặp lại các hàm vẽ hoạt cảnh ở bước 3. Hàm này có một số các tham số như
sau:
fig là đối tượng matplotlib.figure.Figure nơi toàn bộ đồ thị được chứa trong khung chứa này.
animate là tên function sẽ được gọi đến và thực hiện tại mỗi frame, cú pháp function này là func(frame, *fargs) trong đó tham số đầu
chứa biến là chỉ số của các frame và các tham số tiếp theo là tham số tùy chọn. Tham số thứ nhất là bắt buộc nếu blit=True.
frames: số frame.
interval: Độ trễ giữa các frame tính bằng ms, mặc định là 200ms.
blit: Tham số chỉ dẫn việc vẽ lại đồ thị chỉ xảy ra khi có thay đổi, với blit=True nó sẽ giúp việc tạo ra các đồ thị động nhanh hơn.
anim.save('sine_wave.gif', writer='imagemagick'), câu lệnh này lưu toàn bộ các frame thành một ảnh động dạng GIF. Chú ý, bạn cần cài
đặt ImageMagick nếu không sẽ có một cảnh báo về việc không tìm thấy imagemagick và chuyển sang một thư viện khác.
2. Hiển thị ảnh động hoặc video trong Jupyter Notebook
Mặc định Jupyter Notebook không hiển thị được video hoặc ảnh động, do vậy muốn hiển thị được luôn trên Jupyter Notebook chúng ta cần sử
dụng một số cách sau:
Cách 1: Chuyển đổi ảnh động thành video và gọi chức năng hiển thị video trong HTML5 để hiển thị.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
fig = plt.figure()
ax = plt.axes(xlim=(0, 4), ylim=(-2, 2))
line, = ax.plot([], [], lw=3)
def animate(i):
x = np.linspace(0, 4, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = plt.axes(xlim=(0, 4), ylim=(-2, 2))
line, = ax.plot([], [], lw=3)
def animate(i):
x = np.linspace(0, 4, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
![](sin_wave.gif)
3. Một số ví dụ về tạo ảnh động cho đồ thị trong Matplotlib
Bạn có thể tham khảo rất nhiều ví dụ thực hiện với Animation trong Matplotlib, các ví dụ này sẽ giúp bạn thành thạo hơn về Matplotlib và đặc biệt là
thực hiện các ảnh động giúp minh họa cho các thuật toán một cách hiệu quả hơn.
3.1 Ví dụ 1:
Vẽ một tập hợp điểm ngẫu nhiên trong một dải giữa hai đường song song và vẽ một đường thẳng động chạy giữa hai đường này.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(-5, 25))
def animate(i):
label = 'timestep {0}'.format(i)
line.set_ydata(x - 5 + i)
ax.set_xlabel(label)
return line, ax
anim = FuncAnimation(fig, animate, frames=10, interval=50)
anim.save('line.gif', writer='imagemagick')
Kết quả như ảnh động sau:
3.2 Ví dụ 2: Vẽ đường xoắn ốc
Thực hiện vẽ một đường xoắn ốc, trong toán học đường xoắn ốc có phương trình hệ tọa độ cực là r=aeθcotbr=aeθcotb. Để thực hiện vẽ đường
xoắn này, chúng ta thực hiện chuyển đổi về dạng tham số như sau
x(t)=aebtcostx(t)=aebtcost
y(t)=aebtsinty(t)=aebtsint
fig = plt.figure()
ax = plt.axes(xlim=(-200, 200), ylim=(-200, 200))
line, = ax.plot([], [], lw=2)
def animate(i):
t = 0.06*i
a = 1.27
b = 0.35
x = a*math.exp(b*t)*np.sin(t)
y = a*math.exp(b*t)*np.cos(t)
xdata.append(x)
ydata.append(y)
line.set_data(xdata, ydata)
return line,
anim.save('vi-du-2.gif', writer='imagemagick')
Kết quả như sau:
3.3 Ví dụ mô phỏng con lắc đôi
Đây là một ví dụ tương đối phức tạp về tính toán quỹ đạo con lắc đôi, là một ví dụ hay về mô phỏng nếu bạn có thể tìm hiểu kỹ, nó sẽ giúp bạn có
thêm nhiều kiến thức lập trình Python.
class DoublePendulum:
"""Double Pendulum Class
def position(self):
"""compute the current x,y positions of the pendulum arms"""
(L1, L2, M1, M2, G) = self.params
x = np.cumsum([self.origin[0],
L1 * sin(self.state[0]),
L2 * sin(self.state[2])])
y = np.cumsum([self.origin[1],
-L1 * cos(self.state[0]),
-L2 * cos(self.state[2])])
return (x, y)
def energy(self):
"""compute the energy of the current state"""
(L1, L2, M1, M2, G) = self.params
x = np.cumsum([L1 * sin(self.state[0]),
L2 * sin(self.state[2])])
y = np.cumsum([-L1 * cos(self.state[0]),
-L2 * cos(self.state[2])])
vx = np.cumsum([L1 * self.state[1] * cos(self.state[0]),
L2 * self.state[3] * cos(self.state[2])])
vy = np.cumsum([L1 * self.state[1] * sin(self.state[0]),
L2 * self.state[3] * sin(self.state[2])])
return U + K
dydx = np.zeros_like(state)
dydx[0] = state[1]
dydx[2] = state[3]
return dydx
#------------------------------------------------------------
# set up initial state and global variables
pendulum = DoublePendulum([180., 0.0, -20., 0.0])
dt = 1./30 # 30 fps
#------------------------------------------------------------
# set up figure and animation
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False,
xlim=(-2, 2), ylim=(-2, 2))
ax.grid()
def init():
"""initialize animation"""
line.set_data([], [])
time_text.set_text('')
energy_text.set_text('')
return line, time_text, energy_text
def animate(i):
"""perform animation step"""
global pendulum, dt
pendulum.step(dt)
line.set_data(*pendulum.position())
time_text.set_text('time = %.1f' % pendulum.time_elapsed)
energy_text.set_text('energy = %.3f J' % pendulum.energy())
return line, time_text, energy_text
# choose the interval based on dt and the time to animate one step
from time import time
t0 = time()
animate(0)
t1 = time()
interval = 1000 * dt - (t1 - t0)
ani.save('vi-du-3.gif', writer='imagemagick')
Kết quả: