Author : zbzhen, Modified : Fri Nov 1 01:13:45 2024
目标:
和LaTex一样, 绘图也不难, 难的是找到一个顺手且实用的模板
推荐用保存mp4文件的方法
中文支持, 只需加上代码
plt.rcParams["font.sans-serif"]=["SimHei"] plt.rcParams["axes.unicode_minus"]=False
矢量图推荐用pdf
格式或svg
格式, 建议在保存代码之前加上fig.tight_layout()
import numpy as np import matplotlib.pyplot as plt fig = plt.figure(figsize=(10,8), dpi=72,facecolor="w") ax = plt.subplot(111) x = np.linspace(-np.pi, np.pi, 13) plt.plot(x, np.sin(x)+0.5, "v", label="polyline", linestyle='dashed') x = np.linspace(-np.pi, np.pi, 100) plt.gca().set_aspect('equal',adjustable='box') # 设置坐标轴比例为1:1 plt.plot(x, np.sin(x)+0.5, label="curve: $\sin(x)+\\dfrac{1}{2}$") plt.legend() fig.tight_layout() plt.savefig("test0.svg") plt.show()
import numpy as np import matplotlib.pyplot as plt import matplotlib.font_manager as mft from matplotlib.ticker import MultipleLocator, FormatStrFormatter plt.rcParams["font.sans-serif"]=["SimHei"] #支持中文 plt.rcParams["axes.unicode_minus"]=False #负号变短 fig = plt.figure(figsize=(10,8), dpi=72,facecolor="w") ax = plt.subplot(111) x = np.linspace(-np.pi, np.pi, 13) plt.plot(x, np.sin(x)+0.5, "v", label="polyline", linestyle='dashed') x = np.linspace(-np.pi, np.pi, 100) plt.plot(x, np.sin(x)+0.5, label="curve: $\sin(x)+\\dfrac{1}{2}$") ### 设置坐标范围 # plt.xlim(-3.3, 3.3) # plt.ylim(-0.6, 1.6) # ax.set_xticks(np.pi/2*np.array([-2,-1,0,1,2])) # 也可以指定x轴主刻度 xmajorLocator = MultipleLocator(np.pi/2) #x轴主刻度 ax.xaxis.set_major_locator(xmajorLocator) xminorLocator = MultipleLocator(np.pi/8) #x轴次刻度 ax.xaxis.set_minor_locator(xminorLocator) # 刻度字体 fontz = 20 for tick in ax.yaxis.get_major_ticks(): tick.label1.set_fontsize(fontz) for tick in ax.xaxis.get_major_ticks(): tick.label1.set_fontsize(fontz) #设置x轴标签文本的格式 xmajorFormatter = FormatStrFormatter('%0.2f') ax.xaxis.set_major_formatter(xmajorFormatter) plt.title("Title", fontsize=fontz) plt.grid() # ax.xaxis.grid(True, which='minor') #x坐标轴的网格使用主刻度 # ax.yaxis.grid(True, which='major') #y坐标轴的网格使用次刻度 plt.legend(prop = mft.FontProperties(size=fontz)) plt.xlabel("$x$", fontsize = fontz) plt.ylabel("$y$", fontsize = fontz) fig.tight_layout() plt.savefig("test1.svg") plt.show()
import numpy as np import matplotlib.pyplot as plt import matplotlib.font_manager as mft from matplotlib.ticker import MultipleLocator, FormatStrFormatter # from pylab import mpl # mpl.style.use('classic') config = { "font.family":'serif', "font.size": 20, "font.sans-serif": "SimHei", # "mathtext.fontset":'cm', "font.serif":'SimSun' } plt.rcParams.update(config) fig = plt.figure(figsize=(10,8), dpi=72,facecolor="w") ax = plt.subplot(111) x = np.linspace(-np.pi, np.pi, 13) plt.plot(x, np.sin(x)+0.5, "v", label="polyline", linestyle='dashed') x = np.linspace(-np.pi, np.pi, 100) plt.plot(x, np.sin(x)+0.5, label="curve: $\sin(x)+\\dfrac{1}{2}$") plt.xticks(np.pi/2*np.array([-2,-1,0,1,2]), ["$-\pi$", "$-\\dfrac{\pi}{2}$", "$0$", "$\\dfrac{\pi}{2}$", "$\pi$"]) ran = np.arange(5)*0.5-0.5 plt.yticks(ran) ymajorLocator = MultipleLocator(0.5) ax.yaxis.set_major_locator(ymajorLocator) plt.yticks(fontsize=20) plt.title("Title") plt.grid() plt.legend(loc='upper left') plt.xlabel("$x$") plt.ylabel("$y$") fig.tight_layout() plt.savefig("test2.svg") plt.show()
该方法只能在本地运行, 并且不能用于jupyter
import numpy as np import matplotlib.pyplot as plt def f(x, y): return np.sin(x) + np.cos(y) x = np.linspace(0, 2 * np.pi, 120) y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) plt.ion() # Ensure that redrawing is possible fig, ax = plt.subplots() im = plt.imshow(f(x, y)) fig.show() for i in range(20): x += np.pi / 15. y += np.pi / 20. z = f(x, y) im.set_array(z) tt = (5-len(str(i)))*'0' + str(i) plt.title(tt) plt.pause(0.1) # plt.savefig('./fig/'+tt+'.png') fig.canvas.draw() plt.show()
或者下面的方法, 只能在本地运行, 并且不能用于jupyter
import matplotlib.pyplot as plt import numpy as np # 初始化图形和轴对象 fig, ax = plt.subplots() # 循环次数,例如10次 for i in range(10): # 清除之前的图形 ax.clear() # 生成新的数据 x = np.linspace(0, 4 * np.pi, 100) y = np.sin(x + i * np.pi / 5) # 绘制新的数据 ax.plot(x, y) # 设置图形的标题 ax.set_title(f"Frame {i}") # 显示图形 plt.pause(0.1) # 暂停一段时间,以便图形可以更新 # 显示最终的图形 plt.show()
该方法只能在本地运行, 在jupyter中会生成很多文件
import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 10, 100) y = np.cos(x) fig = plt.figure() for p in range(50): x += p*0.01 y=np.cos(x) plt.plot(x,y) plt.draw() plt.pause(0.1) fig.clear()
该方法只能在jupyter中跑, 其实显示效果并不好, 并且会清除输出
from matplotlib import pyplot as plt from IPython.display import clear_output import numpy as np for i in range(50): clear_output(wait=True) y = np.random.random([10,1]) plt.plot(y) plt.show()
该方法只能在jupyter中跑, 并非实时更新, 但是最后可以生成一个动态交互图
# https://matplotlib.org/2.0.2/examples/animation/index.html import matplotlib.animation import matplotlib.pyplot as plt import numpy as np plt.rcParams["animation.html"] = "jshtml" plt.rcParams['figure.dpi'] = 150 plt.ioff() fig, ax = plt.subplots() x= np.linspace(0,10,100) def animate(t): plt.cla() plt.plot(x-t,x) plt.xlim(0,10) matplotlib.animation.FuncAnimation(fig, animate, frames=10)
最容易想到的办法是把绘图数据保存起来, 后续再导入数据绘图, 但是这种方案只能针对于数据量小的情形, 稍微大一点的模型, 数据文件会特别大.
另外一种方案是, 一边跑数据, 一边把数据写入.mp4
视频文件中, 这种方案的优势体现在:
可以通过dpi
的设置, 控制.mp4
视频的清晰度和大小
.mp4
文件体积相对数据文件的体积要小很多
.mp4
文件具有可控性, 也很方便观察, 如果用Python开启服务器的http服务, 则可以直接在浏览器中播放服务器中的mp4
视频. 这种方法很简单, 只需在服务器的命令行中输入
python3 -m http.server 8889
组合快捷键 Ctrl + C 可关闭. 然后在本地浏览器中打开网址 http:10.26.6.81:8889可看视频, 这里的10.26.6.81
为内网ip地址, 根据实际情况修改.
在jupyter中, 可在markdown里插入下面代码, 直接显示视频
<video width="600" height="300" autoplay="flase" preload="none" controls="controls" src="http://localhost:8889/writer_test.mp4" > </video>
上面scr
里的视频地址也可以用mp4
绝对路径, 相对路径似乎会出问题
预想方案, 对于超大型数据, 可以考虑用直播
的原理, 实时更新视频画面, 感觉没什么必要, 因为要分享和交流, 最后还是得保存.mp4
文件. 如果真的是数据量超大, 可以考虑保存多个.mp4
文件. 最后用这些视频拼接成一个视频.
3D动图(尤其是3D交互动图)无论用哪种方案, 都比较麻烦, 主要原因是数据量太大
该方法可能会适合某些特殊的场合
下面例子里, 保存的数据文件超过1Mb
import numpy as np import matplotlib.pyplot as plt ################################ ### This codes run on remote ### x = np.linspace(0, 2 * np.pi, 300) y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) with open('test.npy', 'wb') as f: for i in range(20): x += i*np.pi / 150. y += i*np.pi / 200. z = np.sin(x) + np.cos(y) z = np.array(z, dtype=np.float16) # np.float32 np.save(f, z) ### This codes run on remote ### ################################ ################################ ### This codes run on local ### import numpy as np import matplotlib.pyplot as plt plt.ion() # Ensure that redrawing is possible fig, ax = plt.subplots() f = open('test.npy', 'rb') z = np.load(f) dtype = np.float32 z = np.array(z, dtype=dtype) im = plt.imshow(z) fig.show() for i in range(19): z = np.load(f) z = np.array(z, dtype=dtype) im.set_array(z) tt = (5-len(str(i)))*'0' + str(i) plt.title(tt) fig.canvas.draw() plt.show() f.close() ### This codes run on local ### ################################
这里推荐借助免费开源的ffmpeg
生成.mp4
文件, 文件不到100M
只需下载解压再添加环境变量. 以linux为例:
在~/.bashrc
文件最后面添加一行代码
PATH=$PATH: 解压后的ffmpeg所在路径
然后命令行输入source ~/.bashrc
生效
需要安装ffmpeg, 下面的例子保存的.mp4
文件不到40kb
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as manimation # 万能方法: 清除每次绘制后的fig, 虽然略微慢一点, 但是非常实用 # fps太小的话, 在linux中容易出bug FFMpegWriter = manimation.writers['ffmpeg'] writer = FFMpegWriter(fps=8) fig = plt.figure() x = np.linspace(0, 2 * np.pi, 300) y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) with writer.saving(fig, "writer_test.mp4", 100): for i in range(30): x += i*np.pi / 150. y += i*np.pi / 200. z = np.sin(x) + np.cos(y) plt.imshow(z) plt.title(str(i)) plt.xlabel(r"x", fontsize = 15) plt.ylabel(r"y", fontsize = 15) fig.tight_layout() writer.grab_frame() fig.clear()
上面的方法原理是: 每一帧图都完全重画, 可能会略微影响效率, 但真的非常省事, 所以就不考虑别的方案了.
推荐使用保存mp4文件的方法, 主要优势: 支持本地和服务器, 也支持jupyter, 代码的可读性和可修改性也强, 并且生成的mp4文件非常实用.
缺陷表现在:
总之, 缺陷可以接受, 习惯了就好说
Ref.