使用Python制作基本动画和交互式绘图
有时,您想创建一个动态图形,该图形可以随时间变化,例如视频,或者根据交互式用户输入进行调整。这些可视化在真正显示输出如何随输入变化方面做了大量工作。在本文中,我将提供与静态图,动画和交互式图相同的数据。我要绘制的数据将来自固态物理学中使用最广泛的方程式之一:费米-狄拉克分布,它描述了固体中电子的占有率。该方程如下所示,该方程将在能量E下占据的状态分数与费米能量和温度的函数联系起来。
![Python动态可视化入门—简单绘图、动图和交互式绘图](http://p2.ttnews.xyz/loading.gif)
静态图
我们的第一个图将是一个静态图,其中在不同温度下将具有f(E)曲线。首先,我们导入所需的库:
<code># Import packages %matplotlib inline import matplotlib as mpl import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider import numpy as np/<code>
由于我们将多次计算费米-狄拉克分布,因此我们应该编写一个函数为我们进行此计算:
<code># Fermi-Dirac Distribution def fermi(E: float, E_f: float, T: float) -> float: k_b = 8.617 * (10**-5) # eV/K return 1/(np.exp((E - E_f)/(k_b * T)) + 1)/<code>
现在我们可以开始绘制数据了!首先,我将编辑一些常规绘图参数:
<code># General plot parameters mpl.rcParams['font.family'] = 'Avenir' mpl.rcParams['font.size'] = 18 mpl.rcParams['axes.linewidth'] = 2 mpl.rcParams['axes.spines.top'] = False mpl.rcParams['axes.spines.right'] = False mpl.rcParams['xtick.major.size'] = 10 mpl.rcParams['xtick.major.width'] = 2 mpl.rcParams['ytick.major.size'] = 10 mpl.rcParams['ytick.major.width'] = 2/<code>
我们创建图并向其中添加axes对象:
<code># Create figure and add axes fig = plt.figure(figsize=(6, 4)) ax = fig.add_subplot(111)/<code>
为了将变化的温度数据提供给我们的Fermi-Dirac函数,我们使用以下命令生成了一个介于100 K和1000 K之间的值的数组numpy.linspace:
<code># Temperature values T = np.linspace(100, 1000, 10)/<code>
对于每个温度值,我们需要将不同的颜色映射到其结果曲线。我们将从coolwarm颜色版生成颜色,因为我们实际上是在处理温度的变化,而且我们已经生成了上面的10个温度值,所以我们将从coolwarm 颜色版中提取10种颜色。
<code># Get colors from coolwarm colormap colors = plt.get_cmap('coolwarm', 10)/<code>
绘制数据的最简单方法是遍历所有温度值并每次绘制对应的曲线。为了生成x轴值,我们再次使用numpy.linspace创建一个数组,该数组包含一个介于0和1之间的100个均匀间隔的值。此外,我们对所有计算都使用0.5 eV的固定费米能量值。
<code># Plot F-D data for i in range(len(T)): x = np.linspace(0, 1, 100) y = fermi(x, 0.5, T[i]) ax.plot(x, y, color=colors(i), linewidth=2.5)/<code>
我们的绘图需要的最后一个元素是一种区分不同色温曲线的方法。为此,我们将通过首先创建标签列表,然后将其传递给axes.legend方法来创建图例。
<code># Add legend labels = ['100 K', '200 K', '300 K', '400 K', '500 K', '600 K', '700 K', '800 K', '900 K', '1000 K'] ax.legend(labels, bbox_to_anchor=(1.05, -0.1), loc='lower left', frameon=False, labelspacing=0.2)/<code>
labelspacing—图例条目之间的垂直间距(默认为0.5)
最后,在添加轴标签之后,我们将看到以下图:
![Python动态可视化入门—简单绘图、动图和交互式绘图](http://p2.ttnews.xyz/loading.gif)
费米能量值为0.5 eV时费米-狄拉克分布的温度依赖性
生成动图
现在说我们想提供与上述相同的数据,但作为视频呈现—我们将如何做?事实证明,我们可以很容易地做到这一点matplotlib!我们必须导入以下内容以使我们可以使用此功能:
<code>from matplotlib.animation import FuncAnimation/<code>
如果我们使用的是Jupyter Notebook,我们还应该更改matplotlib用于渲染其图形的后端,以便进行交互式绘图。
<code># Change matplotlib backend %matplotlib notebook/<code>
对于我们的动画,我们需要执行以下操作:
(1)将对绘制曲线的参考存储为变量
(2)使用带有此变量的函数调用来不断更新绘图数据
我们将首先绘制空数组并将其存储为名为f_d的变量。另外,我们将添加一个文本注释以显示当前图显示的温度,因此我们还将存储对此的变量引用。们将文本注释的右上角对齐到axis对象的右上角。
<code># Create variable reference to plot f_d, = ax.plot([], [], linewidth=2.5) # Add text annotation and create variable reference temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24)/<code>
现在,我们的动画的主力—动画函数,该函数将获取索引的输入,i并在每次调用时更新图。它还将使用当前温度更新文本注释,并且基于颜色图中的相同颜色来更改绘图和文本的coolwarm颜色。
<code># Animation function def animate(i): x = np.linspace(0, 1, 100) y = fermi(x, 0.5, T[i]) f_d.set_data(x, y) f_d.set_color(colors(i)) temp.set_text(str(int(T[i])) + ' K') temp.set_color(colors(i))/<code>
set_data(x, y)—为绘图设置新的x和y数据。
我们使用以下代码行:
<code># Create animation ani = FuncAnimation(fig, animate, frames=range(len(T)), interval=500, repeat=True)/<code>
fig—将图形传递给动画函数
func —绘图的动画函数
frames—一个从0开始的数组,代表动画的帧。在这种情况下,我们传递的长度等于要设置动画的温度的数量(这也是传递给func的索引i))。
interval —帧之间的延迟(以毫秒为单位)
repeat—是否在结束时重复播放动画
费米能量为0.5 eV时,费米-狄拉克分布的温度依赖性动画
现在,如果您的轴标签或部分绘图被切除,则可以尝试添加以下代码行,以确保所有元素都在图中。
<code># Ensure the entire plot is visible fig.tight_layout()/<code>
现在,要保存动画,我们使用以下内容:
<code># Save and show animation ani.save('AnimatedPlot.gif', writer='ImageMagick', fps=2)/<code>
writer—动画编写器程序—生成.gif文件,我使用ImageMagick
fps —动画的每秒帧数(由于我们只有10帧,因此我使用fps值为2来模拟500 ms之前的间隔延迟)
互动图
最后,如果我们想让用户使用输入参数来观察它们在输出上的变化,我们可以制作一个交互式图。我们首先导入所需的库。
<code>from matplotlib.widgets import Slider/<code>
我们再次从创建图形和轴对象开始以保存绘图。但是,这一次,我们调整图的大小以为要添加的滑块腾出空间。
<code># Create main axis ax = fig.add_subplot(111) fig.subplots_adjust(bottom=0.2, top=0.75)/<code>
figure.subplots_adjust()接受顶部、底部、左侧和右侧的输入,以指示在何处绘制轴边框的四个角。在本例中,我们要确保顶部不超过0.75,这样才能将滑块放置在绘图区的顶部。
滑块就像其他任何轴对象一样开始。在这里,我们把两个都加到图上(一个用来改变费米能量,一个用来改变温度)。此外,由于我们更改了全局图设置以删除右侧和顶部,所以我们将把它们添加回这里作为滑块。
<code># Create axes for sliders ax_Ef = fig.add_axes([0.3, 0.85, 0.4, 0.05]) ax_Ef.spines['top'].set_visible(True) ax_Ef.spines['right'].set_visible(True) ax_T = fig.add_axes([0.3, 0.92, 0.4, 0.05]) ax_T.spines['top'].set_visible(True) ax_T.spines['right'].set_visible(True)/<code>
现在,我们必须将这些轴对象变成滑块:
<code># Create sliders s_Ef = Slider(ax=ax_Ef, label='Fermi Energy ', valmin=0, valmax=1.0, valinit=0.5, valfmt=' %1.1f eV', facecolor='#cc7000') s_T = Slider(ax=ax_T, label='Temperature ', valmin=100, valmax=1000, valinit=300, valfmt=' %i K', facecolor='#cc7000')/<code>
ax —将轴对象转换为滑块
label —滑块左侧的滑块标签
valmin —滑块的最小值
valmax —滑块的最大值
valfmt—要显示为滑块值的字符串,位于右侧。%1.1f是具有1个小数点的浮点数,并且%i是整数
facecolor —填充滑块的颜色
现在,我们已经创建了滑块,让我们绘制“默认”数据集,该数据集将在首次加载图形时显示(0.5 eV的费米能量和300 K的温度):
<code># Plot default data x = np.linspace(-0, 1, 100) Ef_0 = 0.5 T_0 = 300 y = fermi(x, Ef_0, T_0) f_d, = ax.plot(x, y, linewidth=2.5)/<code>
就像在动图中一样,我们现在将定义update函数,该函数将在更新滑块时更改数据。此update函数获取滑块的当前值,更改绘图中的数据,然后重新绘制图形。
<code># Update values def update(val): Ef = s_Ef.val T = s_T.val f_d.set_data(x, fermi(x, Ef, T)) fig.canvas.draw_idle() s_Ef.on_changed(update) s_T.on_changed(update)/<code>
Slider.on_changed(func)func更改滑块值时调用更新。
费米-狄拉克分布的温度依赖性交互式图
结论
希望本文能够展示如何利用动图和交互式滑块使数据可视化动态化。
關鍵字: Ef 动图 matplotlib