In trying to create a custom legend, I create an array of custom lines from which I want to extract the labels.
In a for loop, I create an array of custom_names like this:
custom_names=[custom_names, [Line2D([0],[0], color=colors[i], marker='None',label=filename[i][0:9])]]
I then try to use Legend to add this as a second, custom legend:
from matplotlib.legend import Legend
leg = Legend(ax, custom_names, loc='lower right')
ax.add_artist(leg)
this produces an error:
leg = Legend(ax, custom_names, loc='lower right')
TypeError: __init__() missing 1 required positional argument: 'labels'
How do I extract the labels used in the creation of the custom_names array to use it in the Legend function(?) ? Something like:
leg = Legend(ax, custom_names, custom_names.get_label(), loc='lower right')
but this doesn't work.
In fact, if I print custom_names, I get this:
print(custom_names)
[[[[], [<matplotlib.lines.Line2D object at 0x0000021232E72BC8>]], [<matplotlib.lines.Line2D object at 0x0000021232E83E08>]], [<matplotlib.lines.Line2D object at 0x0000021232E8CE08>]]
What is "inside" the ['<'matplotlib.lines.Line2D object at 0x0000021232E72BC8'>'] element, for instance? How do I see or get access to the attributes used in the creation of that array element via the
custom_names=[custom_names, [Line2D([0],[0], color=colors[i], marker='None',label=filename[i][0:9])]]
statement?
I apologize if the question doesn't make sense. I'm struggling with calling things by their proper names (I am not a s/w guy).....I can try and clarify based on questions anyone raises about this question.
Related
Lets assume i have a class which handels my data processing and this class contains a function that delivers my a standardize plot with a function that retunrs an matplotlib axis object.... now someone wants a GUI for this...
def get_plot(self, ax=None,.... *lkwargs)
# This function does some stuff and returns "ax": a matplotlib axis object
return ax
now want i want to do is to use the ax object with wxmplot. Ideally i want to pass the existing ax object with all its handels / content to wxmplot. Is this possible?
Thanks in advance
The simplest answer is "No". Wxmplot creates matplotlib figure and axes objects, but none of the main interface routines accept matplotlib objects.
OTOH, one can access the matplotlib axis after the wxmplot plotting objects are created. That is, it is possible to "do some stuff" (depending on the details) with the underlying axes.
I've created a plot in sympy and would like to customize the x- and y-axis. I want to turn them into red and have them be dashed. I've looked around and tried some stuff but nothing seems to work, such as:
plt.axhline(linewidth = 1, linestyle = '--', color = 'red')
plt.axvline(linewidth = 1, linestyle = '--', color = 'red')
Are there some ways to do this that would actually work?
Thanks in advance!
Sympy's source code of */sympy/plotting/plot.py has this comment:
Especially if you need publication ready graphs and this module is
not enough for you - just get the _backend attribute and add
whatever you want directly to it. In the case of matplotlib (the
common way to graph data in python) just copy _backend.fig which
is the figure and _backend.ax which is the axis and work on them
as you would on any other matplotlib object.
This means that, in general, Sympy's plots can be tweaked modifying the underlying Axes object, that can be accessed using the _backend attribute of the Sympy's plot instance.
To address your specific requests, each Axes contains an OrderedDict of Spine objects, the one that you want to modify are the 'bottom' and the 'left' ones (to modify these objects you have to use their set_x methods)
In [33]: from sympy import *
...: x = symbols('x')
...: p = plot(sin(x))
...: for spine in ('bottom', 'left'):
...: p._backend.ax.spines[spine].set_linestyle((0, (5, 10)))
...: p._backend.ax.spines[spine].set_edgecolor('red')
...: p._backend.fig.savefig('Figure_1.png')
produces
Note: if one uses p.save('...') then the figure is reset and they'll miss any tweaking they've made, hence I used the savefig method of the underlying Figure object, accessed again using the _backend attribute.
I have plotted a histogram and would like to modify it, then re-plot it. It won't plot again without redefining the Figure and Axes object definitions. I'm using Jupyter Notebook, and I'm new to matplotlib, so I don't know if this is something that I'm not understanding about matplotlib, if it's an issue with the Jupyter Notebook or something else.
Here's my 1st block of code:
"""Here's some data."""
some_data = np.random.randn(150)
"""Here I define my `Figure` and `Axes` objects."""
fig, ax = plt.subplots()
"""Then I make a histogram from them, and it shows up just fine."""
ax.hist(some_data, range=(0, 5))
plt.show()
Here's the output from my 1st block of code:
Here's my 2nd block of code:
"""Here I modify the parameter `bins`."""
ax.hist(some_data, bins=20, range=(0, 5))
"""When I try to make a new histogram, it doesn't work."""
plt.show()
My 2nd block of code generates no visible output, which is the problem.
Here's my 3rd and final block of code:
"""But it does work if I define new `Figure` and `Axes` objects.
Why is this?
How can I display new, modified plots without defining new `Figure` and/or `Axes` objects? """
new_fig, new_ax = plt.subplots()
new_ax.hist(some_data, bins=20, range=(0, 5))
plt.show()
Here's the output from my 3rd and final block of code:
Thanks in advance.
When you generate a figure or an axis, it remains accessible for rendering or display until it's used for rendering or display. Once you execute plt.show() in your first block, the ax becomes unavailable. Your 3rd block of code is showing a plot because you're regenerating the figure and axes.
I'm trying to conciliate dots annotation in a Matplotlib scatter plot with a manual limit setting, but I either got an error message or I get a design problem.
Here is my code :
fig, ax = plt.subplots(figsize = (20,10)) #manual limit setting
plt.axis([-2,3,-2.5,5])
plt.scatter(x, y)
for i, txt in enumerate(n): #dot annotation
ax.annotate(txt, (x[i], y[i]))
Here is a screen cap of the output (I got the final scatter plot as a small rectangle located in the left corner of a big white rectangle :
I tried this also :
fig, ax = plt.subplots(figsize = (20,10))
ax = plt.axis([-2,3,-2.5,5])
plt.scatter(x, y)
for i, txt in enumerate(n):
ax.annotate(txt, (x[i], y[i]))
But of course I got the following error message (even though the chart correctly displays, but without the labels next to each corresponding dot).
AttributeError: 'list' object has no attribute 'annotate'
The error arises because my loop tries to iterate through ax = plt.axis([-2,3,-2.5,5]), which doesn't make sense indeed.
Any solution to overcome this issue ?
Thank you
The problem occurs because of the special casing of texts when it comes to clipping. Usually you might want text outside the axes to be shown. Therefore annotations and text have a annotation_clip argument. However, this interferes with the bbox_inches="tight" option when saving annotations, because the annotations is then still considered part of the layout and hence the figure takes annotations outside the axes still into account.
Two solutions:
Set annotation_clip and clip_on. I.e. You may explicitely tell the annotation to clip at the axes:
ax.annotate(txt, (x[i], y[i]), annotation_clip=True, clip_on=True)
Set bbox_inches to None. When using the IPython inline backend you can tell it not to expand the figure via
%config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
in a cell before starting to create your content. (This is seen in this answer)
I can't replicate the first issue (tried in versions 2.2.3, 3.1.1, 3.1.2) - I get this (using random data). Try upgrading your version of matplotlib or using
plt.savefig('/path/to/output/image.png')
To save the figure to the disk instead of showing it directly and see if the problem persists.
I can however explain the error
AttributeError: 'list' object has no attribute 'annotate'
This occurs because plt.axis() returns [xmin, xmax, ymin, ymax], not an axes instance (fig, ax = plt.subplots(figsize=(20,10) returns an axes instance to ax).
I am trying to add a BrokenBarHCollection to multiple axis on my figure, as follows:
barcollection = collections.BrokenBarHCollection(...
ax1 = plt.subplot(211)
ax1.add_collection(barcollection)
ax2 = plt.subplot(212)
ax2.add_collection(barcollection)
plt.show()
As is, the figure only shows the collection in the second subplot. If I comment the ax2.add line out, it shows the collection only in the first subplot. Declaring the barcollection again between lines 3 and 4 makes it show up in both subplots. Why is this happening?
This is because the matplotlib objects know what plot they are attached to and will not attach to more than one.
If you use the copy module to make a shallow copy, then you can re-use most of the data structure across multiple axes.
import copy
bc2 = copy.copy(barcollection)
ax2.add_collection(bc2)
There was another question about this recently, but I am having trouble finding it.