I want to make a bar plot that has number line type spacing.
So if we have data like this:
d = {'Avg_Price': [22.1, 19.98, 24.4, 24.4, 12.0, 41.98, 12.0, 35.0, 25.84, 25.0, 60.0],
'estimated_purchasers': [2796.9999999999995, 1000.0, 672.98, 672.98, 335.0, 299.0, 500.0, 104.22, 42.96, 500.0, 225.0]}
revenues = pd.DataFrame(data=d)
This is just a basic bar plot:
ax = sns.barplot(x='Avg_Price',
y='estimated_purchasers',
data=revenues)
I want it to be spaced like a number line (so let's equally spaced from 0 to 60) - something more like this:

I am likely fully overthinking this, but how can I do this??
The big problem you're bumping into is that a seaborn automatically casts a barplot to have an x-axis that is categorical. So instead of true numeric positions, seaborn resamples your x-axis to be in the range of 0 - (number of unique x-values), and then labels them with the string representation of that category. To achieve the plot you want, you can either
import seaborn as sns
import matplotlib.pyplot as plt
sns.set() # invoke seaborn styling
# manually making axes to make a wider plot for viewing
fig, ax = plt.subplots(figsize=(12, 4))
ax = sns.barplot(x='Avg_Price',
y='estimated_purchasers',
data=revenues)
# Get all unique x-values in ascending order
x_values = sorted(revenues["Avg_Price"].unique())
# New xlim spans from -1 to [max(x_values) + 1]
ax.set_xlim(-1, x_values[-1] + 1)
# a barplot w/ error bars are rectangles (patches) & lines
# so we fetch all artists related to these to update their position on the Axes
artists = zip(ax.patches, ax.lines)
for x_val, (rect, err_line) in zip(x_values, artists):
# ensure everything is centered on the x_val
new_rect_x = x_val - (rect.get_width() / 2)
rect.set_x(new_rect_x)
err_line.set_xdata([x_val, x_val])
# Take care to update the x-axis itself
new_xticks = [0, 30, 60]
ax.set_xticks(new_xticks)
ax.set_xticklabels(new_xticks)

My preferred solution will be to skip seaborn all together in this case
import seaborn as sns
import matplotlib.pyplot as plt
sns.set() # invoke seaborn styling
# Perform data aggregation explicitly instead of relying on seaborn
agg_rev = (
revenues.groupby("Avg_Price")["estimated_purchasers"]
.agg(["mean", "sem"])
.reset_index()
)
agg_rev["sem"] = agg_rev["sem"].fillna(0)
# Now we can plot :)
fig, ax = plt.subplots(figsize=(12, 4))
ax.bar(x="Avg_Price", height="mean", data=agg_rev)
ax.errorbar(x="Avg_Price", y="mean", yerr="sem", data=agg_rev, fmt="none", ecolor="black")
ax.set_xticks([0, 30, 60])
ax.set_xlabel("Avg Price")
ax.set_ylabel("estimated_purchases")
ax.grid(False, axis="x") # turn off vertical gird lines b/c they look silly

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With