Animate scatter plot - python

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()

Related

Adding name and history attribute to 2D particle simulation

I am attempting to create a program which simulates particles colliding in a 2D box, but each particle is labeled with a random 5 character string name and each collision is tracked in a list along each particle. So after the simulation, I would like a list from each particle listing which particles it has hit. I have forked this great simulation https://github.com/xnx/collision and added the name and history attributes to the particle class. However, whenever I attempt to access .name or .history, my kernel dies. The output says:
Kernel died, restarting
Restarting kernel...
The failure happens in the handle_collisions function (line 197), or whenever I try to access the history or the name, so there must be something wrong in my implementation of the name and history attributes. I have also tried to instantiate name and history in the init_particles function instead of place_particles but that had the same results. I'm not exactly sure how to correctly implement them. Thanks for your help.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import animation
from itertools import combinations
import random
import string
class Particle:
"""A class representing a two-dimensional particle."""
def __init__(self, x, y, vx, vy, name, history, radius=0.01, styles=None):
"""Initialize the particle's position, velocity, name, history and radius.
Any key-value pairs passed in the styles dictionary will be passed
as arguments to Matplotlib's Circle patch constructor.
"""
self.r = np.array((x, y))
self.v = np.array((vx, vy))
self.radius = radius
self.mass = self.radius**2
self.styles = styles
if not self.styles:
# Default circle styles
self.styles = {'edgecolor': 'b', 'fill': False}
# For convenience, map the components of the particle's position and
# velocity vector onto the attributes x, y, vx and vy.
#property
def x(self):
return self.r[0]
#x.setter
def x(self, value):
self.r[0] = value
#property
def y(self):
return self.r[1]
#y.setter
def y(self, value):
self.r[1] = value
#property
def vx(self):
return self.v[0]
#vx.setter
def vx(self, value):
self.v[0] = value
#property
def vy(self):
return self.v[1]
#vy.setter
def vy(self, value):
self.v[1] = value
#property
def history(self):
return self.history
#history.setter
def history(self,value):
self.history=value
#property
def name(self):
return self.name
#name.setter
def name(self,value):
self.name=value
def overlaps(self, other):
"""Does the circle of this Particle overlap that of other?"""
return np.hypot(*(self.r - other.r)) < self.radius + other.radius
def draw(self, ax):
"""Add this Particle's Circle patch to the Matplotlib Axes ax."""
circle = Circle(xy=self.r, radius=self.radius, **self.styles)
ax.add_patch(circle)
return circle
def advance(self, dt):
"""Advance the Particle's position forward in time by dt."""
self.r += self.v * dt
class Simulation:
"""A class for a simple hard-circle molecular dynamics simulation.
The simulation is carried out on a square domain: 0 <= x < 1, 0 <= y < 1.
"""
ParticleClass = Particle
def __init__(self, n, radius=0.01, styles=None):
"""Initialize the simulation with n Particles with radii radius.
Each particle is initialized with a 10 letter string name and an empty history.
radius can be a single value or a sequence with n values.
Any key-value pairs passed in the styles dictionary will be passed
as arguments to Matplotlib's Circle patch constructor when drawing
the Particles.
"""
self.init_particles(n, radius, styles)
self.dt = 0.01
def place_particle(self, rad, styles):
# Choose x, y so that the Particle is entirely inside the
# domain of the simulation.
x, y = rad + (1 - 2*rad) * np.random.random(2)
# Choose a random velocity (within some reasonable range of
# values) for the Particle.
vr = 0.1 * np.sqrt(np.random.random()) + 0.05
vphi = 2*np.pi * np.random.random()
vx, vy = vr * np.cos(vphi), vr * np.sin(vphi)
name = self.assignname
history = []
particle = self.ParticleClass(x, y, vx, vy, name, history, rad, styles)
# Check that the Particle doesn't overlap one that's already
# been placed.
for p2 in self.particles:
if p2.overlaps(particle):
break
else:
self.particles.append(particle)
return True
return False
def assignname(self):
letters = string.ascii_lowercase
name=''.join(random.choice(letters) for i in range(5))
return name
def init_particles(self, n, radius, styles=None):
"""Initialize the n Particles of the simulation.
Positions and velocities are chosen randomly; radius can be a single
value or a sequence with n values.
"""
try:
iterator = iter(radius)
assert n == len(radius)
except TypeError:
# r isn't iterable: turn it into a generator that returns the
# same value n times.
def r_gen(n, radius):
for i in range(n):
yield radius
radius = r_gen(n, radius)
self.n = n
self.particles = []
for i, rad in enumerate(radius):
# Try to find a random initial position for this particle.
while not self.place_particle(rad, styles):
pass
def change_velocities(self, p1, p2):
"""
Particles p1 and p2 have collided elastically: update their
velocities.
"""
m1, m2 = p1.mass, p2.mass
M = m1 + m2
r1, r2 = p1.r, p2.r
d = np.linalg.norm(r1 - r2)**2
v1, v2 = p1.v, p2.v
u1 = v1 - 2*m2 / M * np.dot(v1-v2, r1-r2) / d * (r1 - r2)
u2 = v2 - 2*m1 / M * np.dot(v2-v1, r2-r1) / d * (r2 - r1)
p1.v = u1
p2.v = u2
def handle_collisions(self):
"""Detect and handle any collisions between the Particles.
When two Particles collide, they do so elastically: their velocities
change such that both energy and momentum are conserved.
"""
# We're going to need a sequence of all of the pairs of particles when
# we are detecting collisions. combinations generates pairs of indexes
# into the self.particles list of Particles on the fly.
#particles share history when they collide
pairs = combinations(range(self.n), 2)
for i,j in pairs:
if self.particles[i].overlaps(self.particles[j]):
self.change_velocities(self.particles[i], self.particles[j])
#FAILS HERE
#self.particles[i].history.append(self.particles[j].name)
#self.particles[j].history.append(self.particles[i].name)
def handle_boundary_collisions(self, p):
"""Bounce the particles off the walls elastically."""
if p.x - p.radius < 0:
p.x = p.radius
p.vx = -p.vx
if p.x + p.radius > 1:
p.x = 1-p.radius
p.vx = -p.vx
if p.y - p.radius < 0:
p.y = p.radius
p.vy = -p.vy
if p.y + p.radius > 1:
p.y = 1-p.radius
p.vy = -p.vy
def apply_forces(self):
"""Override this method to accelerate the particles."""
pass
def advance_animation(self):
"""Advance the animation by dt, returning the updated Circles list."""
for i, p in enumerate(self.particles):
p.advance(self.dt)
self.handle_boundary_collisions(p)
self.circles[i].center = p.r
self.handle_collisions()
self.apply_forces()
return self.circles
def advance(self):
"""Advance the animation by dt."""
for i, p in enumerate(self.particles):
p.advance(self.dt)
self.handle_boundary_collisions(p)
self.handle_collisions()
self.apply_forces()
def init(self):
"""Initialize the Matplotlib animation."""
self.circles = []
for particle in self.particles:
self.circles.append(particle.draw(self.ax))
return self.circles
def animate(self, i):
"""The function passed to Matplotlib's FuncAnimation routine."""
self.advance_animation()
return self.circles
def setup_animation(self):
self.fig, self.ax = plt.subplots()
for s in ['top','bottom','left','right']:
self.ax.spines[s].set_linewidth(2)
self.ax.set_aspect('equal', 'box')
self.ax.set_xlim(0, 1)
self.ax.set_ylim(0, 1)
self.ax.xaxis.set_ticks([])
self.ax.yaxis.set_ticks([])
def save_or_show_animation(self, anim, save, filename='collision.mp4'):
if save:
Writer = animation.writers['ffmpeg']
writer = Writer(fps=10, bitrate=1800)
anim.save(filename, writer=writer)
else:
plt.show()
def do_animation(self, save=False, interval=1, filename='collision.mp4'):
"""Set up and carry out the animation of the molecular dynamics.
To save the animation as a MP4 movie, set save=True.
"""
self.setup_animation()
anim = animation.FuncAnimation(self.fig, self.animate,
init_func=self.init, frames=800, interval=interval, blit=True)
self.save_or_show_animation(anim, save, filename)
if __name__ == '__main__':
nparticles = 20
radii = .02
styles = {'edgecolor': 'C0', 'linewidth': 2, 'fill': None}
sim = Simulation(nparticles, radii, styles)
sim.do_animation(save=False)
I see two immediate problems in your code.
First: you did add history as a Particle.__init__ parameter but you never initialize the property itself.
Add something like this:
def __init__(self, x, y, vx, vy, name, history, radius=0.01, styles=None):
self._history = history
And bigger problem: you have an infinite recursion in your #property definition:
#property
def history(self):
return self.history
#history.setter
def history(self,value):
self.history=value
So in your getter you called history you call itself return self.history which will loop itself until program will crash.
Rename internal property to _history:
#property
def history(self):
return self._history
#history.setter
def history(self,value):
self._history=value

How to make real-time matplotlib faster?

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)

How to run a python program 10 times in a loop and plot the rsults in each time

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

Faster than if-elif statement

I am receiving Points in large number from a sensor in real-time. However, I just need 4 categories of points, i.e., top_left, top_right, bottom_left, and bottom_right. I have an if-elif statement in Python 2 as follows:
from random import random, randint
# points below are received from sensor. however,
# here in this post I am creating it randomly.
points = [Point(randint(0, i), random(), random(), random()) for i in range(100)]
# 4 categories
top_left, top_right, bottom_left, bottom_right = None, None, None, None
for p in points:
if p.id == 5:
top_left = p
elif p.id == 7:
top_right = p
elif p.id == 13:
bottom_left = p
elif p.id == 15:
bottom_right = p
print top_left.id, top_left.x, top_left.y, top_left.z # check variable
Each Point has an id and x, y, z parameters. This is an inbuilt class. I am just showing a sample class here.
class Point():
def __init__(self, id, x, y, z):
self.id = id
self.x = x
self.y = y
self.z = z
Is there any efficient way considering runtime of achieving the same.
Answer:
I am adding the results which I got from the answers. It seems that the answer by Elis Byberi is fastest among all. Below is my test code:
class Point():
def __init__(self, id, x, y, z):
self.id = id
self.x = x
self.y = y
self.z = z
from random import random, randint
n = 1000
points = [Point(randint(0, i), random(), random(), random()) for i in range(n)]
def method1():
top_left, top_right, bottom_left, bottom_right = None, None, None, None
for p in points:
if p.id == 5:
top_left = p
elif p.id == 7:
top_right = p
elif p.id == 13:
bottom_left = p
elif p.id == 15:
bottom_right = p
#print top_left.id, top_left.x, top_left.y, top_left.z
def method2():
categories = {
5: None, # top_left
7: None, # top_right
13: None, # bottom_left
15: None # bottom_right
}
for p in points:
categories[p.id] = p
top_left = categories[5]
#print top_left.id, top_left.x, top_left.y, top_left.z
def method3():
name_to_id = {'top_left': 5, 'top_right': 7, 'bottom_left': 13, 'bottom_right': 15}
ids = [value for value in name_to_id.values()]
bbox = {id: None for id in ids}
for point in points:
try:
bbox[point.id] = Point(point.id, point.x, point.y, point.z)
except KeyError: # Not an id of interest.
pass
top_left = bbox[name_to_id['top_left']]
#print top_left.id, top_left.x, top_left.y, top_left.z
from timeit import Timer
print 'method 1:', Timer(lambda: method1()).timeit(number=n)
print 'method 2:', Timer(lambda: method2()).timeit(number=n)
print 'method 3:', Timer(lambda: method3()).timeit(number=n)
See Below the returned output:
ravi#home:~/Desktop$ python test.py
method 1: 0.174991846085
method 2: 0.0743980407715
method 3: 0.582262039185
You can use a dict to save objects. Dict is very efficient in key lookup.
Using dict is twice as fast as using if else block.
This is the most efficient way in python:
from random import random, randint
class Point():
def __init__(self, id, x, y, z):
self.id = id
self.x = x
self.y = y
self.z = z
# points below are received from sensor. however,
# here in this post I am creating it randomly.
points = [Point(randint(0, i), random(), random(), random()) for i in
range(100)]
# 4 categories
categories = {
5: None, # top_left
7: None, # top_right
13: None, # bottom_left
15: None # bottom_right
}
for p in points:
categories[p.id] = p
>>> print categories[5].id, categories[5].x, categories[5].y, categories[5].z # check variable
5 0.516239541892 0.935096344266 0.0859987803457
Instead of using list-comprehension:
points = [Point(randint(0, i), random(), random(), random()) for i in range(100)]
use a loop and assign the points during creation:
points = []
for i in range(100):
p = Point(randint(0, i), random(), random(), random())
points.append(p)
if p.id == 5:
top_left = p
elif p.id == 7:
top_right = p
elif p.id == 13:
bottom_left = p
elif p.id == 15:
bottom_right = p
This way you get all done in one iteration instead of two.
Here's a way that should be faster because it uses a single if to determine whether a Point is one of the ones representing the extremes based on their id attribute, plus the if uses the very fast dictionary in operation for membership testing. Essentially what is done it the bbox dictionary is preloaded with keys that correspond to the four ids sought, which make checking for any of them a single relatively efficient operation.
Note that if there are points with duplicate ids in the Point list, the last one seen will be the one selected. Also note that if no point with a matching id is found, some of the final variables will have a value of None instead of a Point instance.
from random import randint, random
from pprint import pprint
from operator import attrgetter
class Point():
def __init__(self, id, x, y, z):
self.id = id
self.x = x
self.y = y
self.z = z
points = [Point(randint(0, 20), random(), random(), random()) for i in range(100)]
name_to_id = {'top_left': 5, 'top_right': 7, 'bottom_left': 13, 'bottom_right': 15}
bbox = {id: None for id in name_to_id.values()} # Preload with ids of interest.
for point in points:
if point.id in bbox: # id of interest?
bbox[point.id] = point
# Assign bbox's values to variables with meaningful names.
top_left = bbox[name_to_id['top_left']]
top_right = bbox[name_to_id['top_right']]
bottom_left = bbox[name_to_id['bottom_left']]
bottom_right = bbox[name_to_id['bottom_right']]
for point in [top_left, top_right, bottom_left, bottom_right]:
print('Point({}, {}, {}, {})'.format(point.id, point.x, point.y, point.z))

Matplotlib scatter plot animation keeps crashing Python

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()

Categories