Matplotlib Patches not matching supplied arguments. - python

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."

Related

Circular / arc spine in custom matplotlib Axes class

So I am trying to build a class to create a radialplot, also known as
Galbraith plot (https://en.wikipedia.org/wiki/Galbraith_plot).
It's essentially a cartesian plot of standardised estimates vs associated errors. The estimates values are represented by the slope of the line that goes through the origin.
here is what it looks like.
Now I want reproduce that with matplotlib so I thought I would build a
class that inherits from Axes...
So far so good...
Now I am wondering what to do with the right spine.
I could obviously plot a line and create ticks manually in the plotting area but I would rather do it properly and use the matplotlib machinery...
I saw that there are options to create a circular spine, or even arc spine.
https://matplotlib.org/3.1.0/api/spines_api.html
Looking at the spine documentation a Spine object requires, an axes, a type and a path... I am not sure what to do with that.
If I you give a path to the Spine class, what's the point of having the arc_spine or circular_spine method...
I thought I could do something like this (self refers to an Axis instance):
x, y = self.zscale_coordinates()
verts = list(zip(x,y))
codes = [Path.LINETO for i in range(len(verts))]
codes[0] = Path.MOVETO
spine = Spine(self, "right", Path(verts, codes))
self.spines["right"] = spine
Any help would be really appreciated.
Maybe someone has already done this? I found some R or Java packages but nothing with matplotlib and I would like to include it in some python code.
Thanks!

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.

Matplotlib Event handling

I was playing with this example code for a while now.
http://matplotlib.org/examples/event_handling/poly_editor.html
I want to get a editable line instead of a polygon. I got it done by
xs = np.arange(0,1,0.1)
ys = xs*2.0
but still im getting a filled polygon. How can i just get a line ?
Output
I want to add a button to this so that when I press the button updated x,y pairs will be printed.
How can I get this done.
Thanks much in advance.
Use this statement in place of the similar one in the code.
poly = Polygon(list(zip(xs, ys)), animated=True, fc='w')
I can't help with adding a button. However, you could put the part of the code that creates the plot in a loop that prints what you want after plt.show(), and includes an input statement offering the user another opportunity to use the facility before exiting.

How to update color of streamplot in Matplotlib?

If have a streamplot:
c = streamplot(X, Y, U, V, color=(0,0,0,1))
Now, I want to hide it:
c.lines.set_alpha(0)
c.arrows.set_alpha(0)
draw()
The lines disapear, but not the arrows. Why? How can I change the alpha of the arrows? ('Im using qt4agg backend).
That's... bizarre. As far as I can tell, anything done to c.arrows doesn't actually change any of the arrows: c.arrows.set_visible(False) also does nothing. This is the case for both the Qt4Agg and iPython Inline backends.
One solution is to set the alpha/visibility directly on each arrow. For example, you could do the following:
for x in gca().get_children():
if type(x)==matplotlib.patches.FancyArrowPatch:
x.set_alpha(0) # or x.set_visible(False)
draw()
You may want to replace gca() with a specific axis.
This is not at all ideal, and I'm assuming there's some sort of bug involved. I'll have to look into it more closely.
Edit:
So, there are a few things here. Essentially, the c.arrows PatchCollection is completely useless, as best I can tell:
streamplot, in streamplot.py, doesn't add the c.arrows PatchCollection to the axis at all. Instead, it adds the FancyArrowPatches directly.
Even if it did, PatchCollection does not work with FancyArrowPatch.
There was supposed to be an issue filed about this, but it apparently was never done. I might work on it if I have a chance.
At the very least, the documentation should make clear that c.arrows is not a real collection and instead appears to be a bogus, useless object mean for future-compatibility.

Passing pyplot points as arguments

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], '+')

Categories