常见问题

Q1. UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position xxx: illegal multibyte sequence

上面表示脚本中含有gbk编码的字符,这里需要注意,添加的脚本中不能有中文(这是一个bug,目前还没修复),中文不能存在于python程序的任何地方,包括注释中。所以需要检查代码,将相应的字符删除或者替换成英文。

Q2. 如何为脚本增加参数控制?

程序通过argparse来进行参数调节和解析,下方是一个示例:

// a_plus_b.py
import argparse
import sys

# parser and arguments
parser = argparse.ArgumentParser(description='a plus b problem')
parser.add_argument('--a', help='first num', type=int, default=0)
parser.add_argument('--b', help='second num', type=int, default=0)

def main():
    args = parser.parse_args()
    a = args.a
    b = args.b
    sum = a + b
    print('{0} + {1} = {2}'.format(a, b, sum))
    return 0


if __name__ == "__main__":
    sys.exit(main())
    #main()

你可以通过命令行来运行,

python a_plus_b.py
>>> 0

python a_plus_b.py --a 1 --b 2
>>> 3

项目中更改脚本参数的逻辑和上面一致。

关于argparse查看更多

Q3. 算法可视化中的动画是如何做到的?

动画使用的是Matplotlib中的animation

例如本项目中双因素旋升法,二维动画生成,核心代码如下,

def init2D():
    line.set_data([], [])
    point.set_data([], [])
    value_display.set_text('')
    return line, point, value_display


def animate2D(i):
    line.set_data(track_x[i], track_y[i])

    point.set_data(track_x[i], track_y[i])
    # point.set_data(track_x[i], track_y[i], track_z[i])

    value_display.set_text('Iteration: ' + str(i + 1) +
                           '\n'
                           r'$x(\alpha_1)= $' '{0:.4f}'.format(
                               track_x[i][1]) + r' $x(\alpha_2)= $' '{0:.4f}'.format(
        track_x[i][2]) +
                           '\n'
                           r'$y(\alpha_1)= $' '{0:.4f}'.format(
                               track_y[i][1]) + r' $y(\alpha_2)= $' '{0:.4f}'.format(
        track_y[i][2]) +
                           '\n'
                           r'$z(\alpha_1)= $' '{0:.4f}'.format(
                               track_z[i][1]) + r' $z(\alpha_2)= $' '{0:.4f}'.format(
        track_z[i][2])
                           )

    return line, point, value_display
# 2D
fig = plt.figure(figsize=(8, 8))
# ax1 = Axes3D(fig)
ax1 = plt.gca()
ax1 = snapshot2D(ax1)

line, = ax1.plot([], [], 'r', label='Domain', lw=1.5)
point, = ax1.plot([], [], 'bo', c='b', label='Experiments')
value_display = ax1.text(0.02, 0.08, '', transform=ax1.transAxes)

ax1.legend(loc=1)
ax1.grid(False)

anim2D = animation.FuncAnimation(fig, animate2D, init_func=init2D,
                                 frames=len(track_x),
                                 interval=500,
                                 repeat_delay=80, blit=True)

Writer2D = animation.writers['ffmpeg']
FPS_2D = 5
writer2D = Writer2D(fps=FPS_2D, metadata=dict(artist='L'), bitrate=1800)
# anim.save('Orthogonal-Rotation-004.gif',writer='imagemagick', fps=60)
print('Creating 2D Animation VIDEO ...')
anim2D.save('Orthogonal-Rotation-2D-fps{0}.mp4'.format(FPS_2D), writer=writer2D)
print('Done.\nCreating 2D Animation GIF ...')
anim2D.save('Orthogonal-Rotation-2D-fps{0}.gif'.format(FPS_2D), writer='imagemagick', fps=FPS_2D)
print('Done\n2D Animation Created.')
plt.savefig('Orthogonal-Rotation-2D.pdf')
plt.savefig('Orthogonal-Rotation-2D.png')

其中snapshot2D为生成函数的等高线图,将ax(matplotlib中的坐标轴类)的对象传回,再在该轴上绘制其他图像,也可以同时绘制。


def snapshot2D(ax):
    a = np.linspace(scope_x[0] - 0.2, scope_x[1] + 0.2, num_divs)
    b = np.linspace(scope_y[0] - 0.2, scope_y[1] + 0.2, num_divs)
    x, y = np.meshgrid(a, b)
    # z=func(x,y)
    # ax.contour(x, y,z, levels=np.logspace(-3,3,25), cmap='jet')
    ax.contourf(x, y, func(x, y), 50, cmap=cm.hot, alpha=0.7)
    CS = ax.contour(x, y, func(x, y), 15, colors='k')
    ax.clabel(CS, inline=True, fontsize=13)
    ax.set_xlabel(r'x', fontdict={'fontsize': 18, 'fontweight': 'medium'})
    ax.set_ylabel(r'y', fontdict={'fontsize': 18, 'fontweight': 'medium'})

    ax.set_title(r'Orthogonal Rotation Analysis $f(x,y)$', fontdict={'fontsize': 20, 'fontweight': 'medium'})
    ax.xaxis.set_tick_params(labelsize=18)
    ax.yaxis.set_tick_params(labelsize=18)

    # ax.plot(final_x, final_y, marker='*', c='r', markersize=15)
    ax.scatter(final_x, final_y, marker='*', c='r', s=50, label='Final Result')

    return ax

3D的动画生成逻辑相同。


def init3D():
    line.set_data([], [])
    line.set_3d_properties([])
    point.set_data([], [])
    point.set_3d_properties([])

    point1.set_data([], [])
    point1.set_3d_properties([])

    point2.set_data([], [])
    point2.set_3d_properties([])

    point3.set_data([], [])
    point3.set_3d_properties([])

    display_value.set_text('')

    # return line, point, display_value
    return line, point, point1, point2, point3, display_value


def animate3D(i):
    line.set_data(track_x[i], track_y[i])
    line.set_3d_properties(track_z[i])

    point.set_data(track_x[i], track_y[i])
    point.set_3d_properties(track_z[i])

    point1.set_data(track_x[i], track_y[i])
    point1.set_3d_properties([-0.5] * len(track_x[i]))

    # point2.set_data(scope_x[0]* len(track_x[i]), track_y[i]) # a bug here
    # point2.set_3d_properties(track_z[i])
    point2.set_data([scope_x[0] - 0.3] * len(track_x[i]), track_y[i])
    point2.set_3d_properties(track_z[i])

    point3.set_data(track_x[i], [scope_y[1] + 0.3] * len(track_x[i]))
    point3.set_3d_properties(track_z[i])

    display_value.set_text('Iteration: ' + str(i + 1) +
                           '\n'
                           r'$x(\alpha_1)= $' '{0:.4f}'.format(track_x[i][1]) + r' $x(\alpha_2)= $' '{0:.4f}'.format(
        track_x[i][2]) +
                           '\n'
                           r'$y(\alpha_1)= $' '{0:.4f}'.format(track_y[i][1]) + r' $y(\alpha_2)= $' '{0:.4f}'.format(
        track_y[i][2]) +
                           '\n'
                           r'$z(\alpha_1)= $' '{0:.4f}'.format(track_z[i][1]) + r' $z(\alpha_2)= $' '{0:.4f}'.format(
        track_z[i][2]))

    # return line, point, display_value
    return line, point, point1, point2, point3, display_value

# 3D
fig1 = plt.figure(figsize=(8, 8))
ax2 = Axes3D(fig1)
ax2 = snapshot3D(ax2)

line, = ax2.plot([], [], [], 'r-', label='Domain', lw=1.5)
point, = ax2.plot([], [], [], 'bo', markersize=10, label='Experiments')  # on the surface
point1, = ax2.plot([], [], [], 'ro', markersize=10, alpha=0.2)  # projection to z
point2, = ax2.plot([], [], [], 'ro', markersize=10, alpha=0.2)  # projection to x
point3, = ax2.plot([], [], [], 'ro', markersize=10, alpha=0.2)  # projection to y

display_value = ax2.text(scope_x[1], scope_y[1], final_z + 0.5, '', transform=ax2.transAxes)
ax2.grid(False)
ax2.legend(loc=1)

anim = animation.FuncAnimation(fig1, animate3D, init_func=init3D,
                               frames=len(track_x),
                               interval=1000,
                               repeat_delay=80, blit=True)

Writer3D = animation.writers['ffmpeg']
FPS_3D = 5
writer3D = Writer3D(fps=FPS_3D, metadata=dict(artist='L'), bitrate=1800)
# anim.save('Orthogonal-Rotation-004.gif',writer='imagemagick', fps=6)
print('Creating 3D Animation VIDEO ...')
anim.save('Orthogonal-Rotation-3D-fps{0}.mp4'.format(FPS_3D), writer=writer3D)
print('Done.\nCreating 3D Animation GIF ...')
anim.save('Orthogonal-Rotation-3D-fps{0}.gif'.format(FPS_3D), writer='imagemagick', fps=FPS_3D)
print('Done\n3D Animation Created.')
plt.savefig('Orthogonal-Rotation-3D.pdf')
plt.savefig('Orthogonal-Rotation-3D.png')
# plt.show()

其中`snapshot3D:


def snapshot3D(ax):
    a = np.linspace(scope_x[0] - 0.2, scope_x[1] + 0.2, num_divs)
    b = np.linspace(scope_y[0] - 0.2, scope_y[1] + 0.2, num_divs)
    X, Y = np.meshgrid(a, b)
    Z = func(X, Y)

    # ax.plot_surface(X, Y, Z, rstride=1, cstride=1, edgecolor='none', cmap='jet')
    ax.plot_wireframe(X, Y, Z, rstride=1, cstride=1, cmap='jet')
    cset = ax.contourf(X, Y, Z, zdir='z', offset=-0.5, cmap='jet', alpha=0.2)
    cset = ax.contourf(X, Y, Z, zdir='x', offset=scope_x[0] - 0.3, cmap='jet', alpha=0.2)
    cset = ax.contourf(X, Y, Z, zdir='y', offset=scope_y[1] + 0.3, cmap='jet', alpha=0.2)

    # CS = ax.contour(x, y, func(x, y), colors='k')
    # ax.clabel(CS, inline=True, fontsize=13)
    ax.set_xlabel(r'X', fontdict={'fontsize': 18, 'fontweight': 'medium'})
    ax.set_ylabel(r'Y', fontdict={'fontsize': 18, 'fontweight': 'medium'})
    ax.set_ylabel(r'Z', fontdict={'fontsize': 18, 'fontweight': 'medium'})

    ax.set_title(r'Orthogonal Rotation Analysis $f(x,y)$ 3D', fontdict={'fontsize': 20, 'fontweight': 'medium'})
    ax.xaxis.set_tick_params(labelsize=18)
    ax.yaxis.set_tick_params(labelsize=18)

    # ax.plot([final_x], [final_y], [final_z], marker='*', c='r', markersize=20, label='Final result')
    ax.scatter([final_x], [final_y], [final_z], marker='*', c='r', s=80, label='Final result')

    # ax.plot(track_x[0], track_y[0], c='b', marker='o')

    return ax

其余算法可视化的逻辑类似,总体为:

  1. 弄清要解决的问题
  2. 设计实现相应算法
  3. 将数据过程中的任意点进行可视化
  4. 将算法执行过程以动画形式表现出来

更多的关于代码,后续会以博客文章的形式进行讲解,不在本项目的讨论范围之中。

Q4. 如何生成的视频还有.gif图?

gifmp3分别用到了imagemagickffmpeg

关于imagemagick,点击这里获取更多帮助。

关于ffmepeg,点击这里获取更多信息。

Q5. 系统如何将python脚本执行的结果显示到前端的?如何修改?

前端采用HTML+JavaScript+CSS来控制,后端是SQLite数据库。将结果文件显示到前端利用的是Django框架的ORM(对象关系映射)机制和MVT(模型,视图,模板)设计模式(即经典的MVC设计模式),view层通过从Model中取相应的结果文件的地址,以字典的形式传入到Template层,经过前端的渲染,从而得到显示效果。故要单单修改前端风格,布局什么的,只需要修改template即可,修改其他则需要更具view的功能来进行相应的修改。