I have a problem in a function to be used in matplotlib FuncAnimation.
I'm aware this is not 100% related to the PlotAnimation code. It is more related to how return works. Anyway, here it go:
I need to plot several lines in the same graph - but I don't know in advance how many lines. So, I created a list of plots (called lines) like that:
fig = plt.figure()
ax = plt.axes(xlim=(0, 1), ylim=(0, 1), aspect='equal')
lines=[]
for i in range(len(disc)):
line, = ax.plot([], [], '-', lw=1.5)
lines.append(line)
Let's say len(disc) is equal to or greater than 3. If I do this:
def animate(i):
global disc
for i in range(len(disc)):
lines[i].set_data(disc[i].x, disc[i].y)
return line[0], line[1], line[2]
anim = animation.FuncAnimation(fig, animate, blit=True)
plt.show()
All works perfecly (except for the fact that, if len(disc) is greated than 3, I miss some of the lines in the plot).
However, I don't know len(disc) beforehand. So, my question is: Is there a way to use something like that:
return lines
(or something like that; I know the above doesn't work), replacing the return statement in the animate function above?
The first thing I can think of is using a for loop
return lines[0:len(lines)]
I don't know, if I got this right, as I don't use matplotlib myself, but from the documentation I got an idea.
Why does return lines not work? It should be equal to
return lines[0], lines[1], ..., lines[len(lines)-1]
Maybe an iterator helps here. The keyword is yield:
def def animate(i):
global disc
for i in range(len(disc)):
lines[i].set_data(disc[i].x, disc[i].y)
yield from lines # in Python 2.x use: yield iter(lines)
Whereas I don't think, this is the correct/desired way to got, I cannot think of another solution for now.
Do you really need to return something? The documentation doesn't mention a required return-value.
Related
Currently trying to save figures each with a name coming from a list in a for loop.
Input:
plot_l=['test1','test2',.........]
for i in range(ydata.shape[1]):
plt.figure()
fig, ax = plt.subplots(constrained_layout=True)
ax.plot(dsfg,ydata[i])
ax.set_xlabel('dsfg')
ax.set_ylabel('tzui')
ax.set_title('misc ')
secax.set_xlabel('len')
plt.savefig('plot_l{0}.jpg'.format(i))
plt.close()
Output: The figures are generated but with incorrect figure name, ie,
plot_l1.png
plot_l2.png
plot_l3.png
Desired Output:
test1.png
test2.png
test3.png
I have also tried plt.savefig(plot_l[i]+'.png') in place of plt.savefig('plot_l{0}.jpg'.format(i)) Suggestions are welcome....thanks
You are iterating on integers generated by range:
for i in range(ydata.shape[1]):
And you are naming the files with this parameter i.
plt.savefig('plot_l{0}.jpg'.format(i))
Assuming there is no error and the list of names contains as many names as there are iteration on i (i.e. ydata.shape[1] == len(plot_l)), then you can replace the savefig with:
plt.savefig(f'{plot_l[i]}.jpg')
The notation f followed by a string is equivalent to str.format(), but is more explicit as you can place the variable you are formatting inside the {}.
I'm producing some figures with the following code:
def boxplot_data(self,parameters_file,figure_title):
data = pandas.read_csv(parameters_file)
header = data.keys()
number_of_full_subplots = len(header)/16
remainder = len(header)-(16*number_of_full_subplots)
try:
for i in range(number_of_full_subplots+1):
fig =plt.figure(i)
txt = fig.suptitle(figure_title+' (n='+str(len(data[header[0]]))+') '+'Page '+str(i)+' of '+str(number_of_full_subplots),fontsize='20')
txt.set_text(figure_title+' (n='+str(len(data[header[0]]))+') '+'Page '+str(i)+' of '+str(number_of_full_subplots))
for j in range(16):
plt.ioff()
plt.subplot(4,4,j)
plt.boxplot(data[header[16*i+j]])
plt.xlabel('')
mng=plt.get_current_fig_manager()
mng.window.showMaximized()
plt.savefig(str(i)+'.png',bbox_inches='tight',orientation='landscape')
plt.close(fig)
plt.ion()
except IndexError:
txt = fig.suptitle(figure_title+' (n='+str(len(data[header[0]]))+') '+'Page '+str(i)+' of '+str(number_of_full_subplots),fontsize='20')
txt.set_text(figure_title+' (n='+str(len(data[header[0]]))+') '+'Page '+str(i)+' of '+str(number_of_full_subplots))
print '{} full figures were created and 1 partially filled \
figure containing {} subplots'.format(number_of_full_subplots,remainder)
This produces and saves the figures to file in the properly formatted manner however, no matter what I do the code seems to bypass the fig.suptitle line(s) and consequently I can't give my figure a title. Apologies if it seems there is a lot going on in this function that I haven't explained but does anybody have an explanation as to why this code refuses to give my figures titles?
Your problem is not that suptitle is bypassed, but that you are never saving the figure that you call suptitle on. All your calls to savefig are within the inner loop and as such are saving only the subplots. You can actually watch this happening if you open the png file while your code is running - you see each of the 16 sub axes being added one by one.
Your code looks unnecessarily complicated. For instance, I don't think you need to use ion and ioff. Here is a simple example of how to do what I think you want, followed by a translation of your code to fit that (Obviously i can't test, because I don't have your data)
import matplotlib.pyplot as plt
test_y=range(10)
test_x=[8,13,59,8,81,2,5,6,2,3]
def subplotsave_test():
for i in range(5):
fig = plt.figure(i)
txt = fig.suptitle('Page '+str(i)+' of '+str(5),fontsize='20')
for j in range(16):
plt.subplot(4,4,j+1)
plt.plot(test_y,test_x)
plt.savefig(str(i)+'.png',bbox_inches='tight',orientation='landscape')
if __name__ == '__main__':
subplotsave_test()
One tip I have found works for me - do a plt.show() wherever you intend to save the figure and ensure it looks like you want beforehanad and then replace that call with plt.savefig()
Possible translation of your function
def boxplot_data(self,parameters_file,figure_title):
data = pandas.read_csv(parameters_file)
header = data.keys()
number_of_full_subplots = len(header)/16
remainder = len(header)-(16*number_of_full_subplots)
for i in range(number_of_full_subplots+1)
fig =plt.figure(i)
fig.suptitle(figure_title+' (n='+str(len(data[header[0]]))+') '+'Page '+str(i)+' of '+str(number_of_full_subplots),fontsize='20')
for j in range(16):
plt.subplot(4,4,j+1)
if 16*i + j < len(header):
plt.boxplot(data[header[16*i+j]])
plt.xlabel('')
#You might want the showMaximized() call here - does nothing
#on my machine but YMMV
else:
print '{} full figures were created and 1 partially filled \
figure containing {} subplots'.format(number_of_full_subplots,remainder)
break
plt.savefig(str(i)+'.png',bbox_inches='tight',orientation='landscape')
plt.close(fig)
This is my first time using python (and stackoverflow) , and it keeps showing "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
Can anyone please tell me what I am doing wrong ?
It is supposed to show three graphs.
K,r,a,h,e,m=9,0.5,0.3,0.05,0.1,0.1
def dz(N,P):
return (r*N*(1-N/K)-a*N*P/(1+a*h*N),e*a*N*P/(1+a*h*N)-m*P)
N0,P0=6,4
pas=10**(-3)
def z(t):
u,x,y=0,N0,P0
if t>0 :
while t-u>pas:
x,y,u=x+pas*dz(x,y)[0],y+pas*dz(x,y)[1],u+pas
return x+(t-u)*dz(x,y)[0],y+(t-u)*dz(x,y)[1]
else :
while u-t>pas:
x,y,u=x-pas*dz(x,y)[0],y-pas*dz(x,y)[1],u-pas
return x+(t-u)*dz(x,y)[0],y+(t-u)*dz(x,y)[1]
def N(t):
return z(t)[0]
def P(t):
return z(t)[1]
close()
figure('N(t), P(t), P(N)')
t=linspace(0,15,100)
autoscale(enable=True, axis=u'both', tight=None)
plot(t,N(t),'b--',t,P(t),'r--',N(t),P(t),'k')
axis('scaled')
grid(True)
problem solved, please see answers.
You need to write some code for exercise first. For example understand how to write and use the functions and what is/are passed to the functions before you begin to write some awesome code. It is what people would say Rome wasn't built in a day.
Back to your code: The way you want the function Z(t) treat t is as a single variable. However, when you use N(t) and call Z(t), t is passed to Z(t) as an array.
Solution: add a for loop after you specify t, to calculate each N(t_i) for each t_i in the array t, and then change the plot part accordingly; alternatively, you have to change the "if else" part in the definition of z(t), into the way that treats t as an array (for example loop through t).
First of all thank you for your solution/comments.
I don't have an error message anymore. And my graphs are showing what they should.
Do you have any tips to make the code faster ? Because it still takes a lot of time to show the graphs (and my very old/slow PC doesn't like that).
Here is the new code (yes, I know, it is really long for such a simple code, but it works) :
from pylab import *
K,r,a,h,e,m=20,0.5,0.3,0.05,0.1,0.1
def dz(N,P):
return (r*N*(1-N/K)-a*N*P/(1+a*h*N),e*a*N*P/(1+a*h*N)-m*P)
N0=10
P0=3
print('conditions initiales :')
print('N0=')
print(N0)
print(' P0=')
print(P0)
pas=10**(-3)
def z(t):
u,x,y=0,N0,P0
if t>0 :
while t-u>pas:
x,y,u=x+pas*dz(x,y)[0],y+pas*dz(x,y)[1],u+pas
return x+(t-u)*dz(x,y)[0],y+(t-u)*dz(x,y)[1]
else :
while u-t>pas:
x,y,u=x-pas*dz(x,y)[0],y-pas*dz(x,y)[1],u-pas
return x+(t-u)*dz(x,y)[0],y+(t-u)*dz(x,y)[1]
def N(t):
return z(t)[0]
def P(t):
return z(t)[1]
close()
figure('N(t), P(t), P(N)')
n=100
t=linspace(0,100,n)
N1=zeros(n)
P1=zeros(n)
for i in range(len(t)):
N1[i]=N(t[i])
P1[i]=P(t[i])
subplot(2,2,1)
plot(t,N1,'b--')
title('N(t)')
xlabel('t')
ylabel('N')
autoscale(enable=True, axis=u'both', tight=True)
grid(True)
subplot(2,2,2)
plot(t,P1,'r--')
title('P(t)')
xlabel('t')
ylabel('P')
autoscale(enable=True, axis=u'both', tight=True)
grid(True)
subplot(2,2,3)
plot(N1,P1,'k')
xlabel('N')
ylabel('P')
autoscale(enable=True, axis=u'both', tight=True)
grid(True)
Thank you for your help and have a wonderful day.
I'm trying to build a module that will count all the color words in a text and then display them on a pie chart. I already have the counting and the chart coded separately, but I can't figure out how to connect them.
Right now I have:
def colorperc(text):
y0 = text.count("red")...text.count("rust")
y1 = ...
return {y0...y10}
colorperc(mobydick.txt)
...
fracs y0, y1...
How do I take these return values and plug them into the piechart fracs? I just keep getting an error: NameError: name 'y0' is not defined
First of all, text.count is unlikely to do what you want.
>>> "Red wired tired".count("red")
2
You will probably want to read through the text line by line, lowercase and split each line, and update a Counter. Then as Matt said, you will need to assign the value returned by your function rather than trying to access local variables.
Since your comment says you're using nltk, perhaps something like this (untested). We'll assume you're happy having the whole text in memory, since that matches your problem description.
from collections import Counter
import matplotlib.pyplot as plt
import nltk
def countcolors(text)
tokens = nltk.word_tokenize(text)
tokens = map(lamda x: x.lower(), tokens)
ctr = Counter(tokens)
colorlist = ("red", "rust", "blue", "green")
colorcounts = dict((color,ctr[color]) for clr in colorlist)
return colorcounts
#we'll call the text in memory `mytext`
colors, counts = countcolors(mytext).items()
fig, ax = plt.subplots(1,1)
ax.pie(counts,labels=colors)
plt.show()
Note that we simply use assignment (with =) to get the value returned by the function.
I'm still learning Python, and I'd love to know a way to make the following work:
a_function(
for n,item in enumerate(list):
inside_function(code code code,
code code code,
code code code))
So there's a function nested inside another function, and I need to play out the inside function a number of times, but not the outside one. The code I'm working with is not mine so I can't change the way these functions work.
I can edit with the actual code if anyone needs it, it's something from PyChart.
Edit: actual code:
ar = area.T(y_coord = category_coord.T(data, 0),
x_grid_style=line_style.gray50_dash1,
x_grid_interval=chartlength/5, x_range = (0,chartlength),
x_axis=axis.X(label="X label"),
y_axis=axis.Y(label="Y label"))
chart_object.set_defaults(interval_bar_plot.T, direction="horizontal",
width=5, cluster_sep = 0, data=data)
ar.add_plot(
for n,item in enumerate(compactlist):
interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)))
)
can = canvas.default_canvas()
can.set_title("Chromosome comparison")
can.set_author("Foo")
ar.draw()
The ar.add_plot function creates a working area in the canvas (as I understand it), while the interval_bar_plot function creates the bars, one by one. So I need multiple interval_bar_plot functions but only the one add_plot, or it simply repeats the first bar n times.
Edit: and the error:
File "intvlbar.py", line 105
for n,item in enumerate(compactlist):
^
SyntaxError: invalid syntax
What you are trying to do is pass several bar plot objects to the add_plot method (documented in here). One way you can do this is to pass them each explicitly. For example:
ar.add_plot(bar1, bar2, bar3)
Examples of this are in the sample code sections of the PyChart documentation for bar plots and interval bar plots, for example.
You do not want to do this because your compactlist might be inconveniently long or of varying length between runs. Another option is to use argument unpacking. Create a list containing your bar plot objects:
bars = [interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)))
for n,item in enumerate(compactlist)]
Now call add_plot with your bars:
ar.add_plot(*bars)
The error you are getting is because the for loop does not return anything in itself. But the for loop is placed inside the function call ar.add_plot() where the parameters should go. So python is telling you "ar.add_plot() needs parameters, but this for loop isn't going to give them to me"
What parameters does ar.add_plot() need?
You need something closer to this (though this probably isn't correct):
ar.add_plot()
for n,item in enumerate(compactlist):
interval_bar_plot.T(line_styles = [None, None],
fill_styles = [fill_style.red, None],
label=compactlist[n], cluster=(n,len(compactlist)