I am using Matplotlib to plot a 3*4 subplots. I am going to change the style of tick label to 'sci' so that the plotting can be more neat. But using the following code, the ticklabel_format do not even take effect on the axis.
import matplotlib.pylab as pl
fig, axes = pl.subplots(nrows=3, ncols=4)
for i,row in enumerate(axes):
for j,ax in enumerate(row):
ax.set_title('title')
ax.ticklabel_format(styl='plain')
pl.tight_layout()
pl.show()
I have intently make a typo 'styl', but it doesn't report error. So I assume the ticklabel_format function doesn't even run.
It not taking effect because ax.ticklabel_format takes any keyword argument and creates a dictionary. To see this take a look at the documentation here and you will see it takes an argument **kwargs. If you just replace styl with style then your code will work.
I suggest you take a look at this SO post to get a feel of what was going wrong but in brief: the function can take any argument. It then attempts to pass these on to other functions. If none of these require it then the argument is simply lost in the ether. As a result there is no error message!
Try playing around with the following example to get a feel **kwargs.
def f(**kwargs):
print kwargs
return
f(anything='something')
Related
I am calling sns.displot to get a FacetGrid
g = sns.displot(
data=df_all,
x='a_number',
hue='a_condition',
row='a_factor',
col='another_factor',
stat="density",
common_norm=False,
element='step',
palette=palette,
)
Similar to this question, I want to place the automatically-generated legend on one of the axes, instead of outside them as a figure-level legend:
How to put the legend on first subplot of seaborn.FacetGrid?
Just calling g.fig.axes[row][col].legend() does not work:
No handles with labels found to put in legend.
So I should generate the handles and labels?
I looked at how Grid.add_legend does this and it seems to be some magic that would require me knowing a lot more about how the class works to reproduce it (maybe I am wrong). There's also no _legend_data I can use to dynamically recreate the legend in the same way that the Grid.add_legend method does.
>>> g._legend_data
{}
The "easy" (lazy?) way would be if I could somehow copy the legend instance, add that copy to the axes I want, and then call g.fig._legend.remove() (unless anyone has any better ideas)
I can't figure out how to copy the legend and then assign it to a specific Axes.
It dawned on me I could just call g.legend.set_bbox_to_anchor() as a workaround.
In this case the legend is still figure-level but it lets me get it where I want it.
edit:
#mwaskom replied with a link to this comment on a GitHub issue, that has a code snippet that does what I had in mind:
https://github.com/mwaskom/seaborn/issues/2280#issuecomment-692350136
for my purposes I tweaked it like so
def move_legend(g, ax, new_loc, **kws):
old_legend = g.legend
handles = old_legend.legendHandles
labels = [t.get_text() for t in old_legend.get_texts()]
title = old_legend.get_title().get_text()
ax.legend(handles, labels, loc=new_loc, title=title, **kws)
move_legend(g, g.fig.axes[-2], "upper right")
g.legend.remove()
relevant discussion of what's going on under the hood is on that issue
I'm trying to make an animation with matplotlib, in this case a 3D scatter plot. I'm hitting a problem that I absolutely always hit when I try to do this, which is that I don't know what arguments I should pass to set_data, and I don't know how to find out. In this case, it apparently expects two arguments, despite it being a 3d plot.
Since I've experienced related problems often, rather than asking about the specifics of the particular plot I'm trying to animate, I will ask the general question: given an element of a MatPlotLib plot, how can I determine what arguments its set_data method expects, either by interrogating it, or by knowing where it's documented?
From an example for an Animated 3D random walk from the MatPlotLib documentation:
def update_lines(num, dataLines, lines):
for line, data in zip(lines, dataLines):
# NOTE: there is no .set_data() for 3 dim data...
line.set_data(data[0:2, :num])
line.set_3d_properties(data[2, :num])
return lines
So as confusing as you discovered it is set_data by itself is not meant for 3D data, as well as according to the docs it accepts:
2D array (rows are x, y) or two 1D arrays
Looking more at this example we can see that the set_3d_properties has been used altogether.
This whole update_lines was set as a callback parameter for animation.FuncAnimation.
Usually to find the documentation you can either search it up online (e.g doc for set_data) or from a python prompt you can use the help function, which will show you the docstring of the object (can be used on a module/function/class etc) if it has any.
For example if you want to know what the datetime.datetime.now does (I dont have mathplotlib install to use it on it):
>>> import datetime
>>> help(datetime.datetime.now)
Help on built-in function now:
now(tz=None) method of builtins.type instance
Returns new datetime object representing current time local to tz.
tz
Timezone object.
If no tz is specified, uses local timezone.
I'm going through a data visualisation in python course to help with my lab reports and I cant seem to understand the purpose of the second line in this example of creating a scatter plot:
import matplotlib.pyplot as plt
fig,ax = plt.subplots()
ax.scatter([1,2,3,4,5],[1,2,3,4,5])
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
Can someone explain what that first line is doing here.
I have only started using python and I haven't seen this sort of syntax before(fig,ax = plt.subplots()). I tried to test if it was a way to assign 2 variables to the same thing by writing x,y=1, I ended up getting an error "int object is not iterable".
Another thing I dont understand is where is fig being used anywhere in the body of code? My current understanding is that the top line defined what fig and ax are, I can see that ax is used in the body of the code to define the scatter plot, but where is fig used? I tried to delete it and run the code, but I got this error:
'tuple' object has no attribute 'scatter'
If someone could please explain the above misconceptions.
As per the official docs, subplots creates a figure and a set of subplots. Specifically,
Returns:
fig : Figure
ax : axes.Axes object or array of Axes objects. ax can be either a single Axes object or an array of Axes objects if more than one subplot was created. The dimensions of the resulting array can be controlled with the squeeze keyword, see above.
Now when you do
fig, ax = plt.subplots()
the figure object is assigned to the variable fig and the axis object is assigned to the variable ax.
The fig will then give you access to the attributes on a figure-level, for instance, the figure title. The ax will give you access to the attributes on individual subplot level, such as the legends, axis-labels, ticks, of each individual subplot. It will be as array of Axes objects in case you have more than one subplot.
I tried to test if it was a way to assign 2 variables to the same thing by writing x,y=1, I ended up getting an error "int object is not iterable".
You are almost right. That is syntax to assign multiple variables at the same time, but what you are missing is that plt.subplots() returns a tuple - of two values paired together.
If you want to better understand it you can run:
a, b = (1, 4)
or
a,b = 1, 4
(it's the same as far as python is concerns, it packs/unpacks values to a tuple if multiple values are used or returned)
I tried to delete it and run the code, but I got this error:
'tuple' object has no attribute 'scatter'
This is also related to why you got this error. The figure is indeed not in use in your code snippet, but you need it for python to understand you want to use part of the tuple and not the tuple itself.
For example: a=(1,2) will result in a holding a tuple, but in a, b = 1, 2 each of the created variables will hold an integer.
In your case, the axis object has a method scatter, which the tuple object does not have, hence your error.
So recently I was working with matplotlib here is my code
import matplotlib.pyplot as plt
plt.plot([1,2,3] ,[2,4,6], label="line")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Interesting graph")
plt.legend()
plt.show()
Now as I have to write this plt. every single time which I feel is redundant
So my question is there any way that only once I have to write plt and I can call all the methods on it by just writing the method name
Here is an example of what I wanted which I am writing using with in python
with plt :
plot([1,2,3] ,[2,4,6], label="line")
xlabel("x")
ylabel("y")
title("Interesting graph")
legend()
plt.show()
If something like this is not present in python can I suggest this in python idea mailing list
I agree with the comments above, but if for some reason you really need/want a way to do what you're describing you could use an OrderedDict to retain the order that you want to call each function in and use the values as the arguments then use the getattr to dynamically make the function call.
It gets a little messy when you have keyword arguments but here's a hacky answer if you really need it. Hopefully it helps!
import matplotlib.pyplot as plt
from collections import OrderedDict
def call_these_funcs_with_args(mod,ordered_funcs_and_args):
"""
calls the functions provided as keys with the values provided as arguments
for the module provided
"""
for attr,args in attrs_and_args.iteritems():
if attr in dir(mod):
args = attrs_and_args[attr]
if isinstance(args,list):
getattr(mod,attr)(*args)
elif args:
getattr(mod,attr)(args)
else:
getattr(mod,attr)()
if "{}_kwargs".format(attr) in attrs_and_args.keys() and isinstance(args,list):
kwargs = attrs_and_args["{}_kwargs".format(attr)]
getattr(mod,attr)(*args,**kwargs)
attrs_and_args = OrderedDict(
{'plot':[[1,2,3],[2,4,6]],
'plot_kwargs': {'label':'line'},
'xlabel':"x",
'ylabel':"y",
'title':"Interesting Graph",
'legend':None}
)
call_these_funcs_with_args(plt,attrs_and_args)
plt.show()
this would decrease readability - so to understand one particular line of code you would need to scroll up ever time
name collisions would occur more often
code would become harder to reactor
alternative way is using method chaining (or "return-self") that does not have such disadvantages. but this approach works with methods calls only.
I am just trying to set the axis limits with pyplot, but it seems like it is not detecting the argument of the axis function correctly, as it returns this error:
TypeError: 'list' object is not callable.
It is related to the axis function, but I can not see where the problem is. The part of my code which is giving the problem is the following:
plt.figure(1)
plt.plot(x[:,0],x[:,1])
ax=[0.0,10.0,0.0,10.0]
plt.axis(ax)
If I use x = numpy.random.randn(10,2) then the remainder of this code works with no issue, using NumPy 1.4, matplotlib 1.1.1, and Python 2.7.3. The axis function in the pyplot API accepts a list of 4 values as the first positional argument, so the usage here is correct. The error you are experiencing does not come from this piece of code.
If you provide the full details of the data you are trying to plot and the full details of the code and traceback message, perhaps we can help you.
# Works fine assuming packages are installed...
import numpy as np
import matplotlib.pyplot as plt
x = np.random.randn(10,2)
plt.figure(1)
plt.plot(x[:,0], x[:,1])
ax = [0.0, 10.0, 0.0, 10.0]
plt.axis(ax)
plt.show()
Given the error message you've posted, one possibility is that you have taken the instance of a list object (the thing called ax in your code) that was intended to serve as the boundary of an axis and confused it with the API function axis. In that case, look in your code for a place where you put the function call syntax after the variable name of ax, such as:
ax(x1, x2, y1, y2)
instead of
plt.axis(x1, x2, y1, y2)
In the first case, trying to "call" ax with some arguments will not succeed, since the Python list class does not implement the __call__ protocol, so that suffixing the function call symbols, () (potentially with arguments) after the variable name of a variable that is an instance of list will cause the error you are seeing.
Other things may cause it too, but this is my best guess given the limited info available in the question.