These days, your choice of database is about as controversial as discussing politics and religion; it's the third rail of...data? You can choose the best database, and table structure to suit your particular needs. MySQL serves just fine as an example. In any case, SQL syntax is mostly standard across databases. and the Python database interfaces are also quite standard; they mainly consist of instantiating a connection and cursor, and executing SQL commands.
Ok, so now to the code. In prior examples, we needed to know the number of frames to plot, when we initiate the matplotlib animation. In my case, I've structured the data so a given row in the table corresponds to one data frame, and to one plotted frame in the Matplotlib animation. So, I have a couple of utility functions, one to get the number of frames we'll plot, and the other to query a particular table entry, given the desired row [frame] and column.
import sys
import os
import time
import MySQLdb as mdb
from TableDef import columns #Custom definition of the table structure
def count(table, serverIP, dbUser, password, database):
try:
con = mdb.connect(serverIP, dbUser, password,database)
with con:
cur = con.cursor()
com="SET sql_mode=\'ANSI_QUOTES\'"
cur.execute(com)
row_exists=0
com="SELECT COUNT(*) FROM \"%s\""%table
cur.execute(com)
rows=cur.fetchall()
count=rows[0][0]
except mdb.Error, e:
print "Error %d: %s" % (e.args[0],e.args[1])
sys.exit(1)
finally:
if con:
con.close()
return count
def getdataframe(table,column,frame,serverIP, user, password, database):
if column not in columns:
return
if frame>count(table):
return
try:
con = mdb.connect(serverIP, dbUser, password,database);
with con:
cur = con.cursor()
com="SET sql_mode=\'ANSI_QUOTES\'"
cur.execute(com)
row_exists=0
com="SELECT %s "%column
com=com+"FROM \"%s\" "%table
com=com+"WHERE Frame=%s"%str(frame)
cur.execute(com)
rows=cur.fetchall()
data=rows[0][0]
except mdb.Error, e:
print "Error %d: %s" % (e.args[0],e.args[1])
sys.exit(1)
finally:
if con:
con.close()
return data
Note that I have a custom Python class called TableDef, where I've declared various parameters relevant to the table and database. This part depends on your particular application, how you structure your data, etc. I could have wrapped these table functions in a class if I wanted a more OO feel.
Now, when I go my Matplotlib animation, I can use these utility functions to get the total number of frames, and to query individual data frames.
import sys
import os
import json
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from file_utils import * #table from folder is defined here
import table_read as Table
from TableDef import columns #Custom definition of the table structure
from TableDef import DatabaseCredentials as DB #Custom definition of the table structure
def init():
plotline.set_data([], [])
dplotline.set_data([],[])
return plotline,dplotline,
def animate(i):
global buffersize
if (i%100==0):
print i
line= Table.getdataframe(table,columns[1],i,DB.serverIP, DB.user, DB.password, DB.name)
try:
jsondata=json.loads(line)
except ValueError:
print "Invalid JSON"
print line
f.close()
os.system("pause")
pos=jsondata["Position"]
vel=jsondata["Velocity"]
pos_buf.append(pos)
vel_buf.append(vel)
while len(pos_buf)>buffersize:
pos_buf.pop(0)
while len(vel_buf)>buffersize:
vel_buf.pop(0)
x=np.linspace(-len(pos_buf)/framerate,0,len(pos_buf))
dx=np.linspace(-len(vel_buf)/framerate,0,len(vel_buf))
plotline.set_data(x,headpose_buf)
dplotline.set_data(dx,headvel_buf)
return plotline, dplotline,
table=""
framerate=20.0
buffersize=5*20
vel_buf=[]
pos_buf=[]
fig, ax = plt.subplots()
ax.set_xlim(-5.0,2.0)
ax.set_ylim(-180,180)
my_dpi=96
fig.set_size_inches(640/my_dpi, 360/my_dpi)
plotline, =ax.plot([],[],lw=2,color='b')
dplotline, =ax.plot([],[], lw=2, color='r')
if __name__=='__main__':
if len(sys.argv)<2:
print "Usage:\nplot_pos_vel.py folder"
exit()
folder= sys.argv[1]
outfile=folder+"/pos_vel.avi"
if (os.path.isfile(outfile)):
exit()
table=table_from_folder(folder) #Custom function that maps folders to their table representation, user defined
line_count=Table.count(table)
Writer = animation.writers['ffmpeg']
writer = Writer(fps=25, metadata=dict(artist='Sayanan'), extra_args=['-vcodec','libx264'])
anim = animation.FuncAnimation(fig, animate,init_func=init, frames=line_count-1,blit=True, interval=1, repeat=False)
anim.save(outfile,writer=writer)
#plt.show()
Within the entries in the table, I've further structured my data as json for position and velocity. Again, this is really a design choice, and depending on your database, json encoding might be native.
Other than that, this is the same Matplotlib example as before, but using a database for the data.
No comments:
Post a Comment