可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
With matplotlib
when a log scale is specified for an axis, the default method of labeling that axis is with numbers that are 10 to a power eg. 10^6. Is there an easy way to change all of these labels to be their full numerical representation? eg. 1, 10, 100, etc.
Note that I do not know what the range of powers will be and want to support an arbitrary range (negatives included).
回答1:
Sure, just change the formatter.
For example, if we have this plot:
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.axis([1, 10000, 1, 100000]) ax.loglog() plt.show()
You could set the tick labels manually, but then the tick locations and labels would be fixed when you zoom/pan/etc. Therefore, it's best to change the formatter:
from matplotlib.ticker import ScalarFormatter for axis in [ax.xaxis, ax.yaxis]: axis.set_major_formatter(ScalarFormatter())
回答2:
I've found that using ScalarFormatter
is great if all your tick values are greater than or equal to 1. However, if you have a tick at a number <1
, the ScalarFormatter
prints the tick label as 0
.

I've used this lambda
function with FuncFormatter
to set numbers >= 1
to their integer value, and numbers <1
to their decimal value, with the minimum number of decimal places required (i.e. 0.1, 0.01, 0.001
, etc). It assumes that you are only setting ticks on the base10
values.
import matplotlib.ticker as ticker import numpy as np ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y,pos: ('{{:.{:1d}f}}'.format(int(np.maximum(-np.log10(y),0)))).format(y)))

For clarity, here's that lambda function written out in a more verbose, but also more understandable, way:
def myLogFormat(y,pos): # Find the number of decimal places required decimalplaces = int(np.maximum(-np.log10(y),0)) # =0 for numbers >=1 # Insert that number into a format string formatstring = '{{:.{:1d}f}}'.format(decimalplaces) # Return the formatted tick label return formatstring.format(y) ax.yaxis.set_major_formatter(ticker.FuncFormatter(myLogFormat))
回答3:
regarding these questions
What if I wanted to change the numbers to, 1, 5, 10, 20?
you can solve those issue like this with MINOR formatter:
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter()) ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f")) ax.set_yticks([0.00000025, 0.00000015, 0.00000035])
in my application I'm using this format scheme, which I think solves most issues related to log scalar formatting; the same could be done for data > 1.0 or x axis formatting:
plt.ylabel('LOGARITHMIC PRICE SCALE') plt.yscale('log') ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter("%.8f")) ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter()) ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f")) ##################################################### #force 'autoscale' ##################################################### yd = [] #matrix of y values from all lines on plot for n in range(len(plt.gca().get_lines())): line = plt.gca().get_lines()[n] yd.append((line.get_ydata()).tolist()) yd = [item for sublist in yd for item in sublist] ymin, ymax = np.min(yd), np.max(yd) ax.set_ylim([0.9*ymin, 1.1*ymax]) ##################################################### z = [] for i in [0.0000001, 0.00000015, 0.00000025, 0.00000035, 0.000001, 0.0000015, 0.0000025, 0.0000035, 0.00001, 0.000015, 0.000025, 0.000035, 0.0001, 0.00015, 0.00025, 0.00035, 0.001, 0.0015, 0.0025, 0.0035, 0.01, 0.015, 0.025, 0.035, 0.1, 0.15, 0.25, 0.35]: if ymin<i<ymax: z.append(i) ax.set_yticks(z)
for comments on "force autoscale" see: Python matplotlib logarithmic autoscale
which yields:

then to create a general use machine:
# user controls ##################################################### sub_ticks = [10,11,12,14,16,18,22,25,35,45] # fill these midpoints sub_range = [-8,8] # from 100000000 to 0.000000001 format = "%.8f" # standard float string formatting # set scalar and string format floats ##################################################### ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter()) ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter(format)) ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter()) ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter(format)) #force 'autoscale' ##################################################### yd = [] #matrix of y values from all lines on plot for n in range(len(plt.gca().get_lines())): line = plt.gca().get_lines()[n] yd.append((line.get_ydata()).tolist()) yd = [item for sublist in yd for item in sublist] ymin, ymax = np.min(yd), np.max(yd) ax.set_ylim([0.9*ymin, 1.1*ymax]) # add sub minor ticks ##################################################### set_sub_formatter=[] for i in sub_ticks: for j in range(sub_range[0],sub_range[1]): set_sub_formatter.append(i*10**j) k = [] for l in set_sub_formatter: if ymin<l<ymax: k.append(l) ax.set_yticks(k) #####################################################
yields:

回答4:
I found Joe's and Tom's answers very helpful, but there are a lot of useful details in the comments on those answers. Here's a summary of the two scenarios:
Ranges above 1
Here's the example code like Joe's, but with a higher range:
import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.axis([1, 10000, 1, 1000000]) ax.loglog() plt.show()
That shows a plot like this, using scientific notation: 
As in Joe's answer, I use a ScalarFormatter
, but I also call set_scientific(False)
. That's necessary when the scale goes up to 1000000 or above.
import matplotlib.pyplot as plt from matplotlib.ticker import ScalarFormatter fig, ax = plt.subplots() ax.axis([1, 10000, 1, 1000000]) ax.loglog() for axis in [ax.xaxis, ax.yaxis]: formatter = ScalarFormatter() formatter.set_scientific(False) axis.set_major_formatter(formatter) plt.show()

Ranges below 1
As in Tom's answer, here's what happens when the range goes below 1:
import matplotlib.pyplot as plt from matplotlib.ticker import ScalarFormatter fig, ax = plt.subplots() ax.axis([0.01, 10000, 1, 1000000]) ax.loglog() for axis in [ax.xaxis, ax.yaxis]: formatter = ScalarFormatter() formatter.set_scientific(False) axis.set_major_formatter(formatter) plt.show()
That displays the first two ticks on the x axis as zeroes.

Switching to a FuncFormatter
handles that. Again, I had problems with numbers 1000000 or higher, but adding a precision to the format string solved it.
import matplotlib.pyplot as plt from matplotlib.ticker import FuncFormatter fig, ax = plt.subplots() ax.axis([0.01, 10000, 1, 1000000]) ax.loglog() for axis in [ax.xaxis, ax.yaxis]: formatter = FuncFormatter(lambda y, _: '{:.16g}'.format(y)) axis.set_major_formatter(formatter) plt.show()
