I have an issue. I'm using matplotlib on a project to do some real-time programming, I used this library because it was suggested by my mentor although I think now it was not a good idea since I learned that matplotlib is not real-time oriented. As I was working at the lab with the lab computer, real-time made the computer slow, but not that slow as now that I'm remote working on my laptop that is 5 years old, but I don't think that's the problem. In the project, I use Python 2.7, postgresql, pandas, wxpython, etc, so you get the idea. I have a notebook with multiple tabs and in each tab I implemented one figure of mpl for each instrument I start adding. For performance things I make it stop animation and star as you go between the tabs and only if the have been started with the desirable variable.
The thing is that as I started working on my laptop, as soon as I start the real-time plotting, the applications becomes very laggy, it's mostly unusable because it jumps too much when I'm trying to interact with it, or when is running too long. Then I think it's something more of the code and how it's plot. I'm seeking a way of making it run faster. My code for plotting is the following.
class live_panel_2(wx.Panel): # Real Time plotter for 2 Variable
def __init__(self, parent):
super(live_panel_2, self).__init__(parent)
#sns.set() # Set Plot Style
self.parent = parent
self.x_axis = [] # First x-axis
self.x2_axis = [] # Second x-axis
self.y_axis = [] # First y-axis
self.y2_axis = [] # Seconf y-axis
self.line_width = 1 # Line width for plots
self.flag = False
self.switch = True # Switch Flag
self.animation = False # Animation Flag
self.figure = Figure(figsize=(10, 3)) # Figure Size
self.canvas = FigureCanvas(self, -1, self.figure) # Canvas of Figure
self.axis = self.figure.add_subplot(1, 1, 1)
self.axis2 = self.axis.twinx() # Adding Second Axis
self.toolbar = NavigationToolbar(self.canvas) # Adding Navigation Tool
self.toolbar.Realize()
self.ani = None
self.figure.subplots_adjust(left=0.09, right=0.92, top=0.92, bottom=0.2, wspace=1) # Figure Space Adjust
self.axis.format_xdata = mdates.DateFormatter('%Y-%m-%d') # Format Dates
self.axis2.format_xdata = mdates.DateFormatter('%Y-%m-%d') # Format Dates
# self.axis.tick_params(axis='x', direction='inout', length=5, labelrotation=0)
# Sizer Adds
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 0, wx.EXPAND, 5)
sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND, 5)
# sizer.Add(self.button, 0, wx.ALL, 5)
self.SetSizerAndFit(sizer)
self.Bind(wx.EVT_CLOSE, self.stop)
self.canvas.draw()
def build(self, model, model_2, y, y2, delta, localization): # Method that builds the plot based on info received:
""""
Model: Mapped Model to be Used.
Y: Mapped parameter to be plotted.
Delta: Plot Range
Localization: Legend localization
"""
self.axis.set_title(model.Datetime.name + ' vs ' + y.name + ' vs ' + y2.name)
self.axis.set_xlabel(model.Datetime.name)
self.axis.set_ylabel(y.name)
# self.axis.annotate(str(value), xy=(time, value), xytext=(10, -2), textcoords='offset pixels',
# bbox=dict(boxstyle=custom_box_style, alpha=0.2))
# Set Axis 2 Format
self.axis2.set_xlabel(model.Datetime.name)
self.axis2.set_ylabel(y2.name)
# self.axis2.annotate(str(value2), xy=(time, value2), xytext=(10, -2), textcoords='offset pixels',
# bbox=dict(boxstyle=custom_box_style, alpha=0.2))
# Turn of scientific notation
self.axis.yaxis.set_major_formatter(mticker.ScalarFormatter())
self.axis.yaxis.get_major_formatter().set_scientific(False)
self.axis.yaxis.get_major_formatter().set_useOffset(False)
self.axis2.yaxis.set_major_formatter(mticker.ScalarFormatter())
self.axis2.yaxis.get_major_formatter().set_scientific(False)
self.axis2.yaxis.get_major_formatter().set_useOffset(False)
# Set legend and turn axes 2 grid off
self.axis2.grid(False)
if self.animation: # If Animation is true
self.ani._stop() # Stop animation
self.x_axis = [] # Make Arrays empty
self.x2_axis = []
self.y_axis = []
self.y2_axis = []
self.delta = delta # Set Delta
self.ani = animation.FuncAnimation(self.figure, self.animate,
fargs=(
self.x_axis, self.x2_axis, self.y_axis, self.y2_axis, model, model_2, y,
y2,
localization), interval=500) # Call Animate Method.
self.animation = True # Set Animation to True
self.ani._start() # Start Animation
def close(self):
self.ani._stop()
def stop(self, event): # Stop method when changing between real-time notebook
if self.ani != None: # If animation have been created.
# If animation is running and is not current page.
if self.flag == False and self.animation == True and self.parent != self.parent.parent.GetCurrentPage():
self.ani.event_source.stop() # Stop animation
self.flag = True
self.animation = False # Set Animation flag to False
# If animation is not running and is current page.
elif self.flag == True and self.animation == False and self.parent == self.parent.parent.GetCurrentPage():
self.ani.event_source.start()
self.flag = False
self.animation = True
event.Skip()
def mode_stop(self, event): # Stop method when changing between views.
if self.ani != None:
if self.switch != True and self.parent == self.parent.parent.GetCurrentPage():
self.ani.event_source.start()
self.switch = True
else:
self.ani.event_source.stop()
self.switch = False
event.Skip()
# Animation
def animate(self, i, x_axis, x2_axis, y_axis, y2_axis, model, model2, y, y2, localization):
# Data query
self.now = datetime.now() # Get Present Time
self.now_delta = self.now - timedelta(minutes=self.delta) # Get past time with delta.
if not x_axis: # If x_axis array have not been created.
with session_scope() as s: # Open SQL Session
value = s.query(y).order_by(model.Datetime.desc()).filter(model.Datetime > self.now_delta).filter(
model.Datetime < self.now).all() # Query Values for y array in axis 1.
value2 = s.query(y2).order_by(model2.Datetime.desc()).filter(model2.Datetime > self.now_delta).filter(
model2.Datetime < self.now).all() # Query Values for y array in axis 2.
time = s.query(model.Datetime).order_by(model.Datetime.desc()).filter(
model.Datetime > self.now_delta).filter(
model.Datetime < self.now).all() # Query dates for x array in axis 1.
time2 = s.query(model2.Datetime).order_by(model2.Datetime.desc()).filter(
model2.Datetime > self.now_delta).filter(
model2.Datetime < self.now).all() # Query date for x array in axis 2.
# Reverse List.
for i in reversed(value):
y_axis.append(i[0])
for i in reversed(value2):
y2_axis.append(i[0])
for i in reversed(time):
x_axis.append(i[0])
for i in reversed(time2):
x2_axis.append(i[0])
if x_axis: # If X Axis Array Exist
with session_scope() as s: # Open SQL Session
value = s.query(y).filter(model.Datetime > x_axis[
-1]).all() # Query Valyes for Y array in axis 1, after the date of the last point in the array.
value2 = s.query(y2).filter(model2.Datetime > x2_axis[
-1]).all() # Query Valyes for Y array in axis 2, after the date of the last point in the array.
time = s.query(model.Datetime).filter(model.Datetime > x_axis[
-1]).all() # Query Valyes for X array in axis 1, after the date of the last point in the array.
time2 = s.query(model2.Datetime).filter(model2.Datetime > x2_axis[
-1]).all() # Query Valyes for X array in axis 2, after the date of the last point in the array.
# Appent in Array
for i in value:
y_axis.append(i[0])
for i in value2:
y2_axis.append(i[0])
for i in time:
x_axis.append(i[0])
for i in time2:
x2_axis.append(i[0])
# Alinate Array
x_axis, y_axis = array_alinate(x_axis, y_axis)
x2_axis, y2_axis = array_alinate(x2_axis, y2_axis)
# Create Dataframe from the arrays
data_1 = pd.DataFrame(y_axis, x_axis, [y.name], dtype=float)
data_2 = pd.DataFrame(y2_axis, x2_axis, [y2.name], dtype=float)
# Set Index Name to Models Datetime Name
data_1.index.name = model.Datetime.name
data_2.index.name = model2.Datetime.name
# Resapmle to second if the models are from wibs-neo
if model == (wibs_neo_monitoring or wibs_neo_particle):
data_1 = data_1.resample('S').mean()
data_2 = data_2.resample('S').mean()
# Secure data points dont past delta, asuming each datapoint lapse one second.
data_1 = data_1.iloc[-self.delta * 60:]
data_2 = data_2.iloc[-self.delta * 60:]
# Clear Axis
#self.axis.clear()
#self.axis2.clear()
self.axis.set_xlim(min(data_1.index), max(data_1.index))
self.axis2.set_xlim(min(data_2.index), max(data_2.index))
line_2 = self.axis2.plot(data_2, linewidth=self.line_width, color='G', label=y2.name)
line_1 = self.axis.plot(data_1, linewidth=self.line_width, color='B', label=y.name)
lines = line_1 + line_2
labels = [l.get_label() for l in lines]
self.axis2.legend(lines, labels, loc=localization)
# Format plot
self.figure.canvas.mpl_connect('close_event', self.close)
# tm.sleep(1)
Related
I've been working on a text based adventure game, in the process of doing the skeleton for the game building system. I got the movement working by typing NESW originally but after removing the debugging code, its been broken for over a day. Thought I would reach out for help...
Here's the main issue:
A grid is automatically generated with coordinate objects containing a dictionary value of places it "canGo" corresponding for a single letter key and a bool, defaulted to False. After initializing the grid, I pass a list of coordinates I want to activate to the grid that executes the following function: (1)it activates all the grids in the list passed; (2)it runs a function that generates a list of active squares; (3)it passes that list to a function that checks one square away from each of the active coords and sees if it is active, if so then it marks that direction bool as True (example canGo["N"] = True.
Issue: (Debug below) the correctly sets the attributes when initializing, after completion the keys come back WRONG but were seemingly not set again to something else in my code. Further, it sets all of keys to the same thing (in this case all of the keys in all coords are ["S"] = True)
-SD--------
SETTING DIRECTIONG debug
(1, 1) >> N
-SD--------
(1, 1) directions = N
(1, 1){'N': True, 'S': False, 'E': False, 'W': False}
-------------------
-SD--------
SETTING DIRECTIONG debug
(1, 2) >> S
-SD--------
(1, 2) directions = S
(1, 2){'N': False, 'S': True, 'E': False, 'W': False}
-------------------
(1,1): {'N': False, 'S': True, 'E': False, 'W': False}
Which direction would you like to move? <NESW>:
Debug so far: I have debug code inside that shows me it correctly recognizes the activated grids, correctly passes them to the list, and that the list correctly sets the ["canGo"] coordinate attribute. No other code is executed after initializing and processing the activated coords and before it asks you what direction you would like to go.
Code below...
Main
from coordinates import Coordinates as Coords
from grid import Grid
from player import Player
def main():
gridSizeX = 5
gridSizeY = 5
game = Grid(gridSizeX,gridSizeY, Player("John Doe", 1, 1))
#game.setActiveGridSquares([(1,1),(1,2),(1,3)])
game.grid[1][1].active = True
game.grid[1][2].active = True
game.grid[1][1].setDirections("N")
game.grid[1][2].setDirections("S")
while(True):
x,y = game.player.getLocation()
print("({x},{y}): {canGo}".format(x=x,y=y,canGo=game.grid[x][y].canGo))
move = input("Which direction would you like to move? <NESW>: ")
if(move.upper() == "EXIT"):
break
game.movePlayer(move.upper())
if __name__ == "__main__":
main()
Grid
In here, I am passing an immutable list of tuples (x,y) that then iterates through, and breaks apart. Main processing of the directions in the error occurs under Grid class in directionsProcessing()
from coordinates import Coordinates as Coords
from player import Player
class Grid:
"""Builds Grid and takes Player Object to move along grid"""
playerLocation = None
def __init__(self, gridSizeX, gridSizeY, player):
self.grid = self.buildGrid(gridSizeX,gridSizeY)
self.player = player
def buildGrid(self, gridSizeX, gridSizeY):
"""Builds and returns a grid object as a dictionary of [x][y]
Starts at 1 and ends at gridSize(X/Y)
gridSize(X/Y) will be the (x/y) max. -> 5 would be 1 to 5
"""
Grid = {}
for x in range(1, gridSizeX+1):
Grid[x] = {}
for y in range(1, gridSizeY+1):
Grid[x][y] = Coords(x, y)
return Grid
def copyGrid(self):
"""Returns a copy of grid dictionary"""
return self.grid
def setPlayer(self, playerToSet):
"""Sets player object into grid class to allow for player tracking"""
self.player = playerToSet
def setActiveGridSquares(self, squares):
"""Sets a list of grid squares to active"""
for t in squares:
x,y = t
self.grid[x][y].isActive = True
self.solveGridDirections(self.getAllActiveSquares())
def getAllActiveSquares(self):
"""Returns list of all active grid squares"""
activeGridSquares = []
for x in self.grid:
for y in self.grid[x]:
if(self.grid[x][y].isActive):
activeGridSquares.append((self.grid[x][y].x,self.grid[x][y].y))
return activeGridSquares
def solveGridDirections(self, activeSquares):
"""Resolves all active grid squares direction components to
allow movement into nearby active squares"""
for t in activeSquares:
adjoiningDirections = []
x,y = t
#print("Evaluating ActiveSquare: ("+str(x)+","+str(y)+")")
if((x,y+1) in activeSquares):
adjoiningDirections.append("N")
if((x,y-1) in activeSquares):
adjoiningDirections.append("S")
if((x+1,y) in activeSquares):
adjoiningDirections.append("E")
if((x-1,y) in activeSquares):
adjoiningDirections.append("W")
self.grid[x][y].setDirections("".join(adjoiningDirections)) #Sets allowed move directions inside grid
def movePlayer(self, direction):
"""Moves player in direction, preforms coordinate check if player can move"""
if(len(direction) > 1):
print("Lenght must be 1 character ONLY <NESW>")
return
x,y = self.player.getLocation()
print("-MP-------------") #####
print("({x},{y})Can I move in {direction} direction? {tf}".format(x=str(x),y=str(y),direction=direction,tf=str(self.grid[x][y].canGo[direction])))
print("-MP-------------") #####
if(self.grid[x][y].canGo[direction]):
self.player.movePlayer(direction)
else:
print("Player cannot move in that direciton on this grid square.")
Coordinates
class Coordinates:
"""Set coordinates of path squards in the world"""
actionOnEnter = None
choices = {"a":None,"b":None,"c":None}
canGo = {"N": False,"S": False,"E": False,"W": False}
isActive = False
def __init__(self, x, y):
self.x = x #set x coords
self.y = y #set y coords
def directionsProcessing(self, directions):
"""Directions are processed into a specific order and canGo is amended"""
listOfDirections = ["N", "S", "E", "W"]
verifiedDirections = []
coordsDir = str(self.getCoords())
print(coordsDir+" directions = "+directions) #####
for m in listOfDirections:
if(directions.find(m) != -1):
self.canGo[m] = True
verifiedDirections.append(m)
else:
self.canGo[m] = False
print(coordsDir+str(self.canGo)) #####
print("-------------------") #####
def setDirections(self, direcitons):
"""Sets directions a player can move, updates from initialization"""
print("-SD--------")#####
coordsDir = str(self.getCoords())#####
print("SETTING DIRECTIONG debug")#####
print(coordsDir+" >> "+direcitons)#####
print("-SD--------")#####
self.directionsProcessing(direcitons)
def getCoords(self):
"""Return x,y coordinate duple"""
return self.x,self.y
Player (less important but still plays a roll in motion)
class Player:
"""Player class used to hold player data.
X and Y coords are player's starting coordinates
Control inventory, keys, etc.
"""
inventory = {"sword": False}
keys = {"rectangle": False, "half moon": False}
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
def movePlayer(self, direction):
"""Moves player toards certain coordinates, <NESW>"""
if (len(direction) != 1):
raise Exception("Only 1 letter may be used.")
updown = {"N": 1, "S": -1, "W": -1, "E": 1}
validDirections = ["N","S","E","W"]
if(direction.upper() in validDirections):
if (direction in ["N","S"]):
self.y += updown[direction]
if (direction in ["W","E"]):
self.x += updown[direction]
else:
Exception("Direction is invalid")
def getLocation(self):
"""Returns tuple of player location x,y"""
return self.x,self.y
def setLocation(self, locationTuple):
"""Sets location based on input tuple
Syntax -> Player.setLocation((x,y))
"""
x,y = locationTuple
self.x = x
self.y = y
def toString(self):
"""Converts player data into string"""
return "Player: {name} Location: {x},{y}".format(name=self.name, x=self.x, y=self.y)
Thanks to #barny for your help.
This can be solved in two ways after reading your comments
(1) [better] move canGo to the init() method
(2) create an empty instance of "canGoTemp" inside the directionsProcession() method and pass the values to that, then pass the dict to the canGo attribute (this is without moving canGo to init())
both of these worked but I landed on (1)...
Thanks again man, I know it was a mess
I am trying to run this code. We run the EM for 10 loops and plot the result in each loop. You want to observe the progress for each EM loop below.
X = np.linspace(-5,5,num=20)
X0 = X*np.random.rand(len(X))+10 # Create data cluster 1
X1 = X*np.random.rand(len(X))-10 # Create data cluster 2
X2 = X*np.random.rand(len(X)) # Create data cluster 3
X_tot = np.stack((X0,X1,X2)).flatten() # Combine the clusters to get the random datapoints from above
class GMD:
def __init__(self,X,iterations):
self.iterations = iterations
self.X = X
self.mu = None
self.pi = None
self.var = None
def run(self):
self.mu = [-8,8,5]
self.pi = [1/3,1/3,1/3]
self.var = [5,3,1]
for iter in range(self.iterations):
r = np.zeros((len(X_tot),3))
for c,g,p in zip(range(3),[norm(loc=self.mu[0],scale=self.var[0]),
norm(loc=self.mu[1],scale=self.var[1]),
norm(loc=self.mu[2],scale=self.var[2])],self.pi):
r[:,c] = p*g.pdf(X_tot)
for i in range(len(r)):
r[i] = r[i]/(np.sum(pi)*np.sum(r,axis=1)[i])
"""Plot the data"""
fig = plt.figure(figsize=(10,10))
ax0 = fig.add_subplot(111)
for i in range(len(r)):
ax0.scatter(self.X[i],0,c=np.array([r[i][0],r[i][1],r[i][2]]),s=100)
"""Plot the gaussians"""
for g,c in zip([norm(loc=self.mu[0],scale=self.var[0]).pdf(np.linspace(-20,20,num=60)),
norm(loc=self.mu[1],scale=self.var[1]).pdf(np.linspace(-20,20,num=60)),
norm(loc=self.mu[2],scale=self.var[2]).pdf(np.linspace(-20,20,num=60))],['r','g','b']):
ax0.plot(np.linspace(-20,20,num=60),g,c=c)
var_c.append((1/m_c[c])*np.dot(((np.array(r[:,c]).reshape(60,1))*(self.X.reshape(len(self.X),1)-self.mu[c])).T,(self.X.reshape(len(self.X),1)-self.mu[c])))
plt.show()
GMD = GMD(X_tot,10)
GMD.run()
But after running this code it's showing this below error as a output
this message showing when I am going to run this code
You could make another file and import your class and do:
for x in range(0,9):
class.function()
I'm unable to reply to Klaus' answer but as he pointed out, you might have missed self while calling the pi value on one occasion:
for i in range(len(r)):
r[i] = r[i]/(np.sum(pi)*np.sum(r,axis=1)[i]) # Here the pi should be self.pi
UPDATED:
I would like to plot real time y values generated randomly from Random_Generation_List.py. I have imported the python file and have them in the same folder. The points are only being printed and show only a vertical line on the graph. How do I fix this to make the points plot onto the graph in real time? Like a point every 0.001?
Random_Generation_List:
import random
import threading
def main():
for count in range(12000):
y = random.randint(0,1000)
print(y)
def getvalues():
return [random.randint(0,1000) for count in range(12000)]
def coordinate():
threading.Timer(0.0001, coordinate).start ()
coordinate()
main()
Real_Time_Graph
import time
from collections import deque
from matplotlib import pyplot as plt
from matplotlib import style
import Random_Generation_List
start = time.time()
class RealtimePlot:
def __init__(self, axes, max_entries = 100):
self.axis_x = deque(maxlen=max_entries)
self.axis_y = deque(maxlen=max_entries)
self.axes = axes
self.max_entries = max_entries
self.lineplot, = axes.plot([], [], "g-")
self.axes.set_autoscaley_on(True)
def add(self, x, y):
self.axis_x.append(x)
self.axis_y.append(y)
self.lineplot.set_data(self.axis_x, self.axis_y)
self.axes.set_xlim(self.axis_x[0], self.axis_x[-1] + 1e-15)
self.axes.relim(); self.axes.autoscale_view() # rescale the y-axis
def animate(self, figure, callback, interval = 50):
def wrapper(frame_index):
self.add(*callback(frame_index))
self.axes.relim(); self.axes.autoscale_view() # rescale the y-axis
return self.lineplot
def main():
style.use('dark_background')
fig, axes = plt.subplots()
display = RealtimePlot(axes)
axes.set_xlabel("Seconds")
axes.set_ylabel("Amplitude")
values = Random_Generation_List.getvalues()
print(values)
while True:
display.add(time.time() - start, values)
plt.pause(0.001)
display.animate(fig, lambda frame_index: (time.time() - start, values))
plt.show()
if __name__ == "__main__": main()
Error Message:
raise RuntimeError('xdata and ydata must be the same length')
RuntimeError: xdata and ydata must be the same length
I'm building a Python tool for visualizing data structures in 3D. The code below is the full program, it's even set up to run a default test model with some random data; you just need numpy and matplotlib. Basically, you declare a Node, connect it to other Nodes, and it makes pretty 3D networks. I'd like to be able to call switchNode() and have it flip the color of a node between black and white. With the way it works right now, every time a Node is instantiated, the plot is added to with another data point. I'm not familiar enough with matplotlib's animation tools to know the best way of doing this (my attempt at following the example from another post is commented out on line 83, and hoped someone could offer me some tips. Thanks!!
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.animation as anim
# user-defined variables
debug = False
axisOn = False
labelsOn = True
maxNodes = 20
edgeColor = 'b'
dottedLine = ':'
darkGrey = '#191919'
lightGrey = '#a3a3a3'
plotBackgroundColor = lightGrey
fontColor = darkGrey
gridColor = 'k'
numFrames = 200
# global variables
fig = None
hTable = []
ax = None
numNodes = 0
# initialize plot
def initPlot():
global ax, fontColor, fig
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d', axisbg=plotBackgroundColor)
if axisOn == False:
ax.set_axis_off()
else:
ax.set_axis_on()
fontColor = darkGrey
# gives n random float values between vmin and vmax
def randrange(n, vmin, vmax):
return (vmax - vmin) * np.random.rand(n) + vmin
# builds an empty node with a given value, helper method for makeNode
def makeNodeS(value):
global hTable, numNodes
n = Node(value)
hTable.append(n)
numNodes = len(hTable)
return n
# builds a node with given parameters
def makeNode(value, location, color, marker):
n = makeNodeS(value)
n.setLoc(location)
n.setStyle(color, marker)
if debug:
print("Building node {} at {} with color = {}, marker = {}, and associations = {}.".format(value, location, color, marker, n.assocs))
return n
# aggregate nodes in hTable and plot them in 3D
def plotNodes():
global hTable
if debug:
print("Plotting Graph...")
for elem in hTable:
if debug:
print(" Plotting node {}...".format(elem.value))
global fig, numFrames
scat = ax.scatter(elem.location[0], elem.location[1], elem.location[2], c=elem.color, marker=elem.marker)
for c in elem.assocs:
if (getNode(c).value != elem.value):
if elem.count in getNode(c).assocs: # if the two nodes are associated to each other, draw solid line
ax.plot([elem.location[0], getNode(c).location[0]], [elem.location[1], getNode(c).location[1]], [elem.location[2], getNode(c).location[2]], edgeColor)
if debug:
print(" Plotting double edge between {} and {}...".format(elem.value, getNode(c).value))
else:
ax.plot([elem.location[0], getNode(c).location[0]], [elem.location[1], getNode(c).location[1]], [elem.location[2], getNode(c).location[2]], edgeColor + dottedLine)
if debug:
print(" Plotting single edge from {} to {}...".format(elem.value, getNode(c).value))
#ani = anim.FuncAnimation(fig, update_plot, frames=xrange(numFrames), fargs=(['b', 'w'], scat))
# build single connection from node A to node B
def sConnect(nodeA, nodeB):
nodeA.addAssoc(nodeB)
if debug:
print(" Drawing single connection from node {} to node {}...".format(nodeA.value, nodeB.value))
# build double connection from node A to node B, and from node B to node A
def dConnect(nodeA, nodeB):
if debug:
print("\nDouble node connection steps:")
sConnect(nodeA, nodeB)
sConnect(nodeB, nodeA)
# update scatter with new color data
def update_plot(i, data, scat):
scat.set_array(data[i])
return scat
# returns the node with given count
def getNode(count):
global hTable
n = hTable[count-1]
return n
# set up axis info
def defineAxis():
ax.set_xlabel('X Label')
ax.xaxis.label.set_color(lightGrey)
ax.tick_params(axis='x', colors=lightGrey)
ax.set_ylabel('Y Label')
ax.yaxis.label.set_color(lightGrey)
ax.tick_params(axis='y', colors=lightGrey)
ax.set_zlabel('Z Label')
ax.zaxis.label.set_color(lightGrey)
ax.tick_params(axis='z', colors=lightGrey)
# randomly populate nodes and connect them
def test():
for i in range (0, maxNodes):
rand = np.random.rand(2)
if (0 <= rand[0] <= 0.25):
q = makeNode(i, np.random.rand(3), 'r', '^')
elif (0.25 < rand[0] <= 0.5):
q = makeNode(i, np.random.rand(3), 'b', 'o')
elif (0.5 < rand[0] <= 0.75):
q = makeNode(i, np.random.rand(3), 'g', 'v')
elif (0.75 < rand[0]):
q = makeNode(i, np.random.rand(3), 'w', 'o')
if (0 < i < maxNodes-1):
if (rand[1] <= 0.2):
dConnect(q, getNode(q.count-1))
elif (rand[1] < 0.5):
sConnect(q, getNode(q.count-1))
# randomly populate binary nodes and connect them
def test2():
for i in range (0, maxNodes):
rand = np.random.rand(2)
if (0 <= rand[0] <= 0.80):
q = makeNode(i, np.random.rand(3), 'k', 'o')
else:
q = makeNode(i, np.random.rand(3), 'w', 'o')
if (i > 0):
if (rand[1] <= 0.2):
dConnect(q, getNode(q.count-1))
elif (rand[1] > 0.2):
sConnect(q, getNode(q.count-1))
# switches a binary node between black and white
def switchNode(count):
q = getNode(count)
if (q.color == 'b'):
q.color = 'w'
else:
q.color = 'b'
# main program
def main():
## MAIN PROGRAM
initPlot()
test2()
plotNodes()
defineAxis()
plt.show()
# class structure for Node class
class Node(str):
value = None
location = None
assocs = None
count = 0
color = None
marker = None
# initiate node
def __init__(self, val):
self.value = val
global numNodes
numNodes += 1
self.count = numNodes
self.assocs = []
self.color = 'b'
self.marker = '^'
# set node location and setup 3D text label
def setLoc(self, coords):
self.location = coords
global labelsOn
if labelsOn:
ax.text(self.location[0], self.location[1], self.location[2], self.value, color=fontColor)
# define node style
def setStyle(self, color, marker):
self.color = color
self.marker = marker
# define new association
def addAssoc(self, newAssociation):
self.assocs.append(newAssociation.count)
if debug:
print(" Informing node association: Node {} -> Node {}".format(self.value, newAssociation.value))
main()
Scatter returns a collection and you can change the colors of the points in a collection with set_facecolor. Here's an example you can adapt for your code:
plt.figure()
n = 3
# Plot 3 white points.
c = [(1,1,1), (1,1,1), (1,1,1)]
p = plt.scatter(np.random.rand(n), np.random.rand(n), c = c, s = 100)
# Change the color of the second point to black.
c[1] = (0,0,0)
p.set_facecolor(c)
plt.show()
I've found some flaky resources online about what to do when a matplotlib animation crashes Python.
Here is the code. I know that certain changes to the number of points or smallness of the dt variable might cause some overwhelming computation, but I could really use some help. Overall it behaves extremely slowly and I need to have this running smoothly. The code is for a physics class and it is not an optimization problem - the goal of the project is not to optimize matplotlib's animation facilities but to model a system in phase space. I could use any help I can get.
import matplotlib
#matplotlib.use("TkAgg")
from matplotlib import animation
import matplotlib.pyplot as plt
import matplotlib.pylab as pl
import numpy as np
import math
# Create the window and the plot.
fig = plt.figure()
fig.canvas.set_window_title("Modelling Non-Linear Chaotic Systems in Phase ")
ax = fig.add_subplot(111)
##### CONSTANTS #####
NUM_POINTS = 40 # The number of points to discretize.
AUTOSCALE = False # Sets autoscale to true or false. If true, there will be odd (expected to a degree) behavior as time goes on.
NO_TRAIL = False # Adds a trail.
##### CONSTANTS #####
##### Cool Settings #####
# Double tail swirl.
# k = .2
# alpha = .4
# dt = .1
##### #####
##### VARIABLES #####
m = 1 # mass constant
k = 5 # spring constant
alpha = .3 # stretch/deformation constant
dt = .01 # change in time
interval = 100 # interval in milliseconds between each animation
##### VARIABLES #####
class Point():
def x():
return self.x
def y():
return self.y
def pos(xy):
self.x=xy[0]
self.y=xy[1]
# initial_pos is a tuple
def __init__(self, initial_pos):
self.initial_position = initial_pos
self._x = initial_pos[0]
self._y = initial_pos[1]
self.x = self._x
self.y = self._y
def evolve(x_map, y_map, l):
return [Point([x_map(el), y_map(el)]) for el in l]
"""
Lagrangian
L = T - V = .5dq^2 - .5kq^2 - .333alpha*q^3
p = mdq; dx = p/m
Hamiltonian
H = p^2/m - L
H = p^2/m - p^2/2m + .5kq^2 + .333alpha*q^3
dp = -partial(H)/partial(q) = -kq - alpha*q^2
dx = partial(H)/partial(p) = p/m
"""
def time_dependent_funcs(name, dt):
### This is the adjustment of p over time. ###
def p_map(point):
p = point.y
q = point.x
try:
dp = -1*k*q-alpha*q**2
ddp = -k*p/m-alpha*q*p/m
return p+dp*dt+ddp*dt**2
except:
return p
### End adjustment of p. ###
### This is the adjustment of q over time. ###
def q_map(point):
q = point.x
p = point.y
try:
dq = p/m
dp = -1*k*q-alpha*q**2
ddp = -k*p/m-alpha*q*p/m
return q+dq*dt+ddp*dt**2/m
except:
return q
### End adjustment of q. ###
if name == "p_map":
return p_map
elif name == "q_map":
return q_map
# Used this post to help create this function: http://stackoverflow.com/questions/14619876/how-do-i-generate-pair-of-random-points-in-a-circle-using-matlab
def circle(r, n):
l = []
for i in range(n):
x=r+1 # necessary to enter loop
y=r+1 # necessary to enter loop
while (x**2 + y**2) > r**2:
x = 2*r*np.random.randn()
y = 2*r*np.random.randn()
l.append(Point([x,y]))
return l
### DOESN'T CURRENTLY WORK
def square(h, n):
l = []
for i in range(n):
x=h+1 # necessary to enter loop
y=h+1 # necessary to enter loop
while x < h and y < h:
x = np.random.randn()
y = np.random.randn()
l.append(Point([x,y]))
return l
def rand(n):
return [Point([np.random.randn(),np.random.randn()]) for i in range(n)]
def x(l):
return [el.x for el in l]
def y(l):
return [el.y for el in l]
#list_of_points = rand(NUM_POINTS) # works
#list_of_points = square(.2, 400) # doesn't currently work
list_of_points = circle(20,NUM_POINTS) # need this to be a global
def init():
global list_of_points, dt
scat = ax.scatter(x(list_of_points), y(list_of_points),color="blue", animated=NO_TRAIL)
if NO_TRAIL:
return scat,
def animate(i):
global list_of_points, dt
map_p = time_dependent_funcs("p_map", dt)
map_q = time_dependent_funcs("q_map", dt)
### evolve the points ###
list_of_points = evolve(map_q, map_p, list_of_points)
scat = ax.scatter(x(list_of_points), y(list_of_points), color="blue",lw=2, animated=NO_TRAIL)
if NO_TRAIL:
return scat,
### Animates the figure fig by executing init_func init (initially) followed by animation function
### animate for 10 frames at an interval of 100ms all the while erasing the last animation (blit=True).
ani = animation.FuncAnimation(fig, animate, init_func=init, frames = 100, interval=interval,blit=NO_TRAIL,save_count=0)
if not AUTOSCALE:
plt.autoscale(enable=False)
plt.show()