首先,我要感谢任何可以提前通过我的人。我是Matplotlib的新手,通常也不在python中编码。
我所拥有的是大量数据文件(100's-10,000)。这些文件中的每一个都有20个我想变成动画的图,其中每个文件代表不同的帧。在尝试让事情发挥作用时,代码变得令人难以置信。有6个子图(3,2,1-6)。它们都共享相同的x轴。在任何给定的子图上,我都有1到6 y的值。他们还需要适当的标签,有些是'symlog'图,因为我想查看正面和负面的对数数据。我希望在不依赖于ffmpeg或mencoder的情况下形成动画,因为在运行代码的计算机上可能无法使用这些动画。看起来这个明显的解决方案在于FuncAnimation,但这真的让我太困惑了。我见过的大多数例子都只是为一个图添加了另一个点。我想在基本上20个图中替换数据。我将仅包括两个子图,并假设我足够聪明,将其推断为6 ...
我已经成功地将图表形成为单独的“安全”文件中的.png文件。
所以这里有一些非常非常丑陋的代码:
#!/usr/bin/python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import matplotlib.animation as animation
import glob
import os
import re
from StringIO import StringIO
from matplotlib.ticker import MaxNLocator
from matplotlib.font_manager import FontProperties
def GetFiles(dir_name, extension):
fileList = []
for file in os.listdir(dir_name):
index = file.find(extension)
if index != -1:
dirfile = os.path.join(dir_name, file)
fileList.append(dirfile)
return fileList
dirString = raw_input('Please enter a directory name: ')
extension = raw_input('Please enter the extension: ')
ClearFiles = raw_input('Remove temporary .png files (Y/N): ')
dataName = GetFiles(dirString, extension)
print dataName #just make sure right files are being loaded
pngfilelist = []
figure = plt.figure()
fontP = FontProperties()
fontP.set_size('small')
ax1 = figure.add_subplot(1,2,1)
ax1.grid(True) # Enable Grid Lines
ax1.legend(loc=9, ncol=6, borderaxespad=0., prop=fontP)
ax1.set_title('Band Diagram')
PsiPlot, = ax1.plot([], [], label='Psi')
EcPlot, = ax1.plot([], [], label='Ec')
EvPlot, = ax1.plot([], [], label='Ev')
ax1.legend(loc=9,ncol=6, borderaxespad=0., prop=fontP)
ax2 = figure.add_subplot(1,2,2, sharex=ax1)
NPlot, = ax2.plot([], [], label='n')
PPLot, = ax2.plot([], [], label='p')
ax2.grid(True) # Enable Grid Lines
ax2.set_title('Electron/Hole Concentrations')
ax2.legend(loc=9,ncol=2, borderaxespad=0., prop=fontP)
X = []
Psi = []
Ec = []
Ev = []
n = []
p = []
def UpdatePlot(dataFile):
data = np.genfromtxt(dataFile, autostrip=True, skip_header=4, names=True, usecols=("X", "Psi", "Ec", "Ev", "n", "p")) #Load the specified data into giant list
entries = len(data)
for ctr in range(0,entries):
X.append(data[ctr][0]) # X-value for all plots
Psi.append(data[ctr][1]) #plot 1,1
Ec.append(data[ctr][2])
Ev.append(data[ctr][3])
n.append(data[ctr][4]) # plot 1,2
p.append(data[ctr][5])
figure.suptitle(dataFile, y=0.99)
PsiPlot.set_data(X, Psi)
EcPlot.set_data(X, Ec)
EvPlot.set_data(X, Ev)
NPlot.set_data(X, n)
PPlot.set_data(X, p)
plt.subplot(1,2,1)
plt.xlabel('Position (cm)')
plt.ylabel('Energy (eV)')
plt.subplot(1,2,2)
plt.xlabel('Position (cm)')
plt.ylabel('cm^-3')
plt.yscale('symlog', linthreshy=1e10)
figure.set_size_inches(16,10)
figure.set_dpi(200)
plt.tight_layout(pad=0.2, w_pad=0.2, h_pad=0.2)
filename = dataFile.replace(extension, '.png')
plt.savefig(filename)
pngfilelist.append(filename)
return PsiPlot, EcPlot, EvPlot, NPlot, PPlot,
ani = animation.FuncAnimation(figure, UpdatePlot, dataName, interval=500, blit=True)
ani.save('test.mp4', fps=15)
# remove the temporary png files if wanted.
if ClearFiles == 'y' or ClearFiles == 'Y':
for fname in pngfilelist:
os.remove(fname)
我意识到的事情: 附加数据不是获取列表中所有X和Y数据的最佳方法。第二组数据还将包括在此片段中编写的第一组数据(寻找删除它的好方法,以后不会引起问题)。当我尝试各种各样的东西时,可能会有比我目前需要的更多的进口,然后将它们切掉。使用这种方法,X / Y比例不会自动设置,因为我尝试做的只是保存到.png文件(我通过plt.plot做所有事情并保存后清除而不是所有这些关于设置数据的东西轴)。例如,我想将最低的Y值设置为最低的Ev,将最高的Y值设置为最高的Psi。此外,在第二个情节似乎没有任何工作。当然,价值非常大。
使用此代码,我收到“未找到标记对象”的警告。还有一个运行时错误,说已经删除了底层C / C ++对象 - 我在正好的情节中没有收到两个错误并保存到.png文件代码。
我真的不知道如何将所有这些图表都放到FuncAnimation中。
有什么想法?我厌倦了对Python的抨击 - 我真的需要对我的模拟代码感到头疼。
最后,这是旧(坏)数据文件的示例部分:
Data from: TestLoad.dev
Simulation time: 4.08738e-013
Iteration : 665
Data binning: 5
Point X Psi Ec Ev Fermi Fermi_n Efp n p n*p Rho Ec_Carriers Ev_Carriers Light_Gen Generation_Th Recomb_Thermal SRH_Rate1 SRH_Rate2 SRH_Rate3 SRH_Rate4 Jn_x Jp_x
0 4.9e-006 3.58 -0.500001 -0.500001 -0.5 -0.5 -0.500001 2.72507e+021 2.72507e+021 7.42603e+042 0 2.67057e+008 2.67057e+008 0 0 0 0 0 0 0 4577.65 0
1 9.95e-006 3.58 -0.5 -0.5 -0.5 -0.499999 -0.5 2.72508e+021 2.72508e+021 7.42603e+042 0 8.17523e+006 8.17523e+006 0 0 0 0 0 0 0 0 -114441
2 1.015e-005 3.61356 0.0255559 -1.09444 -0.95916 -0.95916 -0.830208 0 1.08799e+015 0 -0.132665 0 0.971429 0 0 0 0 0 0 0 0 -114441
3 1.025e-005 3.62841 0.0404132 -1.07959 -0.944094 -0.944094 -0.844848 0 2.89096e+015 0 -0.132656 0 3.02857 0 0 0 0 0 0 0 0 -119019
4 1.035e-005 3.64199 0.0539899 -1.06601 -0.930857 -0.930857 -0.854293 0 9.46844e+015 0 -0.131488 0 10.3143 0 0 0 0 0 0 0 0 -114441
5 1.045e-005 3.6543 0.0662974 -1.0537 -0.919519 -0.919519 -0.867723 0 2.36441e+016 0 -0.129511 0 21.6571 0 0 0 0 0 0 0 0 -123596
6 1.055e-005 3.66535 0.0773546 -1.04265 -0.909748 -0.909748 -0.873209 0 4.47623e+016 0 -0.125061 0 48.4286 0 0 0 0 0 0 0 0 -96130.6
7 1.065e-005 3.6752 0.0872047 -1.0328 -0.901449 -0.901449 -0.876584 0 6.9861e+016 0 -0.1222 0 66.2857 0 0 0 0 0 0 0 0 -146485
8 1.075e-005 3.68388 0.0958752 -1.02412 -0.895041 -0.895041 -0.880708 0 1.18029e+017 0 -0.113068 0 124.286 0 0 0 0 0 0 0 0 -86975.3
9 1.085e-005 3.69145 0.103454 -1.01655 -0.889233 -0.889233 -0.879943 0 1.57625e+017 0 -0.111058 0 136.829 0 0 0 0 0 0 0 0 -137329
10 1.095e-005 3.69796 0.109961 -1.01004 -0.885743 -0.885743 -0.881837 0 2.16895e+017 0 -0.0941347 0 240.457 0 0 0 0 0 0 0 0 -22888.2
788 0.00998975 4.19373 0.605734 -0.514266 -0.3792 -0.3792 -0.287991 0 5.78298e+015 0 -0.131942 0 5.48571 0 0 0 0 0 0 0 0 77820
789 0.00998985 4.17975 0.591751 -0.528249 -0.393181 -0.393181 -0.292558 0 2.27746e+015 0 -0.132404 0 1.6 0 0 0 0 0 0 0 0 68664.7
790 0.0099904 4.08 -1.45745e-006 -1.45745e-006 3.16863e-016 4.06816e-008 -7.67735e-007 2.72507e+021 2.72507e+021 7.42603e+042 0 2.72507e+007 2.72507e+007 0 0 0 0 0 0 0 0 0
791 0.00999545 4.08 -1.45745e-006 -1.45745e-006 3.16863e-016 4.06816e-008 -7.67735e-007 2.72507e+021 2.72507e+021 7.42603e+042 0 2.47982e+008 2.47982e+008 0 6.27943e+027 0 0 0 0 0 0 0
为了防止未来的人们看到这一点,在我开始投入所有其他情节之前,这就是最终的简化答案(概念证明)。
#!/usr/bin/python
import numpy as np
import matplotlib as mpl
mpl.use( "agg" )
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import os
from StringIO import StringIO
from matplotlib.font_manager import FontProperties
def GetFiles(dir_name, extension):
fileList = []
for file in os.listdir(dir_name):
index = file.find(extension)
if index != -1:
dirfile = os.path.join(dir_name, file)
fileList.append(dirfile)
return fileList
def UpdatePlot(dataFile):
global MinX, MaxX, ax1MinY, ax1MaxY, ax2MinY, ax2MaxY, first
print 'loading data for ', dataFile
data = np.genfromtxt(dataFile, autostrip=True, skip_header=4, names=True, usecols=("X", "Psi", "Ec", "Ev", "n", "p")) #Load the specified data into giant list
# get new bounds on limits for graphs
tempMin = data['X'].min()
tempMax = data['X'].max()
if tempMax > MaxX or first == True:
MaxX = tempMax + (tempMax - tempMin) * 0.01
ax1.set_xlim(MinX, MaxX)
ax2.set_xlim(MinX, MaxX)
if tempMin < MinX or first == True:
MinX = tempMin - (tempMax - tempMin) * 0.01
ax1.set_xlim(MinX, MaxX)
ax2.set_xlim(MinX, MaxX)
tempMin = data['Psi'].min()
tempMax = data['Psi'].max()
if tempMax > ax1MaxY or first == True:
ax1MaxY = tempMax + (tempMax - tempMin) * 0.5
ax1.set_ylim(ax1MinY, ax1MaxY)
tempMin = data['Ev'].min()
tempMax = data['Ev'].max()
if tempMin < ax1MinY or first == True:
ax1MinY = tempMin - (tempMax - tempMin) * 0.2
ax1.set_ylim(ax1MinY, ax1MaxY)
tempMax = data['n'].max()
if tempMax > ax1MaxY or first == True:
ax2MaxY = tempMax * 2 # This is basically a log plot...
ax2.set_ylim(ax2MinY, ax2MaxY)
tempMax = data['p'].max()
if tempMax > ax1MaxY or first == True:
ax2MaxY = tempMax * 2 # This is basically a log plot...
ax2.set_ylim(ax2MinY, ax2MaxY)
# Now update all the data for the plots
titleText.set_text(dataFile)
PsiPlot.set_data(data['X'], data['Psi'])
EcPlot.set_data(data['X'], data['Ec'])
EvPlot.set_data(data['X'], data['Ev'])
NPlot.set_data(data['X'], data['n'])
PPlot.set_data(data['X'], data['p'])
plt.draw() # need to update the figure regardless because the title changes
if GeneratePNG == 'Y' or GeneratePNG == 'y':
filename = dataFile.replace(extension, '.png')
plt.savefig(filename)
first = False
return
dirString = raw_input('Please enter a directory name: ')
extension = raw_input('Please enter the extension: ')
GeneratePNG = raw_input('Generate .png files of each file (Y/N): ')
framesps = raw_input('Frames per second: ')
outname = raw_input('Output file name (no extension): ')
outname = outname + '.mp4'
dataName = GetFiles(dirString, extension)
# print dataName
MinX = 0
MaxX = 0
ax1MinY = 0
ax1MaxY = 0
ax2MinY = 0
ax2MaxY = 0
first = True
figure = plt.figure()
fontP = FontProperties()
fontP.set_size('small')
figure.set_size_inches(16,10)
figure.set_dpi(200)
titleText = figure.suptitle('Title', y=0.99) # must do this way to allow title to be changed later
ax1 = figure.add_subplot(1,2,1)
ax1.grid(True) # Enable Grid Lines
ax1.set_title('Band Diagram')
plt.xlabel('Position (cm)')
plt.ylabel('Energy (eV)')
PsiPlot, = ax1.plot([], [], label='Psi')
EcPlot, = ax1.plot([], [], label='Ec')
EvPlot, = ax1.plot([], [], label='Ev')
ax1.legend(loc=9,ncol=6, borderaxespad=0., prop=fontP)
ax2 = figure.add_subplot(1,2,2, sharex=ax1)
plt.xlabel('Position (cm)')
plt.ylabel('cm^-3')
plt.yscale('symlog', linthreshy=1e10)
ax2.grid(True) # Enable Grid Lines
ax2.set_title('Electron/Hole Concentrations')
NPlot, = ax2.plot([], [], label='n')
PPlot, = ax2.plot([], [], label='p')
ax2.legend(loc=9,ncol=2, borderaxespad=0., prop=fontP)
plt.tight_layout(pad=0.5, w_pad=0.5, h_pad=0.5)
ani = mpl.animation.FuncAnimation(figure, UpdatePlot, dataName, init_func=None, interval=500, blit=True)
ani.save(outname, fps=framesps, codec='mpeg4')
再次感谢您指出我正确的方向!
答案 0 :(得分:5)
好的,我已经深入研究了你的代码。我可以看到一些事情。
首先,如果发生错误,请始终尝试提供完整的回溯。在这种情况下,错误相当好的可谷歌(*),并引导我到例如RuntimeError: underlying C/C++ object has been deleted when saving and afterwards closing a pyplot figure。这表明pyqt存在问题;显然,这是你的默认后端。
因此,正如该页面上的答案所示,请尝试更改后端:
import matplotlib as mpl
mpl.use( "agg" )
由于您只是保存文件,因此无论如何都不需要交互式后端。 “agg”是一个非交互式的,所以没有带有情节的弹出窗口,你只能保存到磁盘。 这可以解决RuntimeError。我并没有真正困扰标签错误,虽然我猜你正试图在实际数据不存在的地块上放置标签。也许这是未使用的第6个情节(因为我没有看到任何地方的第6个情节)?
在阅读数据时:您现在似乎将下一组数据附加到当前数据。那是正确的?或者你想替换它? 如果更换,只需执行以下操作:
Psi = data['Psi']
等。当然没有for循环。甚至更简单,只有:
PsiPlot.set_data(data['X'], data['Psi'])
如果你要追加,它有点棘手,但你可以做以下我猜。首先,初始分配需要是numpy数组(仅显示Psi):
Psi = nparray([])
然后,追加成为:
Psi = np.concatenate((Psi, data['Psi']))
(注意括号的两组。再次,没有for循环)。
我注意到你以这种方式获得了UnboundLocalError。看起来默认的Python对象(像[]这样的列表)会自动在函数中找到,但是像numpy.ndarray(上面的Psi那样)的命名空间对象却不是。因此,在UpdatePlot开始时,您需要:
global X, Psi, Ec, Ev, n, p
让Python找到要追加的正确对象。
最后,您提到您不希望mencoder
或ffmpeg
依赖关系,但在引擎盖下,matplotlib.animation.save()
会调用这些程序。例如,我试图运行你的脚本并得到一个RuntimeError,因为我的系统上没有安装ffmpeg
。因此,尝试在其他地方运行此代码时请注意这一点。我不知道只有Python的mpeg编码器。
(*)事实上,你可能也是如此。我猜你对整个过程和Python脚本太沮丧的事实意味着你没有在错误和其他细节中分解问题,因为你提到了几个问题。它可能没问题,但像往常一样,它会分散实际问题。