I'm trying to get the text in the offset of the scientific notation of matplotlib, but get_offset() or get_offset_text() returns an empty string. I have checked these questions, but they didn't work:
How to move the y axis scale factor to the position next to the y axis label?
Adjust exponent text after setting scientific limits on matplotlib axis
prevent scientific notation in matplotlib.pyplot
Here is a simple example:
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
import numpy as np
x = np.arange(1,20)
y = np.exp(x)
fig,ax = plt.subplots(1,1)
ax.plot(x,y)
ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True))
print(ax.yaxis.get_offset_text())
print(ax.yaxis.get_major_formatter().get_offset())
fmt = ax.yaxis.get_major_formatter()
offset = ax.yaxis.get_major_formatter().get_offset()
print(offset)
plt.show()
That generates:
I'd like to get the x10^8, but it returns only:
Text(0, 0.5, '')
The same happens if I don't use the ScalarFormatter. Am I missing something? Is there a separate function to get the multiplier (instead of the offset)?
edit: I'm currently using Python 3.9.0 with matplotlib 3.4.2 on a MacBook Pro, and I just run python3 test.py.
edit2: I have installed Python 3.9.5, and the solution with fig.canvas.draw() still does not work. The same with Linux works.
edit3: The problem happens when using the MacOSX backend. When changing to TkAgg or Agg, the get_offset works with the provided solution.
You first need to draw the figure for the object to not hold some default values. From the source code on FigureCanvasBase.draw:
"""
Render the `.Figure`.
It is important that this method actually walk the artist tree
even if not output is produced because this will trigger
deferred work (like computing limits auto-limits and tick
values) that users may want access to before saving to disk.
"""
Simply call fig.canvas.draw() and then ax.yaxis.get_offset_text() will have the updated values you want.
x = np.arange(1, 20)
y = np.exp(x)
fig, ax = plt.subplots(1, 1)
ax.plot(x, y)
ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True))
fig.canvas.draw()
offset = ax.yaxis.get_major_formatter().get_offset()
print(offset)
# $\times\mathdefault{10^{8}}\mathdefault{}$
Related
I have the following code for generating a time series plot
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
series = pd.Series([np.sin(ii*np.pi) for ii in range(30)],
index=pd.date_range(start='2019-01-01', end='2019-12-31',
periods=30))
series.plot(ax=ax)
I want to set an automatic limit for x and y, I tried using ax.margins() but it does not seem to work:
ax.margins(y=0.1, x=0.05)
# even with
# ax.margins(y=0.1, x=5)
What I am looking for is an automatic method like padding=0.1 (10% of whitespace around the graph)
Pandas and matplotlib seem to be confused rather often while collaborating when axes have dates. For some reason in this case ax.margins doesn't work as expected with the x-axis.
Here is a workaround which does seem to do the job, explicitely moving the xlims:
xmargins = 0.05
ymargins = 0.1
ax.margins(y=ymargins)
x0, x1 = plt.xlim()
plt.xlim(x0-xmargins*(x1-x0), x1+xmargins*(x1-x0))
Alternatively, you could work directly with matplotlib's plot, which does work as expected applying the margins to the date axis.
ax.plot(series.index, series)
ax.margins(y=0.1, x=0.05)
PS: This post talks about setting use_sticky_edges to False and calling autoscale_view after setting the margins, but also that doesn't seem to work here.
ax.use_sticky_edges = False
ax.autoscale_view(scaley=True, scalex=True)
You can use ax.set_xlim and ax.set_ylim to set the x and y limits of your plot respectively.
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
series = pd.Series([np.sin(ii*np.pi) for ii in range(30)],
index=pd.date_range(start='2019-01-01', end='2019-12-31',
periods=30))
# set xlim to be a between certain dates
ax.set_xlim((pd.to_datetime('2019-01-01'), pd.to_datetime('2019-01-31'))
# set ylim to be between certain values
ax.set_ylim((-0.5, 0.5))
series.plot(ax=ax)
I'm trying to add a second x-axis label to the top of a plot. The normal axis is in log scale and displays as 10^-1, 10^0, etc, as it should, but I also want ticks at each 10^x.5 along the top (.1, .32, 1, 3.2, etc). When I try to do this with twiny, they align completely incorrectly and in a way that I can't even understand the reason for. Here is my code (and the resulting plot):
from pylab import *
import numpy as np
dfile = "data.txt" #DATA STUFF YOU DON'T NEED
data = np.loadtxt(dfile,dtype=float)
asep = data[:,1]
par= data[:,2]
dist = 1000/par
dsep = asep*dist
ldsep = np.log10(dsep)
#RELEVANT BITS
ax1=subplot(211)
ax1.set_xlim([0,100])
plt.gca().set_xscale("log")
plt.hist(allsep,bins=[.1,.32,1,3.2,10,32,100],facecolor='red')
plt.ylabel('$N_{stars}$')
ax2 = ax1.twiny()
ax2.set_xscale("log")
newpos=[.1,.32,1,3.2,10,32,100]
newlabel=[0.1,0.32,1.0,3.2,10,32,100]
ax2.set_xticks(newpos)
ax2.set_xticklabels(newlabel)
ax2.xaxis.set_ticks_position('top')
ax2.xaxis.set_label_position('top')
ax2.set_xlim(ax1.get_xlim())
#SECOND PLOT, NOT REALLY NECESSARY
ax3=subplot(212)
ax3.set_xlim([0,100])
plt.hist(allsep,bins=[0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80],facecolor='red')
plt.xlabel('Projected Separation (AU)')
plt.ylabel('$N_{stars}$')
plt.savefig('dhist.png',dpi=300)
plt.show()
Thanks all!
When I try to run your code, the first error message is UserWarning: Attempted to set non-positive xlimits for log-scale axis;...
So - what versions of python and matplotlib do you use? Perhaps you should consider an update.
But still, before that you could simply first test what happens if you change the setting of x-axis limits to [ 0.1,... ]:
ax1.set_xlim([0.1,100])
I have to plot several curves with very high xtick density, say 1000 date strings. To prevent these tick labels overlapping each other I manually set them to be 60 dates apart. Code below:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
ts_index = pd.period_range(start="20060429", periods=1000).strftime("%Y%m%d")
fig = plt.figure(1)
ax = plt.subplot(1, 1, 1)
tick_spacing = 60
for i in range(5):
plt.plot(ts_index, 1 + i * 0.01 * np.arange(0, 1000), label="group %d"%i)
plt.legend(loc='best')
plt.title(r'net value curves')
xticks = ax.get_xticks()
xlabels = ax.get_xticklabels()
ax.set_xticks(xticks[::tick_spacing])
ax.set_xticklabels(xlabels[::tick_spacing])
plt.xticks(rotation="vertical")
plt.xlabel(r'date')
plt.ylabel('net value')
plt.grid(True)
plt.show()
fig.savefig(r".\net_value_curves.png", )
fig.clf()
I'm running this piece of code in PyCharm Community Edition 2017.2.2 with a Python 3.6 kernel. Now comes the funny thing: whenever I ran the code in the normal "run" mode (i.e. just hit the execution button and let the code run "freely" till interruption or termination), then the figure I got would always miss xticklabels:
However, if I ran the code in "debug" mode and ran it step by step then I would get an expected figure with complete xticklabels:
This is really weird. Anyway, I just hope to find a way that can ensure me getting the desired output (the second figure) in the normal "run" mode. How can I modify my current code to achieve this?
Thanks in advance!
Your x axis data are strings. Hence you will get one tick per data point. This is probably not what you want. Instead use the dates to plot. Because you are using pandas, this is easily converted,
dates = pd.to_datetime(ts_index, format="%Y%m%d")
You may then get rid of your manual xtick locating and formatting, because matplotlib will automatically choose some nice tick locations for you.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
ts_index = pd.period_range(start="20060429", periods=1000).strftime("%Y%m%d")
dates = pd.to_datetime(ts_index, format="%Y%m%d")
fig, ax = plt.subplots()
for i in range(5):
plt.plot(dates, 1 + i * 0.01 * np.arange(0, 1000), label="group %d"%i)
plt.legend(loc='best')
plt.title(r'net value curves')
plt.xticks(rotation="vertical")
plt.xlabel(r'date')
plt.ylabel('net value')
plt.grid(True)
plt.show()
However in case you do want to have some manual control over the locations and formats you may use matplotlib.dates locators and formatters.
# tick every 3 months
plt.gca().xaxis.set_major_locator(mdates.MonthLocator((1,4,7,10)))
# format as "%Y%m%d"
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y%m%d"))
In general, the Axis object computes and places ticks using a Locator object. Locators and Formatters are meant to be easily replaceable, with appropriate methods of Axis. The default Locator does not seem to be doing the trick for you so you can replace it with anything you want using axes.xaxis.set_major_locator. This problem is not complicated enough to write your own, so I would suggest that MaxNLocator fits your needs fairly well. Your example seems to work well with nbins=16 (which is what you have in the picture, since there are 17 ticks.
You need to add an import:
from matplotlib.ticker import MaxNLocator
You need to replace the block
xticks = ax.get_xticks()
xlabels = ax.get_xticklabels()
ax.set_xticks(xticks[::tick_spacing])
ax.set_xticklabels(xlabels[::tick_spacing])
with
ax.xaxis.set_major_locator(MaxNLocator(nbins=16))
or just
ax.xaxis.set_major_locator(MaxNLocator(16))
You may want to play around with the other arguments (all of which have to be keywords, except nbins). Pay especial attention to integer.
Note that for the Locator and Formatter APIs we work with an Axis object, not Axes. Axes is the whole plot, while Axis is the thing with the spines on it. Axes usually contains two Axis objects and all the other stuff in your plot.
You can set the visibility of the xticks-labels to False
for label in plt.gca().xaxis.get_ticklabels()[::N]:
label.set_visible(False)
This will set every Nth label invisible.
I work on a plot in python using the matplot library. The numbers which I have to generate are very big, so also the ticks on the axes are a large numbers and take a lot of space. I was trying to present them as a powers (for example instead having a tick 100000000 I want to have 10^8). I used command: ax.ticklabel_format(style='sci', axis='x', scilimits=(0,4)) however this only created something like this
Is there any other solution to have ticks for the plot as: 1 x 10^4, 2 x 10^4, etc or write the value 1e4 as 10^4 at the end of the label's ticks?
You can use the matplotlib.ticker module, and set the ax.xaxis.set_major_formatter to a FuncFormatter.
For example:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
plt.rcParams['text.usetex'] = True
fig,ax = plt.subplots(1)
x = y = np.arange(0,1.1e4,1e3)
ax.plot(x,y)
def myticks(x,pos):
if x == 0: return "$0$"
exponent = int(np.log10(x))
coeff = x/10**exponent
return r"${:2.0f} \times 10^{{ {:2d} }}$".format(coeff,exponent)
ax.xaxis.set_major_formatter(ticker.FuncFormatter(myticks))
plt.show()
Note, this uses LaTeX formatting (text.usetex = True) to render exponents in the tick labels. Also note the double braces required to differentiate the LaTeX braces from the python format string braces.
There might be a better solution, but if you know the values of each xtick, you can also manually name them.
Here is an example:
http://matplotlib.org/examples/ticks_and_spines/ticklabels_demo_rotation.html
I just wanted to get started on using the matplotlib library for the first time.
So I type the following commands:
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
data = sp.genfromtxt("web_traffic.tsv", delimiter = "\t");
x = data[:, 0];
y = data[:, 1];
x = x[~sp.isnan(y)];
y = y[~sp.isnan(y)];
plt.scatter(x, y);
And I have received the following error :
<matplotlib.collections.PathCollection object at 0x246abd0>
I have no idea what is causing this, I have just installed the required packages, scipy, matplotlib and it returned to me that particular error. I tried to google it but with no results.
I am using openSuse as OS and python came by default. My main purpose is to start learning using the scykit learn package.
Can you give me any advice on how to get over this error?
It's not an error message. It's a string representation of an object.
If you ran the code above in an interactive shell, then what you see is a string representation of the value returned by the plt.scatter function.
To actually open the window, you usually need to call plt.show() at the end.
Or if you want it to be interactive, it is suggested to set interactive: True in your .matplotlibrc.
On an unrelated note, semicolons are not needed at the end of the line in Python.
As shown in the matplotlib example for plt.scatter():
"""
Simple demo of a scatter plot.
"""
import numpy as np
import matplotlib.pyplot as plt
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radiuses
plt.scatter(x, y, s=area, alpha=0.5)
plt.show()
As the previous answer stated, you will have to call plt.show() to actually render the plot.