Matplotlib has duplicate legend entries - python

I'm using pylab.plot() in a for loop, and for some reason the legend has 6 entries, even though the for loop is only executed 3 times
#Plot maximum confidence
pylab.figure()
for numPeers in sorted(peers.keys()):
percentUni, maxes = peers[numPeers]
labels = list(set([i[1] for i in sorted(maxes,
key=itemgetter(1))]))
percentUni = [i[0] for i in sorted(maxes, key=itemgetter(1))]
x = []
y = []
ci = []
for l in xrange(len(labels)):
x.append(l+1)
y.append(max(maxes[l*3:l*3+3]))
pylab.plot(x, y, marker='o', label = "N=%d"%numPeers)
pylab.title('Maximal confidence in sender')
pylab.xlabel('Contribute Interval')
pylab.ylabel('Percent confident')
pylab.ylim([0,1])
pylab.xlim([0.5, 7.5])
pylab.xticks(xrange(1,8), labels)
pylab.legend(loc='upper right')
The plot looks like this, with each legend entry having exactly 2 copies.
I know the loop only runs 3x, because if I put in a print statement to debug, it only prints the string 3x.
I did see this in my search, but didn't find it helpful:
Duplicate items in legend in matplotlib?

I had a similar problem. What I ended up doing is add plt.close() at the beginning of my loop. I suspect you're seeing 6 because you have a nested loop where you're changing the x and y.

It ended up being a bug/type on my part, where I was supposed to write
maxes = [i[0] for i in sorted(maxes, key=itemgetter(1))]
instead of
percentUni = [i[0] for i in sorted(maxes, key=itemgetter(1))]
This mistake meant that maxes remained a list of 2-tuples instead of a list of integers, which is why things were plotted twice. And because I restricted the y-axis, I never saw that there were additional data elements plotted.
Thanks for your help, those who did answer!

Related

Full graph without calculating all points, possible?

I would like to draw a graph with predefined values from 0 to 605 for example. My pc is not powerful enough to calculate everything so I would like to calculate only some points and connect them all to have a curve on the interval [0;605]. How can I do this? Is this possible?
I tried to put a step, but it automatically reduces the interval.
In my current code it only shows me 605/10 values = 60, so the range of the graph is from 0 to 60 for the x-axis.
tab=[]
for k in range(1,605,10):
img2 = rgb(k)
d = psnr(img1,img2)
tab.append(d)
plt.plot(tab)
plt.xlabel("k")
plt.ylabel("PSNR")
plt.show()
You can set the xticks by yourself: plt.xticks(x_values, [0, 10, 20, 30, ...])
You need to plot with the pattern plt.plot(x, y) instead of only plt.plot(y).
A quick fix (just to show the idea) can be:
Create an empty x, just like tab: x = []
Append k to x in the loop: x.append(k)
Do plt.plot(x, tab), instead of plt.plot(tab)

Is there any way to create a number of equal class objects in a for loop in Python?

I started learning Python about a month ago because I need it for a project at University, so I apologize in advance for any conceptual mistakes or erroneous assumptions I may make in my explanation.
Before explaining my problem, I checked a couple of questions like the following, but I don't think the answers are what I need:
Python create objects in for loop
The program simulates the trajectories of a number of fluid particles. The way I do this is by creating a 'line' and a 'dot' object that will store the values of the x- and y-coordinates of the particles and then animating them with matplotlib:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import math
# initializing the figure in
# which the graph will be plotted
fig = plt.figure()
# setting limits for both the x-axis and y-axis
axis = plt.axes(xlim =(0, 20),
ylim =(-2, 50))
# initializing a line variable for each
# trajectory to be plotted
line1, = axis.plot([], [], lw = 1)
line2, = axis.plot([], [], lw = 1)
redDot, = plt.plot([1], [10], 'ro', markersize = 2)
# data which the line will
# contain (x, y). Lines are
# initially empty objects
def init():
line1.set_data([], [])
line2.set_data([], [])
return line1, line2
# initializing empty values
# for x and y coordinates
xdata1, ydata1 = [], []
xdata2, ydata2 = [], []
# animation function
def animate(i):
# t is a parameter which varies
# with the frame number
t = 0.1 * i
# x, y values to be plotted
x = math.exp(t)
y = 1 + (2/3)*t**(3/2)
# appending values to the previously
# empty x and y data holders
xdata1.append(x)
ydata1.append(y)
line1.set_data(xdata1, ydata1)
x = math.exp(t)
y = 10 + (2/3)*t**(3/2)
# appending values to the previously
# empty x and y data holders
xdata2.append(x)
ydata2.append(y)
line2.set_data(xdata2, ydata2)
redDot.set_data(x, y)
return line1, line2, redDot
anim = FuncAnimation(fig, animate, init_func = init,
frames = 200, interval = 20, blit = True)
The code above creates a line where it says line1, = axis.plot([], [], lw = 1). Immediately after that, it creates a point with redDot, = plt.plot([1], [10], 'ro', markersize = 2), which will follow the line. The problem is I intend to simulate 10 particles at the same time, and I believe there must be a cleaner, more efficient way to create all these objects than having to repeat those two lines of code 10 times. What's more, there are two lists that act as data holders for each particle, and the animate function needs to go through all of them too to assign the calculated values. It would make for a very lengthy code, and it would be downright impractical if I wanted to animate a larger number of particles.
Is there any way to do that with a loop? Something like:
for i in list(range(number_of_particles)):
#number_of_particles lines and dots are created here
I'm wondering if I could store these objets in a list, such as:
my_list = []
for i in list(range(number_of_particles)):
my_list.append(axis.plot([], [], lw = 1))
Then I would only have to call the items of said list in another for loop.
Again, I am just a newbie and I really appreciate any input from anyone who might want to help. Thanks!
I think your own suggestions work more or less, which I have just adapted here below.
To create a list of Matplotlib.lines.Line2D instances
number_of_particles = 10
lines = [axis.plot([], [], lw=1)[0] for i in range(number_of_particles)]
Storing the data for each line: Create a list to store the x and y data for each line, you can do the following, which simply creates a list of tuples, where each tuple contains a list for the x-coordinates and a list for the y-coordinates. This is similar to the spirit of your xdata1, ydata1 = [], [], but just a list of such tuples.
lines_data = [([], []) for i in range(number_of_particles)]
Updating each of the lines in the animate function. Here the built-in enumerate function gets you both the index of the ith iteration so you can use it in the loop, and the item in the iterable list. I suppose to have more flexibility in the functions you could perhaps add the constants to lists and then index them with i if needed.
for i, line in enumerate(lines):
x = math.exp(t)
y = i + (2/3)*t**(3/2)
lines_data[i][0].append(x)
lines_data[i][1].append(y)
line.set_data(lines_data[i][0], lines_data[i][1])
Smart way to return a list. When returning use a * to have the list unpacked. Have just added the lines list to the existing return statement.
return line1, line2, redDot, *lines
Finally, I was not fully sure about the comment
What's more, there are two lists that act as data holders for each
particle, and the animate function needs to go through all of them too
to assign the calculated values.
Maybe you could elaborate on that, but it seems that the new computations are just appended to the end, which I suppose shouldn't have the worst impact.

How can I plot multiple line in the same graph in python?

I want to create this graph 1 in python using matplotlib. I created a list called generation that is initialized with values from 0 to 200. I created a list variable consisting of 38 lists. Each list consists of 200 float numbers. I tried to plot the data but I have the error:
ValueError: x and y must have same first dimension, but have shapes (200,) and (38,)
My code:
generation = []
for i in range(200):
generation.append(i)
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title("A test graph")
for i in range(len(listt)):
#generation is a list with 200 values
#listt is a list with 38 lists
#list1 is a list with 200 values
plt.plot(generation ,[list1[i] for list1 in listt],label = 'id %s'%i)
plt.legend()
plt.show()
The final graph I want to look like the one below:
Each line in this graph 1 corresponds to a different input value. For each input, the algorithm runs 100 generations. The graph shows how the results of the algorithm evolve over 100 generations.
You're almost there! You only need to use listt[i] instead of [list1[i] for list1 in listt].
So, the code should look like this:
import matplotlib.pyplot as plt
#random listt
listt = [[i for j in range(200)] for i in range(38)]
generation = []
for i in range(200):
generation.append(i)
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title("A test graph")
for i in range(len(listt)):
plt.plot(generation ,listt[i],label = 'id %s'%i) #<--- change here
plt.legend()
plt.show()
And it returns this graph:
Of course, it won't be exactly as yours since I randomly generated listt.

How to use matplotlib to plot only the last 50 values of growling lists?

I am trying to use matplotlib function in Python to interactively plot only the last 50 values of 2 growing lists while a loop goes on. However, once the size of the lists grow to more than 50, the values of the plot lines start overlapping.
I want to clear the overlapping.
Here is the photo of the plot at iteration < 50. Nice and clean.
Here is the photo of the plot at iteration > 50. You can see that it's getting messy.
Here is my code
import matplotlib.pyplot as plt
ls1 = []
ls2 = []
while True:
(some computation to get, in every iteration, 2 new values: ls1_new and ls2_new)
ls1.append(ls1_new)
ls2.append(ls2_new)
plt.plot(ls1[-50:])
plt.plot(ls2[-50:])
plt.draw()
plt.pause(0.0001)
Can anyone help me solve the overlapping part? Thanks ahead for the help! :)
Your problem is that you are creating new lines at every iteration. It would probably be nicer to update your existing lines instead. The code below will probably won't work straight away, but it should point you in the right direction. The general idea is to keep a reference to the Line2D object returned by plt.plot() and then using the member functions Line2D.set_data(x, y) or Line2D.set_ydata(y) to update the line at each iteration.
import matplotlib.pyplot as plt
ls1 = []
ls2 = []
l1, = plt.plot([])
l2, = plt.plot([])
while True:
(some computation to get, in every iteration, 2 new values: ls1_new and ls2_new)
ls1.append(ls1_new)
ls2.append(ls2_new)
l1.set_data(range(50),ls1[-50:])
l2.set_data(range(50),ls2[-50:])
plt.draw()
plt.pause(0.0001)

Python plot fix size

thanks for reading my question !
I created plot using Pyplot, this is my data :
Length of "point" array is : 114745
Length of "id_item" array is : 114745
Length of "sessions" array is : 92128
And this is my code :
point = []
id_item = []
sessions = [] # temp_dict.keys()
for item in cursor_fromCompanyDB:
sessions.append(item['sessionId'])
for object_item in item['objects']:
point.append(object_item['point'])
id_item.append(object_item['id'])
plt.figure()
plt.title('Scatter Point and Id of SessionId', fontsize=20)
plt.xlabel('point', fontsize=15)
plt.ylabel('Item', fontsize=15)
plt.scatter(point, id_item, marker = 'o')
plt.autoscale(enable=True, axis=u'both', tight=False)
for label, x, y in zip(sessions, point, id_item):
plt.annotate(label, xy = (x, y))
plt.show()
And this is result :
As you can see, values very close and hard to see.
I want value in id_item show full value and values in the center (sessions) easy to see.
Thanks very much to help me.
There are two ways to fix your plot:
Make the plot so large that you have to scroll down pages to see every session ID.
Reduce your data / don't display everything.
Personally, I'd take option 2. At a certain point it becomes impossible or just really ugly to display a certain amount of points, especially with labels assigned to them. You will have to make sacrifices somewhere.
Edit: If you really want to change your figure size, look here for a thread explaining how to do that.

Categories