问题
Let's consider the following example to plot some y
values for given x
values.
import matplotlib.pyplot as pyplot
if __name__ == "__main__":
x = [1000000000, 2000000000, 4000000000, 8000000000]
y = [0.342, 0.543, 0.874, 1.324]
pyplot.bar(x, y)
pyplot.savefig("test1.png", format="png")
pyplot.close()
pyplot.bar([str(int(x_value / 1000000000)) for x_value in x], y)
pyplot.savefig("test2.png", format="png")
pyplot.close()
My goal is to have equally-spaced x-ticks for all x
values (i.e., not linearly scaled). Moreover, I'd like to see the values in scientific notation (i.e., 1e9
).
test1.png
does not show anything meaningful at all.
test2.png
shows the values in the desired way, but does not have the scientific label on the bottom right (1e9
).
How can I either place 1e9
manually in the x-axis unit location or work around my issue?
回答1:
Your first plot doesn't show the bars as the default width of 0.8 makes them extremely thin compared to the data range on the x-axis. You need to make them much wider.
A logscale will make the positions equidistant. To show the ticks as desired, they can be set explicitly and the default ScalarFormatter
can replace the LogFormatter
that comes with plt.xscale('log')
. There still will be minor ticks at the default positions (e.g. 3,5,6,7 in logscale), which can be suppressed with a NullLocator
.
To obtain nicely centered bars, the default centering doesn't work well with the log scale. Exact positions and widths can be calculated in log space and then transformed again to the linear space. The example below takes the shortest x-distance and multiplies it with 0.8 for separation.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, NullLocator
import numpy as np
x = np.array([1000000000, 2000000000, 4000000000, 8000000000])
y = [0.342, 0.543, 0.874, 1.324]
log_x = np.log(x)
log_width = 0.8 * np.diff(np.log(x)).min()
lefts = np.exp(log_x - log_width / 2)
rights = np.exp(log_x + log_width / 2)
ax = plt.gca()
ax.bar(lefts, y, width=[xi * 0.6 for xi in x], align='edge')
ax.set_xscale('log')
ax.set_xticks(x)
ax.xaxis.set_major_formatter(ScalarFormatter())
ax.xaxis.set_minor_locator(NullLocator()) # optionally remove the minor ticks
plt.tight_layout()
plt.show()
来源:https://stackoverflow.com/questions/64302588/custom-scientific-axis-unit-for-a-bar-plot-with-log-scale-x-axis