Passing pyplot points as arguments - python

So I initialised a pyplot figure
import ... ## import all relevent modules
f = plt.figure(figsize=(8,3),dpi(100)
a = plt.subplot(111)
a.set_xlim(left=0,right=25,auto=False)
a.set_ylim(bottom=0,top=250,auto=False)
a.plot([5,10,15],[80,150,210])
plt.show()
This works fine... What I want to be able to do is to write a function that can update the scatter plot dynamically... Something like:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord])
a.draw() ## I thought this would work... :(
No error, but the point doesn't get plotted. How can I get around this? The reason I've done it using figures is so I can embed it in Tkinter.
Thanks for your help!

plot is perfectly fine to use for plotting individual points (it is even recommend over scatter, if you don't wanna add additional information through color or size of the dots). What is missing in the initial example is setting the right linestyle; obviously, a line consisting of a single point doesn't show up. Changing the line style to '+' or something similar fixes the problem:
def plot_point(x_coord,y_coord):
a.plot([x_coord],[y_coord], '+')

Related

How can I display and update two matplotlib plots in the same window at the same time?

I've implemented a GUI that displays two dropdown menus in which you can choose two different set of graphs to be displayed. However when I create the graphs with the following code:
import matplotlib.pyplot as plt
from matplotlib.backends.backedn_qt4agg import FigureCanvasQTAgg as FigureCanvas
self.comparison_figure1 = plt.figure(figsize=(15,5))
self.comparison_canvas1 = FigureCanvas(self.comparison_figure1)
self.comparison_figure2 = plt.figure(figsize=15,5))
self.comparison_canvas2 = FigureCanvas(self.comparison_figure2)
And then I try to update the plots (plt.tight_layout() for example)
def on_resize(event):
plt.tight_layout()
self.comparison_canvas2.draw()
self.comparison_canvas1.draw() #this would do nothing
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
only the last plot called with "plt." is updated. How do I write my code so that I can reference both plots.
I've also tried to create one plot where I have both graphs being displayed side by side but because of the need to update the graphs independently I encountered more problems. If you are able to make it work that way instead, great! I'm just thinking that fixing the previous problem may be simpler.
If you need more code I can post it!
#
#
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any "plt." with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
#
#
Problem:
Previous solution hasn't fixed all plot updating issues. Though "plt" can be replaced with self.comparison_figure1 when using tight_layout(), the same cannot be done when using cla() to clear the plot.
Code where I'm using cla():
sns.set(style="whitegrid")
plt.cla()
ax = self.comparison_figure2.add_subplot(111)
.....
.....
I can post more code if you need it!
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any plt with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)

How do pyplot functions (show, savefig, etc) work without "object" being passed in?

How do pyplot functions such as show() and savefig() not require a "plot object" to work?
For example, the following code works, but somehow I expect to use a file handler or pass a "plot object" into plt.show() and plot.savefig("venn3.pdf").
from matplotlib import pyplot as plt
from matplotlib_venn import venn3, venn3_circles
# Subset sizes
s = (2,3,4,3,1,0.5,4)
v = venn3(subsets=s, set_labels=('A', 'B', 'C'))
# Subset labels
v.get_label_by_id('100').set_text('Abc')
v.get_label_by_id('010').set_text('aBc')
v.get_label_by_id('110').set_text('ABc')
v.get_label_by_id('001').set_text('Abc')
v.get_label_by_id('101').set_text('aBc')
v.get_label_by_id('011').set_text('ABc')
v.get_label_by_id('111').set_text('ABC')
# Subset colors
v.get_patch_by_id('100').set_color('c')
v.get_patch_by_id('010').set_color('#993333')
v.get_patch_by_id('110').set_color('blue')
# Subset alphas
v.get_patch_by_id('101').set_alpha(0.4)
v.get_patch_by_id('011').set_alpha(1.0)
v.get_patch_by_id('111').set_alpha(0.7)
# Border styles
c = venn3_circles(subsets=s, linestyle='solid')
c[0].set_ls('dotted') # Line style
c[1].set_ls('dashed')
c[2].set_lw(1.0) # Line width
plt.show() # For show() to work without using variable v seems counter-intuitive to me.
plt.savefig("venn3.pdf") # For savefig() to work without using variable v seems counter-intuitive to me.
2[]
matplotlib.pyplot is often called "statemachine". This means that the function it provides do certain things depending on the internal state of pyplot.
In your code, you have created a figure and this is stored as an object; pyplot knows it has one figure.
If you then call other commands, it is assumend that they apply to that one figure which has been created previously, like plt.savefig.
plt.show() would work on all previously created figures (all of them would be shown).
Pyplot uses a global variable to hold the figure object. All pyplot functions work with that variable(s). If you are working interactively, pyplot is perfectly fine since only you will modify that variable. If you are writing multi-threaded or multi-user code pyplot will not work, and you would have to use the layer benath it, which needs the figure object passed in (and is a terrible interface).

set_array() in tripcolor bug?

I am new to Python and matplotlib, and I recently referenced to THIS to update my tripcolor plot. With following data preparation
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import math
r = np.zeros((100,100))
t = np.zeros((100,100))
for i in range(0,100):
for j in range(0,100):
r[i,j]=i
t[i,j]=2*math.pi*j/100
x=r*np.cos(t)
y=r*np.sin(t)
z=r*r
xf=x.flatten()
yf=y.flatten()
zf=z.flatten()
triang = tri.Triangulation(xf,yf)
If I use tripcolor as it is intended,
# Works well
p = plt.tripcolor(triang, zf)
correct figure appears. But, if I try to update after creating tripcolor,
# Not working well
p = plt.tripcolor(triang, xf)
p.set_array(zf)
then, wrong figure appears. Both xf and zf have identical dimensions.
What am I doing wrong? What is the cause of the problem, and how can I avoid it?
Many thanks in advance.
=========================================================
Update
Thank you all. I actually solved myself.
The key was that I need to assign color for each area, which is controlled by shading argument, and default value for tripcolor is 'flat', which is, color for each vertex. So, when I plot the first figure, I need to make sure shading is 'gouraud', which assigns color for each area.
So,
p = plt.tripcolor(triang, xf, shading='gouraud')
p.set_array(zf)
works as I intended.
The reason
p = plt.tripcolor(triang, xf)
p.set_array(zf)
is not working as (may be) expected, is the following. In your case plt.tripcolor() returns a PolyCollection. The PolyCollection's set_array() will essentially set the colors of that Collection. However, the underlying triangles will not be changed, such that you end up with the triangles from xf but the colors from zf.
Since the generation of the tripcolor PolyCollection is quite involved (as it calls Triangulation itself) and there probably is no helper function to set the data externally (at least I am not aware of any), the solution might be not to update the tripcolor at all and instead generate a new one.
Is there any reason for you to update? Couldn't you just directly create p = plt.tripcolor(triang, zf)?
In case there is a real reason to it, like in an animation or so, an option would be to delete the first tripcolor plot before setting up the next.
# create one plot
p = plt.tripcolor(triang, xf)
#delete this plot (you need both lines actually!!)
p.remove()
del p
#create second plot
p = plt.tripcolor(triang, zf)
This is not really efficient, though, and in case someone has a better idea, I'd like to hear about that one as well.

Python matplotlib: how to plot realtime over plotted graph

Here I have plotted a line chart with two lists
import random as random
import matplotlib.pyplot as plt
lis1=random.sample(range(1, 100), 10)
lis2 = random.sample(range(1, 100), 10)
plt.plot(range(0,len(lis1), 1), lis1)
plt.plot(range(0,len(lis2), 1), lis2)
plt.show()
Now, I'm getting a third list from Arduino in realtime. My question is how to plot that third list/line over this plot without redrawing entire chart.
EDIT: Third list is something like this
import time
lis3 =[]
for i in range(10):
lis3.append(i)
time.sleep(1)
plt.show() will display the current chart that you're working on whereas plt.draw() will re-draw the figure. This essentially allows you to change the graph as your data changes
The plt.draw docs state:
This is used in interactive mode to update a figure that has been altered using one or more plot object method calls; it is not needed if figure modification is done entirely with pyplot functions, if a sequence of modifications ends with a pyplot function, or if matplotlib is in non-interactive mode and the sequence of modifications ends with show() or savefig().
have a look at the following post: When to use cla(), clf() or close() for clearing a plot in matplotlib?
i think you can clear the figure by using plt.clear() in an timer event. Re-drawing can be done by using the plt.draw() function. Because of the realtime data you have to have a function which is called after a certain delay. There i would call this plt.clear() or plt.draw() function.
Afterwards you have to re fill the lists or make a new list to draw the third line.
I don't know a better solution and maybe that's not what you want, because it's some keind of re-drawing but i hope that this is useful for you!
Also have a look at:
Dynamically updating plot in matplotlib
How to update a plot in matplotlib?

Matplotlib Patches not matching supplied arguments.

been struggling with python and the matplotlib module. I am trying to draw some circles that are not filled and outlined in black. I am putting the correct arguments in the artist circle but it seems to ignore it and put blue. any ideas? Also the figure shows up automatically without me stating draw or show. How can i block that and control when the graph pops up? Thanks in advance.
my code
def draw_lattice(self,chart):
patches = []
for x in range(1,4):
for y in range (1,4):
circle = Circle((x,y), .25,color='k',fill=False)
# chart.add_patch(circle)
patches.append(circle)
p = PatchCollection(patches)
chart.add_collection(p)
Thanks in advance.
* UPDATE *
if i add each circle individually to the axes it will be formatted properly. If i add the collection it does not work. I have many more shapes to add and i would like to go the collection route. Any reason why one way would work and another wouldn't? I read somewhere that you need to add the artist but i tried that and got an error.
This is a rather late answer but I just came across the same problem and here is how to solve it:
What you need to do is tell the PatchCollection to match the original patches. To do this simply add match_original=True, like so:
p = PatchCollection(my_patches, match_original=True)
You can set the color of the circles when you create the patch collection:
p = PatchCollection(patches,facecolors='w',edgecolor='k')
From the Collection documentation:
"If any of edgecolors, facecolors, linewidths, antialiaseds are None, they default to their matplotlib.rcParams patch setting, in sequence form."

Categories