How to create a bar plot with a logarithmic x-axis and gaps between the bars?

I want to plot a beautiful bar plot using the data mentioned in the script. Additionally, the x-axis should be logarithmic and there must be gaps between the bars.

I tried the script as below:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

fig = plt.figure()

x = [0.000001,0.00001,0.0001,0.001,0.01,0.1,1.0]
height = [5.3,1.8,8.24,5.8,2.8,3.3,4.2]
width = 0.000001

plt.bar(x, height, width, color='b' )
plt.xscale("log")  
plt.savefig('SimpleBar.png')
plt.show()

However, the x-axis values are not plotted as expected.

resulting plot

Answer

With a log scale x-axis, you can’t set constant widths for the bars. E.g. the first bar would go between 0 and 0.000002, (0 is at minus infinity on a log scale).

You could use the x-positions for the left edge of the bars, and the next x-position for the right edge:

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
x = [0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0]
height = [5.3, 1.8, 8.24, 5.8, 2.8, 3.3, 4.2]

plt.xscale("log")
widths = np.diff(x + [x[-1] * 10])
plt.bar(x, height, widths, align='edge', facecolor='dodgerblue', edgecolor='white', lw=2)
plt.show()

bar plot on a log-scale x-axis

If you want to “center” the bars around the original x-values, you need to calculate the start and end positions of each bar in log space. The easiest way to get more spacing between the bars, is to set a thicker white border.

import matplotlib.pyplot as plt
import numpy as np

x = [0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0]
height = [5.3, 1.8, 8.24, 5.8, 2.8, 3.3, 4.2]

plt.xscale("log")
padded_x = [x[0] / 10] + x + [x[-1] * 10]
centers = [np.sqrt(x0 * x1) for x0, x1 in zip(padded_x[:-1], padded_x[1:])]
widths = np.diff(centers)
plt.bar(centers[:-1], height, widths, align='edge', facecolor='dodgerblue', edgecolor='white', lw=4)
plt.margins(x=0.01)
plt.show()

log scale x for bar plot, centered

You can also have a configurable width if you calculate the new left and right positions for each bar:

import matplotlib.pyplot as plt
import numpy as np

x = [0.000001, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1.0]
height = [5.3, 1.8, 8.24, 5.8, 2.8, 3.3, 4.2]

plt.xscale("log")
padded_x = [x[0] / 10] + x + [x[-1] * 10]
width = 0.3  # 1 for full width, closer to 0 for thinner bars
lefts = [x1 ** (1 - width / 2) * x0 ** (width / 2) for x0, x1 in zip(padded_x[:-2], padded_x[1:-1])]
rights = [x0 ** (1 - width / 2) * x1 ** (width / 2) for x0, x1 in zip(padded_x[1:-1], padded_x[2:])]
widths = [r - l for l, r in zip(lefts, rights)]
plt.bar(lefts, height, widths, align='edge', facecolor='dodgerblue', lw=0)
plt.show()

adaptable width