How to update graph in a loop? - python

I created a little model to show a disease could spread. I succeded in showing a different graph for each iteration but I would like to plot a single graph that gets updated at each iteration and shows how the particles move from one iteration to the other.
This is where i call the data i want to plot:
def plotter(population):
for people in population:
if people.status==0:
plt.scatter(people.positionx,people.positiony,c='b')
else:
if people.healthstatus==0:
plt.scatter(people.positionx,people.positiony,c='g')
if people.healthstatus==1:
plt.scatter(people.positionx,people.positiony,c='y')
if people.healthstatus==2:
plt.scatter(people.positionx,people.positiony,c='r')
this is the main where iterate the model
def main(iterations,populationsize):
popde=generator(populationsize)
population=popde[0]
dead=popde[1]
plt.ion()
for numit in range(iterations):
population=movement(population)
popde2=infection(population,populationsize,dead)
population=popde2[0]
dead=popde2[1]
populationsize=popde2[2]
plotter(population)
plt.pause(0.1)
plt.draw()
The code works perfectly fine, it's just a style issue
I tried looking for other solutions on the web but I couldn't the one that fits my problem. Thanks in advance to all those who will help!

I assume that population is a list and that the attributes positionx and positiony of the object people are scalar numbers (float or int). For future questions consider posting a minimal working example. The following code is based on this answer to a similar question.
It is not necessary to create an individual scatter plot for each person. The code below collects all x-, y-positions and colors in one timestep and plots them in a single scatter plot object. This scatter plot is created before the loop (saved to the variable sc) and then passed to the plotter function, which updates the plot properties (positions & colors) each iteration.
import matplotlib.pyplot as plt
def main(iterations,populationsize):
popde=generator(populationsize)
population=popde[0]
dead=popde[1]
plt.ion()
sc = plt.scatter([], []) # initialise empty scatter plot
for numit in range(iterations):
population=movement(population)
popde2=infection(population,populationsize,dead)
population=popde2[0]
dead=popde2[1]
populationsize=popde2[2]
plotter(population, sc) # pass scatter object to plotter function
plt.draw() # update the current figure
plt.pause(0.1)
def plotter(population, sc):
# create list of all x- and y-positions
posx = [people.positionx for people in population]
posy = [people.positiony for people in population]
# create list of colors
color = ['#88888']*len(population) # initialise all colors as grey
for ip, people in enumerate(population):
if people.status == 0:
color[ip] = 'b'
elif people.healthstatus == 0:
color[ip] = 'g'
elif people.healthstatus == 1:
color[ip] = 'y'
elif people.healthstatus == 2:
color[ip] = 'r'
# if none of these criteria match, the marker remains grey
sc.set_offsets(np.c_[posx, posy]) # update marker positions
sc.set_color(color) # update marker color
As a side note, I would recommend to use the "object-oriented coding style" of matplotlib when writing scripts or functions. The above code always plots in the currently active figure, so if you have previously created another figure or the active figure changes while the code is running you might suddenly be plotting in the wrong figure.

Related

Making a legend / getting handles of Kaplan Meier plot objects in python lifelines

I'm working with the lifelines package to make Kaplan-Meier curves. I'd like to add the censored data, but also have a legend that only mentions the two lines.
I'm calling the function iteratively twice to plot two separate lines, as so:
def plot_km(col,months,dpi):
ax = plt.subplot(111)
clrIdx = 0
for r in df[col].unique():
ix = df[col] == r
plt.rcParams['figure.dpi'] = dpi
plt.rcParams['savefig.dpi'] = dpi
kmf.fit(T[ix], C[ix],label=r)
kmf.plot(ax=ax, color=colorsKM[clrIdx],show_censors=True,censor_styles={'ms': 6, 'marker': 's','label':'_out_'})
if clrIdx == 1:
plt.legend(handles=[],labels=['test1', 'test2'])
clrIdx += 1
Where the output is a KM curve as well as the censored datapoints. However, I can't seem to figure out a way to interact with the handles/labels that gets the desired output. The above code seems to ignore the censored objects by using 'label':'_out_' , but it ignores my custom labels in the plt.legend() call. If I enter anything for handles, e.g.: plt.legend(handles=[line0] , it throws "NameError: name 'line0' is not defined"
I tried playing around with h, l = ax.get_legend_handles_labels() but this always returns empty. I believe my issue is with not understanding how each of these "artists"(?) are getting stored, and how to get them again.

Set colors in Matplotlib.pie by label name

I have created a large matrix of pie plots using a function that runs through a datafrane. I am only plotting in the pie charts two variables. When one of the variables is not present in the specific data, matplotlib automatically switches the colors. See sample picture below.
How would I make sure the colors stay consistent based on values? Would I manipulate the colors argument in my function?
my def code that I run the data through
#function to make matrix
def pie(v, l, color=None):
plt.pie(v, labels=l.values, colors = ????, autopct='%0.f')
#function being called for data - l='coverage'
g = sns.FacetGrid(market_covered_sum, col="mkt_mcap_decile", row="market",
margin_titles=True)
g.map(pie, "MKT_Cap_mn", "coverage").set_axis_labels(" ", " ")
I want to keep the colors consistent, and change them to a color code once I can keep consistent.

Using a color cycler more than once in a single matplotlib plot

The following code is called twice in succession for two different sets of evts events data:
mpl.rcParams['axes.prop_cycle'] = plt.cycler('color',colors)
for e in evts:
ef = e[e[:,0]<3500]
plt.plot(ef[:,0],ef[:,1]) # ,cmap='Greens')
The first invocation is with mpl.cm.Greens and the second with mpl.cm.Reds. It seems though that only the first invocation of setting mpl.rcParams['axes.prop_cycle'] actually "takes": even though both Greens and Reds were provided all of the data series are instead printed in Green:
So is it possible to invoke two different cycler's for one plot?
Update Here is the output and code for it using the accepted answer from #ImportanceOfBeingEarnest:
def plotEvts(evts, colors):
ax = plt.axes()
ax.set_prop_cycle(plt.cycler('color',colors))
for e in evts:
ef = e[e[:,0]<3500]
plt.plot(ef[:,0],ef[:,1]) # ,cmap='Greens')
plt.title('Faster RCNN Overall Loss Progression')
plt.xlabel('Steps Count: Approx 25 steps per minute')
plt.ylabel('Loss')
evt_colors = [ (mpl.cm.Greens,'/data/tfevents/sobelXYCanny'),
(mpl.cm.Reds,'/data/tfevents/baseline')]
for cmapMonad, ddir in evt_colors:
evts = processFiles(ddir)
colors = cmapMonad(np.linspace(0,1,2*len(evts))[len(evts):-1])
plotEvts(evts,colors)
plt.show()
As the name axes.prop_cycle suggests, the cycle is a property of the axes. That property is set at axes creation time. Subsequently, the same property cycler is used within the same axes.
You may change the property cycler via Axes.set_prop_cycle() in between your two codes though.
In parallel with the esteemed #ImportaneOfBeingEarnest answer I used the following workaround - that is to combine the color cycles into one big one:
evt_colors = [ (mpl.cm.Greens,'/data/tfevents/sobelXYCanny'),
(mpl.cm.Reds,'/data/tfevents/baseline')]
evts = []
colors = []
for cmapMonad, ddir in evt_colors:
evts.extend(processFiles(ddir))
colors.extend( cmapMonad(np.linspace(0,1,2*len(evts))[len(evts):-1]))
plotEvts(evts,colors)
plt.show()
This gives the desired effect
In any case I will try out his answer as well now.

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.

Add legend or background image to Igraph 0.6 (for python) plot

I plot a graph with python 2.7 by using Igraph 0.6 with the Cairo extention for plotting. All good but I would like to add a legend each time I plot.
If I could only add a background image to the plot that would be also fine, because I make a white image with the right size and with the legend already added there (with general sign explanation).
None of this I can do, nor I can find by googleing it. Maybe I'm just unable to get on the right side of Google or to find the right keyword in Igraph documentations.
gp = Graph(). It's global. Has vertex and edge sequences etc. There are some lists which contain further information about vertexes and edges (in ex.: self.gp_cities, self.road_kind) Here is how I plot:
def showitshort(self,event):
global gp
layout = gp.layout("kk")
color_dict = {"1": "red", "20": "blue"}
visual_style = {}
visual_style["vertex_size"] = 15
visual_style["vertex_color"] = ["yellow"]
visual_style["edge_color"] = [color_dict[elektro] for elektro in self.road_kind]
visual_style["vertex_label"] = self.gp_cities
visual_style["layout"] = layout
visual_style["bbox"] = (4000, 2500)
visual_style["margin"] = 100
visual_style["vertex_label_dist"] = 5
visual_style["vertex_shape"] = "triangle-up"
plot(gp,**visual_style)
The right link I think is enough. Please help a little and Thank you in advance!
The trick is that you can pass an existing Cairo surface into plot and it will simply plot the graph on that surface instead of creating a new one. So, basically, you need to construct a Cairo surface (say, an ImageSurface), draw your legend using standard Cairo calls onto that surface, then pass the surface to plot as follows:
plot(gp, target=my_surface, **visual_style)
As far as I know, plot() will not show the graph itself when invoked this way; it will simply return a Plot object. You can call the show() method of the Plot object to show it or call the save() method to save it into a PNG file.

Categories