Generally when plotting with pyplot we have the current (x,y) values at the bottom right:
But, what happens if you relabel? Code:
import matplotlib.pyplot as plt
plt.ion()
plt.draw()
plt.plot([(i,i) for i in range(10)])
plt.gca().set_xticklabels((1,2,3,4,5,6,7,8,9,10))
plt.draw()
You get:
As you see the x value is ruined, that is, is no longer displayed. This may make sense for non- numeric labeling, and may be difficult to handle, but this actually happened to me while working with a twin axis, which I need to be a transformation of the first. If I relabel the twin the coordinates do not appear as well, even though the main axis is untouched. Is there a way to retrieve coordinate display capability?. If someone feels a code to produce and alter a twin axis is necessary let me know.
When set_xticklabels is used, it will overwrite the formatter used. So it has to be set again:
plt.gca().xaxis.set_major_formatter(FormatStrFormatter('%.5f'))
If you want to display the values transformed somehow, then you have to use FuncFormatter, which accepts a function of tick and position, and returns the label. Read about it here. To combine it with nice scalar formatting (in the link as well) you can use something like:
plt.gca().xaxis.set_major_formatter(FuncFormatter(
lambda t,p,f=yourfunc: ScalarFormatter().format_data_short(f(t))))
You probably want to initialize the ScalarFormatter outside.
Related
I'm trying to determine what the limits of data points are on a matplotlib Axes or Figure, but I can't find any way.
I'm gonna give an example, but the original image is much more complex:
By using Axes.get_xbound() or Axes.get_xlim() I get (-265.6, 6000.0) but I would want to get (0,5570).
I'm asking this because on this part of code I only have access to the Figure or Axes object.
Something like this:
def plot_detail():
fig, ax = plt.subplots(1)
# Code
# ...
return fig,ax
def main():
fig,ax = plot_detail()
print(ax.get_xbound())
print(ax.get_xlim())
# Here I would need the data limits
# Any Idea how?
First, just as a side note, from the fact that you want the data at point in the code where you only have the plot (Figure and Axes), it seems to me that there was at least one not-so-great design decision made while designing/writing your code. If I could see the whole code I could likely recommend a better approach. That said, it is understandable that sometimes we don't anticipate all the needs of our code, and then sometimes (depending on the size of the program) it may not be worth the effort for a redesign/rewrite of part of the code.
So to get the data (in order to know the x-limits of the data itself, and not just of the plot) ... You can do this by getting the lines.Line2D objects from the Axes object.
Even though it appears you are plotting a bar graph, there should still be a line2D object in there. That object contains the xy data.
xdata = ax.get_lines()[0].get_xdata()
print('xdata limits:',xdata[0],xdata[-1])
HTH.
I use matplotlib for my plots, I find it great, but sometimes too much complicated. Here an example:
import matplotlib.pyplot as plt
import numpy as np
idx1 = -3
idx2 = 3
x = np.arange(-3, 3, 0.01)
y = np.sin(np.pi*x*7)/(np.pi*x*7)
major_ticks = np.arange(idx1, idx2, 1)
minor_ticks = np.arange(idx1, idx2, 0.1)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_ylim(-0.3, 1.2)
ax.set_xlim(idx1, idx2)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor = True)
ax.grid(True, which = 'both')
ax.tick_params(axis = 'x', labelsize = 18)
ax.tick_params(axis = 'y', labelsize = 18)
ax.plot(x, y)
plt.show()
Is there anything implemented on matplotlib and/or seaborn in which I can provide all these plot settings just as argument of a function only? It may considerably reduce the number of code lines and make the script easier both to write and understand.
Matplotlib provides an object oriented API. This means that all the elements of the figure are acutally objects for which one can get and set properties and which can be easily manipulated. This makes matplotlib really flexible such that it can produce almost any plot you'd imagine.
Since a plot may consist of a hundred or more elements, a function that would allow the same flexibility would need that amount of possible arguments. It is not necessarily easier to remember all possible arguments of a function than all possible attributes of a class.
Having a single function call that does all of this, does not necessarily mean that you have to type in less characters. The commands would just be ordered differently.
Furthermore the object oriented approach allows to keep things seperate. Some properties of the axes, like the grid or the axis labels are completely independend on what you plot to the axes. So you wouldn't want to set the xticks in the call to plot, because they are simply not related and it may be very confusing to set twice the same ticklabels when plotting two lines in the same axes.
On the other hand, matplotlib is really easy. In order to produce a plot you need two lines
import matplotlib.pyplot as plt
plt.plot([1,2,3],[2,1,3])
which sets most of the parameters exactly as they are needed. The more you want to customize this plot, the more settings you have to apply. Which is fine as it allows the user himself to determine how much in depth he wants to control the appearance of the plot.
Most matplotlib codes can be separated into three parts.
Setting the style
Creating the plot
Customizing the plot
Setting the style in the case of the code from the question involves e.g. the ticklabel size and the use of a grid. Those properties can set as it's done in the code but it may indeed be that one always wants to use the same properities here and finds it annoying to type the same parameters in every time one creates a plot. Therefore matplotlib provides general style settings, called rcParams. They can be set at the beginning of a script, e.g.
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['axes.grid '] = True
plt.rcParams['axes.labelsize'] = 18
and will be applied to all plots within the script. It is also possible to define a complete stylesheet using those parameters. For more information see the Customizing matplotlib article.
It is equally possible to use predefined stylesheets for certain applications.
Simply importing import seaborn is also a possible way to change the style.
Creating the plot can not be simplified much more. It's clear that one needs as many plotting commands as items to plot. Creating the figure and axes like
fig, ax = plt.subplots()
saves one line though.
Equally no simplification is possible if customizing ticks or tickmarks are required. One may however consider to use Tickers and Formatters for this purpose.
At the end one may of course consider to write a custom function which performs much of those tasks, but everyone can decide if that is useful for himself.
Browsing around I saw this wabe page.
This line of code can summarise many settings
import matplotlib as mpl
mpl.rc('lines', linewidth=2, color='r')
ax.set is very useful for this:
ax.set(xlim=(idx1, idx2), ylim=(-0.3, 1.2),
xticks=major_ticks, ...)
You can only set simple single-argument properties (e.g. those which don't need further keywords), but it's a nice timesaver.
Using Pandas to plot in I-Python Notebook, I have several plots and because Matplotlib decides the Y axis it is setting them differently and we need to compare that data using the same range.
I have tried several variants on: (I assume I'll need to apply the limits to each plot.. but since I can't get one working... From the Matplotlib doc it seems that I need to set ylim, but can't figure the syntax to do so.
df2250.plot(); plt.ylim((100000,500000)) <<<< if I insert the ; I get int not callable and if I leave it out I get invalid syntax. anyhow, neither is right...
df2260.plot()
df5.plot()
I'm guessing this was a feature added after this answer was accepted in 2013; DataFrame.plot() now exposes a ylim parameter that sets the y axis limits:
df.plot(ylim=(0,200))
See pandas documentation for details.
Pandas plot() returns the axes, you can use it to set the ylim on it.
ax1 = df2250.plot()
ax2 = df2260.plot()
ax3 = df5.plot()
ax1.set_ylim(100000,500000)
ax2.set_ylim(100000,500000)
etc...
You can also pass an axes to Pandas plot, so plotting it in the same axes can be done like:
ax1 = df2250.plot()
df2260.plot(ax=ax1)
etc...
If you want a lot of different plots, defining the axes beforehand and within one figure might be the solution that gives you the most control:
fig, axs = plt.subplots(1,3,figsize=(10,4), subplot_kw={'ylim': (100000,500000)})
df2260.plot(ax=axs[0])
df2260.plot(ax=axs[1])
etc...
Whenever I plot something in matplotlib, moving the mouse over the plot shows at the bottom the x and y position of the cursor. This is very convenient when exploring data.
Now, If I set the ticklabels to something else, the x position of the cursor at the bottom of the plot is empty, while the gui keeps track of the y position. Is there a way to keep getting the x position?
This is a simple example of this happening:
import numpy as np
fig, ax = plt.subplots()
x=np.linspace(0,np.pi)
y=np.sin(x)
ax.plot(x,y)
ax.set_xticks([0,np.pi/2,np.pi])
ax.set_xticklabels(['0','pi/2','pi'])
plt.show()
No, there is no easy way to do this.
When you set the labels with a list of strings you set the xaxis.major_formatter to be FixedFormatter (doc) which has some peculiar behavior to make it work (it always returns '', unless you give it a third argument which given when it labels the x-axis, but not otherwise). The marking of where the cursor is is generated using the major_formatter and when you call a FixedFormatter it returns '', which is what is displayed.
If you really want this, you have to write your own call back, see Color values in imshow for matplotlib? for a starting point. (or subclass axes and re-implement format_coord (around line 2903 in axes.py in current matplotlib master branch)
Is there a way to whiten out the background of the axis label so that when it crosses the axis line itself, the latter does not run through it?
For example, this script (the best I managed so far)
#!/usr/bin/python
import matplotlib.pyplot as plt
xx=[1,2,3]
yy=[2,3,4]
dy=[0.1,0.2,0.05]
fig=plt.figure()
ax=fig.add_subplot(111)
ax.errorbar(xx,yy,dy,fmt='ro-',ms=6,elinewidth=4)
ax.set_xlim([0.,3.4])
ax.set_ylim([0.,4.4])
ax.set_xlabel(r'$T/t$',fontsize=16)
ax.set_ylabel(r'$S(\mathbf{Q})L^{1+\eta}$',fontsize=16)
# position the axis labels
ax.xaxis.set_label_coords(1,0)
ax.yaxis.set_label_coords(0.1,0.93)
ax.yaxis.get_label().set_rotation('horizontal')
ax.yaxis.get_label().set_backgroundcolor('w')
#ax.yaxis.get_label().set_zorder(222) #doesn't do the trick
plt.show()
produces almost what I'm looking for, but still the y-axis runs over the label: .
By default, the left spine has a zorder of 2.5. For some reason this seems to cause problems; maybe there's something in the code which only works if they're integral? Anyway, if you add
ax.spines['left'].set_zorder(2)
or more generally
ax.spines['left'].set_zorder(ax.yaxis.get_label().get_zorder()-1)
before the show, it should work. Also, set_ylabel returns the ylab object itself, so if you use "ylab = ax.set_ylabel(stuff)" you can avoid all the ax.yaxis.get_label() calls later.
Does this link help you?
http://matplotlib.sourceforge.net/faq/howto_faq.html#automatically-make-room-for-tick-labels
You can simply shift the y-axis to the right to allows some space for the $S(\mathbf{Q})L^{1+\eta}$ mark be fully placed before the axis line.