目录
用matplotlib与mpl-interactions创建可交互式函数图像
追求技术分享的展示性和复现性
很长一段时间我都在纠结于通过一种什么样的方式来展示工作成果与解决方案。这个问题之所以关键,是因为大多数情况下,我们在重复造车轮的工作上消磨了太多时间。
每当这种情况出现的时候,我便意识到站立在前人肩膀上的重要性,过度封闭的技术社区必然扼杀了许许多多潜在的创造力。
科研是一个十分具有社交性的活动,只有当人们聚在一起交流对某一个问题的见解时,才能结束技术上人人各自为政的封建时代,智慧才能在碰撞中产生,从而大大加速了成果转化过程。
所以说好的技术分享,关键在于加强人与人之间的联系,这实质上是推动科技进步的最有效手段。有很多工具的诞生都是基于这样的一个目的,如PPT、Microsoft Teams等等,它们都在技术分享活动中起到了推波助澜的作用。我认为,评价技术分享平台的指标,主要分为以下两点:
1. 是否具有好的展示性
2. 是否具有好的复现性
第一点不难理解,如果你不想让汇报无聊到使得所有人都打瞌睡,展示的过程就应当是能够打动人心的、引人入胜的、拥有较高美术水准的、图文并茂甚至是多媒体的。
举个例子而言,PPT作为传统的技术分享媒介,在很多场合下配合演讲者的现场演说是能够体现很好的展示性的。但是由于现场演示不可能对技术细节进行详尽地汇报,故演示或许能给听众带来深刻的印象,但对于站在同一高度的同行们或是刚进入这个领域的新人,他们往往难以即时地根据演示并将其进行复现,从而站立在演示者的肩膀上。这是PPT形式在第二点复现性上的缺失,此时就需要其他工具进行辅助:比如论文、或是github。
更重要的是,好的展示性将会引发人的思考,良好的排版与生动的动画往往能够让人获得启发,即便你写的东西并不是用来给别人展示的,但是当将来某一个时刻回看自己过去的作品的时候,会给你带来灵感。为将来的自己创作,是追求良好展示性的另一个意义。
技术分享的可复现性为什么重要?本质上,一个好的技术分享包含以下两个方面:1.解决问题的过程与结果。2.解决问题的思想与理念。这也就是说,我们不仅希望分享者介绍解决问题的原理和思路,更希望他能够把他创作的源代码贴出来,因为代码往往就意味着真理。如果真的希望自己的工作与研究成果被广泛地质询,从而不断改进以求应用于现实之中,而不是水一篇文章的话,放出自己的源代码就是研究人员基本的素养,否则你得技术分享的真实性将会受到质疑。当然我们也不希望只有源代码没有任何其他的解说,否则就只有机器人愿意读你的文章了。
根据以上的思考,搭建一个结合良好展示性与可复现性的工作平台,便是提高生产效率的第一步。
用matplotlib与mpl-interactions创建可交互式函数图像
一般而言,我运用python的matplotlib库进行建模工作,用来可视化地展示参数对模型的影响,更进一步地,我们希望这种影响能实时性地通过动画的方式展现在读者眼前,这就要用到matplotlib的widgets子库。使用widgets的方法可参照:https://riptutorial.com/matplotlib/example/23577/interactive-controls-with-matplotlib-widgets
下面给出一个我经常使用的利用matplotlib创建交互式图像的模板。
%matplotlib ipympl #本句在jupyter Notebook下添加,Spyter下运行则可将此句注释掉
#引用库
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.widgets import Slider, Button, RadioButtons, CheckButtons
x = np.arange(0,5,0.01)
A = 1
y = A*x**2
fig = plt.figure() #生成一副空函数图像
ax = fig.subplots() #生成坐标轴
plt.subplots_adjust(left=0.3, bottom=0.25) #调整坐标轴位置,为交互式按键、滑块等预留空间
p, = ax.plot(x,y, label='quadratic') #对函数进行plot,其中的 , 算符用于对元组进行解封,不加这个 , 会导致更新函数不起作用
ax.set_xlabel('x label') # 加入x轴标签
ax.set_ylabel('y label') # 加入y轴标签
ax.set_title("Simple Plot") # 加入标题
ax.legend() # 加入图例
#定义widgets滑块
ax_slide = plt.axes([0.25, 0.1, 0.65, 0.03]) #xposition. yposition, width and height
ax_button = plt.axes([0.1, 0.65, 0.1, 0.05]) #xposition. yposition, width and height
#widgets属性定义
grid_button = Button(ax_button, 'Grid', color='white', hovercolor='grey')
s_factor=Slider(ax_slide, 'factor A', valmin = 0.1, valmax=6, valinit=1, valstep=0.1)
#定义更新函数
def update(val):
current_v = s_factor.val
y = current_v*x**2
p.set_ydata(y)
fig.canvas.draw() #重新对函数图像进行绘制
#当slider的值有变化时更新函数图像
s_factor.on_changed(update)
def grid(val):
ax.grid()
fig.canvas.draw()
grid_button.on_clicked(grid) #触发事件
#----RADIO BUTTONS----
ax_color = plt.axes([0.02, 0.4, 0.15, 0.2])
color_button = RadioButtons(ax_color, ['red', 'green', 'blue', 'black'], [False, False, True, False], activecolor='r')
#function for changing the plot color
def color(labels):
p.set_color(labels)
fig.canvas.draw()
color_button.on_clicked(color)
#---CHECK BUTTONS----
#定义第二条要绘制的函数曲线
y1 = -1*np.sin(x)*(x**2)
p1, = ax.plot(x, y1, color='b', label = 'Plot 2', visible = False)
plots=[p, p1]
activated = [True, False]
labels = ['Plot 1', 'Plot 2']
#instance the axes
ax_check = plt.axes([0.02, 0.15, 0.08, 0.1])
plot_button = CheckButtons(ax_check,labels,activated)
# 定义用于显示和隐藏特定曲线的函数
def select_plot(label):
index = labels.index(label)
#set the plots to visible
plots[index].set_visible(not plots[index].get_visible())
fig.canvas.draw()
plot_button.on_clicked(select_plot)
plt.rcParams.update({'font.size': 26})
plt.show()
执行后效果如下:
但是用这种方法plot函数图像,首先是wedgits的触发函数无法debug,如果用户定义的触发函数出了错误,交互功能就失效了,并且python的IDE不给出任何错误信息,这就给debug带来了极大的困扰。另外更重要的是,代码的可读性较差,这让人难以接受(即损失展示性又损失复现性)。
这个时候就需要运用另一个工具——mpl-interactions库,官网上有关于这个字库的详细说明和案例(https://mpl-interactions.readthedocs.io/en/stable/index.html#),在此不多阐述。仍然还是给出一个测试程序代码:
# imports
%matplotlib ipympl
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体
matplotlib.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
import numpy as np
import math
import mpl_interactions.ipyplot as iplt
# define the function
def f(x, tau, beta):
return np.sin(x * tau) * x ** beta
x = np.linspace(0, 2 * np.pi, 1000)
tau = np.linspace(5, 10)
beta = np.linspace(0.25, 1)
# make the interactive figure
fig, ax = plt.subplots()
controls = iplt.plot(x, f, tau=tau, beta=beta) #可交互式函数定义
iplt.title("tau的值为: {tau:.2f}", controls=controls["tau"])
# you can still use plt commands if this is the active figure
plt.ylabel("生成ylabel!")
plt.xlabel("生成xlabel!")
#Jupyter Notebook支持包
import ipywidgets as widgets
out2 = widgets.Output()
display(out2)
def my_callback(tau, beta):
if tau < 7.5:
ax.tick_params(axis="x", colors="red")
else:
ax.tick_params(axis="x", colors="black")
if beta < 0.5:
ax.tick_params(axis="y", colors="black")
else:
ax.tick_params(axis="y", colors="blue")
controls.register_callback(my_callback, ["tau", "beta"], eager=True) #定义callback函数进一步提升可交互性
效果如下:
一开始我本想在plot出的函数图像中加入用Latex生成的公式和符号进行注解,但是尽管Latex能够产生非常漂亮的文本,但是导致重新绘制函数变得难以忍受的慢。matplotlib通过设置"text.usetex": False或True来取消或开启Latex的支持,以下两张图给出了他们的性能对比。
下面两张图给出了它们的对比:
"text.usetex": False时
十分流畅。
"text.usetex": True时
又慢又卡。。。。
之所以会出现这个问题,原因在于Latex代码编译生成符号或公式后,将被转化为png图片格式,再拷贝到matplotlib所绘制的图像中,这带来了很大的计算量。
一个可行的解决方法是:只在该用Latex的地方,设置usetex=True.https://github.com/matplotlib/matplotlib/issues/4880
例:
但是这仍然无法从根本上解决问题,如果要想利用Latex配以漂亮的文本对程序与模型进行解析,则需要选则在交互性和展示性上更加优秀的平台。
选用jupyter notebook作为技术分享媒介
jupyter notebook拥有非常好的交互界面,并且在运行代码的同时支持用markdown文本(支持Latex)对原理和思路进行解释,同时具备展示性和复现性两大特征。我用的jupyter notebook为6.1.4版本,在Anaconda平台上进行运行。下载好Anaconda后,便可以在windows的开始菜单直接执行jupyter notebook。同时,Anaconda带有一个名叫Spyder的python IDE,由于Spyder拥有更好dubug机制,故我习惯将代码先在Spyder下完成调试后,再放在jupyter上运行与展示。
jupyter notebook默认的开始菜单为用户的根目录,如果不想在每次打开之后都进行繁琐的找文件夹工作,可以对起始地址进行修改,具体方法参照:https://stackoverflow.com/questions/35254852/how-to-change-the-jupyter-start-up-folder
现在,我们就可以为我们的模型配上漂亮的公式作注解了!!
为了进一步提高jupyter notebook的展示性,强烈建议安装目录插件,效果如图所示:
方法参考:
https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html
https://towardsdatascience.com/jupyter-notebook-extensions-517fa69d2231
其他辅助平台与软件
github是我分享与托管代码的平台。
CSDN是我以博客的形式分享工作的地方。
至于文中生成这些.gif图片的方法,我用的软件是Gifcam,这是一个开源的可以直接以屏幕截图形式截取一段视频然后生成.gif图的软件,网址:http://blog.bahraniapps.com/,良心推荐!