Python动态可视化入门—简单绘图、动图和交互式绘图

使用Python制作基本动画和交互式绘图

有时,您想创建一个动态图形,该图形可以随时间变化,例如视频,或者根据交互式用户输入进行调整。这些可视化在真正显示输出如何随输入变化方面做了大量工作。在本文中,我将提供与静态图,动画和交互式图相同的数据。我要绘制的数据将来自固态物理学中使用最广泛的方程式之一:费米-狄拉克分布,它描述了固体中电子的占有率。该方程如下所示,该方程将在能量E下占据的状态分数与费米能量和温度的函数联系起来。

Python动态可视化入门—简单绘图、动图和交互式绘图

静态图

我们的第一个图将是一个静态图,其中在不同温度下将具有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动态可视化入门—简单绘图、动图和交互式绘图

费米能量值为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—是否在结束时重复播放动画

Python动态可视化入门—简单绘图、动图和交互式绘图

费米能量为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更改滑块值时调用更新。

Python动态可视化入门—简单绘图、动图和交互式绘图

费米-狄拉克分布的温度依赖性交互式图

结论

希望本文能够展示如何利用动图和交互式滑块使数据可视化动态化。


分享到:


相關文章: