Custom scientific axis unit for a bar plot with log scale x-axis

别等时光非礼了梦想. 提交于 2021-02-10 07:25:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!