How to resize graph to fixed dimensions in matplotlib animation - python

I am wanting to plot two list in real time using matplotlib animation, with the help of the community I was able to plot my graph. I am now wanting to simplify my real time animation a bit as well as re-structure my graph.
Here are my objectives:
Plot x-axis: length of list "my_average"
Plot y-axis: elements in list "my_average"
y-axis limit -1 to 1 (all my elements in list "my_average" are between -1 and 1)
I do not know what I am doing wrong with my code thus far:
class StdOutListener(StreamListener):
def on_data(self, data):
json_load = json.loads(data)
texts = json_load['text'] # string
#print(texts)
#drop zero in list
if 0 in my_list: my_list.remove(0)
#print
#calculate average
average = numpy.mean(my_list)
b = my_average.append(average)
print "average =", my_average
def __init__(self):
self.start_time = time.time()
self.x = [len(my_average)]
self.y = [my_average]
self.my_average = []
self.line_actual, = plot(self.x, self.y) # line stores a Line2D we can update
self.line_average, = plot(self.x, self.my_average) # line stores a Line2D we can update
def on_data(self, new_value):
time_delta = time.time() - self.start_time # on our x axis we store time since start
self.x.append(time_delta)
self.y.append(new_value)
self.my_average.append(numpy.mean(self.y))
self.line_actual.set_data(self.x, self.y)
self.line_average.set_data(self.x, self.my_average)
ylim([min(self.y), max(self.y)]) # update axes to fit the data
xlim([0, max(self.x)])
draw() # redraw the plot
ion() # ion() allows matplotlib to update animations.
out_listener = StdOutListener()
for i in range(10000):
out_listener.on_data(i + numpy.random.randint(-5,5))
Thank you in Advance

So:
I'm not sure what you mean by plot length of the list. But I assume you want to create an index array with indices from 0 to len(my_average). That is what range is for:
self.x = range(len(my_average))
You already use the ylim function, which does exactly what you want. But instead of passing the min/max of your data you just have to pass your desired static values:
ylim(-1, 1)

Related

If I have a function that randomly creates shapes, how can I add those shapes to a group? (CMU CS Academy)

I'm working in CMU CS Academy's Sandbox and I currently have a function that will draw a rectangle of random size, color, and position:
# List of colors
app.colors = ['crimson', 'gold', 'dodgerBlue', 'mediumPurple']
# Creating random shapes
import random
# Draws a random rectangle with random points for size and center along with a random color
def drawRect(x, y):
color = random.choice(app.colors)
x = random.randint(5,390)
y = random.randint(15,300)
w = random.randint(10,40)
h = random.randint(10,40)
r = Rect(x,y,w,h,fill=color,border='dimGray')
x = r.centerX
y = r.centerY
# Draws multiple random rectangles
def drawRects():
for i in range(5):
x = 50 * i
y = 60 * i
drawRect(x,y)
drawRects()
However, I want to add all the random rectangles that the function draws to a group so that I'm able to use the .hitsShape() method.
I also thought about creating a list with the random x and y values, but I'm not sure how to create a list with coordinates in CS Academy. What should I do to my current code? What should I do next?
Firstly, you have forgotten to end your functions with return .... That way, you can keep working with this data further in the code block. It's one of "best practices" in Python.
Also, I'm assuming you mean a collection by "group"?
You could save them in a tuple in this manner:
def drawRect(x, y):
...
r = Rect(x,y,w,h,fill=color,border='dimGray')
...
return r
def drawRects():
my_shapes = []
for i in range(5):
x = 50 * i
y = 60 * i
my_shapes.append(drawRect(x,y))
return my_shapes

plotting issue with matplotlib

I have an issue with plotting a graph over some values in python and matplotlib. What i want is to plot the i's (iterations) on the x-axis and optimal q*'s on the y-axis in the code below.
#Question 1
#i)
#Defining the parameters using SimpleNameSpace. par = Parameters
par = SimpleNamespace()
par.y = 1 #assets
par.p = 0.2 #probability
par.theta = -2 #elasticity
#Defining utility function for agent
def utility(z,par):
return (z**(1+par.theta))/(1+par.theta)
#Defining premium
def premium(q,par):
return par.p*q
#Defining expected value
#Note that z_1, z_2 represents first and second part of the objective function - just in a compressed version
def exp_value (i,q,par):
z_1 = par.y-i+q-premium(q,par)
z_2 = par.y-premium(q,par)
return par.p*utility(z_1,par)+(1-par.p)*utility(z_2,par)
def opt_q(i,par):
obj = lambda q: -exp_value(i,q,par) #defining the objective function
solution = minimize_scalar(obj, bounds=(0,0.9), method='bounded') #bounded solution within [0, 0.9]
q = solution.x
return q
# ii), iii) Creating a for loop to make a grid of the optimum q
for i in np.linspace(0.01,0.9,num=100):
res = opt_q(i,par)
print(res)
#iv) plotting the is and q*s with matplotlib
plt.plot(res,i)
# function to show the plot
plt.show()
When i run the above code i get nothing out on the graph. Can somebody explain what is missing?

Is it possible to fill in a circular graph with a solid colour and save it as svg in matplotlib?

I wrote some code that creates randomised patches from graphs in matplotlib. Basically how it works is that you create a graph from nodes taken from a circle using the parametric equation for a circle and then you randomly displace the nodes along the vector of (0,0) to the node point on the circumference of the circle. That way you can be certain to avoid lines from crossing each other once the circle is drawn. In the end you just append the first (x,y) coordinate to the list of coordinates to close the circle.
What I want to do next is to find a way to fill that circular graph with a solid colour so that I can create a "stamp" that can be used to make randomised patches on a canvas that hopefully will not create crossing edges. I want to use this to make procedural risk maps in svg format, because a lot of those are uploaded with terrible edges using raster image formats using jpeg.
I am pretty sure that my information of the nodes should be sufficient to make that happen but I have no idea how to implement that. Can anyone help?
import numpy as np
import matplotlib.pyplot as plt
def node_circle(r=0.5,res=100):
# Create arrays (x and y coordinates) for the nodes on the circumference of a circle. Use parametric equation.
# x = r cos(t) y = r sin(t)
t = np.linspace(0,2*np.pi,res)
x = r*np.cos(t)
y = r*np.sin(t)
return t,x,y
def sgn(x,x_shift=-0.5,y_shift=1):
# A shifted sign function to use as a switching function
# in order to avoid shifts lower than -0.5 which is
# the radius of the circle.
return -0.5*(np.abs(x -x_shift)/(x -x_shift)) +y_shift
def displacer(x,y,low=-0.5,high=0.5,maxrad=0.5):
# Displaces the node points of the circle
shift = 0
shift_increment = 0
for i in range(len(x)):
shift_increment = np.random.uniform(low,high)
shift += shift_increment*sgn(maxrad)
x[i] += x[i]*shift
y[i] += y[i]*shift
x = np.append(x,x[0])
y = np.append(y,y[0])
return x,y
def plot():
# Actually visualises everything
fig, ax = plt.subplots(figsize=(4,4))
# np.random.seed(1)
ax.axis('off')
t,x,y = node_circle(res=100)
a = 0
x,y = displacer(x,y,low=-0.15,high=0.15)
ax.plot(x,y,'r-')
# ax.scatter(x,y,)
plt.show()
plot()
got it: the answer is to use matplotlib.Patches.Polygon
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
def node_circle(r=0.5,res=100):
# Create arrays (x and y coordinates) for the nodes on the circumference of a circle. Use parametric equation.
# x = r cos(t) y = r sin(t)
t = np.linspace(0,2*np.pi,res)
x = r*np.cos(t)
y = r*np.sin(t)
return x,y
def sgn(x,x_shift=-0.5,y_shift=1):
# A shifted sign function to use as a switching function
# in order to avoid shifts lower than -0.5 which is
# the radius of the circle.
return -0.5*(np.abs(x -x_shift)/(x -x_shift)) +y_shift
def displacer(x,y,low=-0.5,high=0.5,maxrad=0.5):
# Displaces the node points of the circle
shift = 0
shift_increment = 0
for i in range(len(x)):
shift_increment = np.random.uniform(low,high)
shift += shift_increment*sgn(maxrad)
x[i] += x[i]*shift
y[i] += y[i]*shift
x = np.append(x,x[0])
y = np.append(y,y[0])
return x,y
def patch_distributor(M,N,res,grid='square'):
# Distribute Patches based on a specified pattern/grid.
if grid == 'square':
data = np.zeros(shape=(M,N,2,res+1))
for i in range(M):
for j in range(N):
x,y = displacer(*node_circle(res=res),low=-0.2,high=0.2)
data[i,j,0,:] = x
data[i,j,1,:] = y
return data
def plot(res):
# Actually visualises everything
fig, ax = plt.subplots(figsize=(4,4))
# np.random.seed(1)
ax.axis('off')
# x,y = node_circle(res=res)
# x,y = displacer(x,y,low=-0.15,high=0.15)
# xy = np.zeros((len(x),2))
# xy[:,0] = x
# xy[:,1] = y
patch_data = patch_distributor(10,10,res)
for i in range(patch_data.shape[0]):
for j in range(patch_data.shape[1]):
x,y = patch_data[i,j]
x += i*0.5
y += j*0.5
xy = np.zeros((len(x),2))
xy[:,0] = x
xy[:,1] = y
patch = Polygon(xy,fc='w',ec='k',lw=2,zorder=np.random.randint(2),antialiased=False)
ax.add_patch(patch)
ax.autoscale_view()
# ax.plot(x,y,'r-')
# ax.scatter(x,y,)
plt.savefig('lol.png')
plot(res=40)
# Displace circle along the line of (0,0) -> (cos(t),sin(t))
# Make the previous step influence the next to avoid jaggedness
# limit displacement level to an acceptable amount
# Random displaced cubic grid as placing points for stamps.

Add features above X-axis or at the top of the graph

I have a graph that looks like:
Now I want to add some additional informations slightly above the X-axis based on two X-coordinates.
For example connect the values 1376 and 1837 and annotate them that it looks like (I know it looks crappy but just that you get an idea. And of course the place of the text is not ideal):
And there will be several regions which can be overlapping. I tried to do it with the plt.arrow(1376, 0, 1837, 0) but the arrow is not stopping at 1837. It is going on to the end of the X-axis. I also tried it with the basic text annotation tool but I never got what I want. The other solution would be to add the information on the top of the graph below the headline. So any of your Ideas regarding top or bottom can be helpful.
A possible solution, though this is a bit of a manual process and not ideal (may be a bit tedious if you have lots of these) is to simply plot an extra line on the figure. You can specify the x coordinates between which you want to plot the line, and the y coordinates will be the vertical position on the graph.
import matplotlib.pyplot as plt
import numpy as np
# create some data
x = np.arange(0,10,0.1)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x,y)
ax.plot([2,4],[-1,-1], color="red", lw=1) # add the line
ax.annotate('Test 1', xy=(2.5, -0.95)) # add text above the line
# increase the thickness of the line using lw =
ax.plot([6,8],[-1,-1], color="red", lw=3)
ax.annotate('Test 2', xy=(6.5, -0.95))
plt.show()
Which results in a figure like:
Depending how many of these plots you need to make, you might want to automate the process for a list of regions/intervals. The problem, then, of course, is how to deal with overlapping intervals. The code below is an attempt to automate the process while resolving interval overlaps.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from itertools import chain, combinations
def annotate_intervals(intervals, labels, y0=0, dy=-1, ax=None):
"""
Annotates an interval with a bar and a centred label below.
Arguments:
----------
intervals - (N, 2) array
list of intervals
labels - (N, ) iterable of strings
list of corresponding labels
y0 - int/float (default 0)
baseline y value of annotations
dy - int/float (default -1)
shift in y to avoid overlaps of annotations
ax - matplotlib axis object (default plt.gca())
axis to annotate
"""
if ax is None:
ax = plt.gca()
# assign y values to each interval; resolve overlaps
y = y0 + _get_levels(intervals) * dy
for (start, stop), yy, label in zip(intervals, y, labels):
ax.plot([start, stop], [yy, yy], lw=3)
ax.text(start + (stop-start)/2., yy, label,
horizontalalignment='center', verticalalignment='bottom')
def _get_levels(intervals):
"""
Assign a 'level' to each interval such that no two overlapping intervals are on the same level.
Fill lower levels as much as possible before creating a new level.
"""
# initialise output
n = len(intervals)
levels = np.zeros((n))
# resolve overlaps
overlaps = _get_overlaps(intervals)
if np.any(overlaps):
contains_overlaps, = np.where(np.any(overlaps, axis=0))
remaining = list(contains_overlaps)
ctr = 0
while len(remaining) > 0:
indices = _get_longest_non_overlapping_set(intervals[remaining])
longest = [remaining.pop(ii) for ii in indices[::-1]]
levels[longest] = ctr
ctr += 1
return levels
def _get_overlaps(intervals):
"""
Arguments:
----------
intervals - (N, 2) array
list of intervals
Returns:
--------
overlap - (N, N) array
type of overlap (if any)
overlap[ii,jj] = 0 - no overlap
overlap[ii,jj] = 1 - start of interval[jj] within interval[ii]
overlap[ii,jj] = 2 - stop of interval[jj] within interval[ii]
overlap[ii,jj] = 3 - interval[jj] encapsulated by interval[ii]
overlap[ii,jj] = 4 - interval[jj] encapsulates interval[ii]
"""
n = len(intervals)
overlap = np.zeros((n,n), dtype=np.int)
for ii, (start, stop) in enumerate(intervals):
for jj, (s, t) in enumerate(intervals):
if ii != jj:
overlap[ii,jj] += int((s >= start) and (s < stop))
overlap[ii,jj] += 2 * int((t >= start) and (t < stop))
# if interval[jj] encapsulates interval[ii], overlaps[ii,jj] is still 0
mask = overlap == 3
overlap[mask.T] += 4
return overlap
def _get_longest_non_overlapping_set(intervals):
"""
Brute-force approach:
1) Get all possible sets of intervals.
2) Filter for non-overlapping sets.
3) Determine total length of intervals for each.
4) Select set with highest total.
"""
indices = np.arange(len(intervals))
lengths = np.diff(intervals, axis=1)
powerset = list(_get_powerset(indices))
powerset = powerset[1:] # exclude empty set
total_lengths = np.zeros((len(powerset)))
for ii, selection in enumerate(powerset):
selection = np.array(selection)
if not np.any(_get_overlaps(intervals[selection])):
total_lengths[ii] = np.sum(lengths[selection])
return powerset[np.argmax(total_lengths)]
def _get_powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable) # allows duplicate elements
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
def test():
import string
n = 6
intervals = np.sort(np.random.rand(n, 2), axis=1)
labels = [letter for letter in string.ascii_lowercase[:n]]
annotate_intervals(intervals, labels)
plt.show()

Plotting graph in python

I'm new to Python am trying to plot a graph based on the pyODE tutorial found here. I'm using pylab for the plotting.
Below is the main part of the code and #added represents the code I've added in order to try and display the graph. When looking at the values themselves, y and v are the ones that change and x,z,u,w remain 0.000. When I run the program, the axis scale keeps scaling, implying that something is happening regarding the values, but no line is displayed. What am I doing wrong?
Thanks
yplot = 0 #added
#do the simulation
total_time = 0.0
dt = 0.04
while total_time<2.0:
x,y,z = body.getPosition()
u,v,w = body.getLinearVel()
print "%1.2fsec: pos=(%6.3f,%6.3f,%6.3f) vel=(%6.3f,%6.3f,%6.3f)" % \
(total_time, x,y,z,u,v,w)
world.step(dt)
total_time += dt
yplot += y #added
plot(total_time, yplot) #added
xlabel('Time') #added
ylabel('Height') #added
show() #added
The trick is to accumulate all the values you want to plot first, and then just call plot once.
yplot = 0 #added
#do the simulation
total_time = 0.0
dt = 0.04
times=[]
yvals=[]
while total_time<2.0:
x,y,z = body.getPosition()
u,v,w = body.getLinearVel()
print "%1.2fsec: pos=(%6.3f,%6.3f,%6.3f) vel=(%6.3f,%6.3f,%6.3f)" % \
(total_time, x,y,z,u,v,w)
world.step(dt)
total_time += dt
yplot += y
times.append(total_time)
yvals.append(yplot)
plot(times, yvals,'r-')
xlabel('Time') #added
ylabel('Height') #added
show() #added
The third argument to plot, 'r-', tells pylab to draw a red line connecting the points listed in times,yvals. When you plot points one-at-a-time, there is no way to tell pylab to connect the dots because each plot contains only a single point. Calling plot for each point is also highly inefficient.

Categories