aviss
aviss

Reputation: 2449

How to control scientific notation in matplotlib?

This is my data frame I'm trying to plot:

my_dic = {'stats': {'apr': 23083904, 'may': 16786816, 'june': 26197936}}
my_df = pd.DataFrame(my_dic)

This is how I plot it:

ax = my_df['stats'].plot(kind='bar',  legend=False)
ax.set_xlabel("Month", fontsize=12)
ax.set_ylabel("Stats", fontsize=12)
ax.ticklabel_format(useOffset=False) #AttributeError: This method only works with the ScalarFormatter.
plt.show()

The plot:

enter image description here

I'd like to control the scientific notation. I tried to suppress it by this line as was suggested in other questions plt.ticklabel_format(useOffset=False) but I get this error back -

AttributeError: This method only works with the ScalarFormatter

Ideally, I'd like to show my data in (mln).

Upvotes: 23

Views: 37330

Answers (3)

cottontail
cottontail

Reputation: 23391

If you're here wondering why you got the error,

AttributeError: This method only works with the ScalarFormatter

then you got the error because as the error says, ticklabel_format only works with ScalarFormatter (which formats tick values as numbers), but some tick labels of your plot are not numbers.

1. Check if the plot has minor ticks that need to be formatted

A common way this error might occur is when you want to format the ticklabels of a plot with two levels of ticks (major and minor) where the major ticks are not scalars. A common way this happens is when a bar plot is plotted with log scale. The easiest way to solve the error is to ditch ticklabel_format and use ScalarFormatter on both levels of ticks instead.

plt.bar([1,2], [1000, 2000], log=True)

plt.ticklabel_format(axis='y', style='plain')             # <---- error


from matplotlib.ticker import ScalarFormatter
plt.gca().yaxis.set_major_formatter(ScalarFormatter()) 
plt.gca().yaxis.set_minor_formatter(ScalarFormatter());   # <---- OK
2. Check if the plot has minor ticks that need to be formatted

Another way is when one axis tick labels are not scalars (as in the OP). In the OP, x-axis tick labels are not numbers so the error was raised (by default both axes are flagged). Specifying axis='y' in ticklabel_format solves the error.

ax = my_df['stats'].plot(kind='bar', legend=False, xlabel='Month', ylabel='Stats', rot=0)
ax.ticklabel_format(axis='y', scilimits=(0,10))   # <--- no error
ax.ticklabel_format(axis='x', scilimits=(0,10))   # <--- error because ticklabels are strings

Also if the y-ticklabels need a thousands separator comma, then set_yticks() could be used to change it as such.

ax = my_df['stats'].plot(kind='bar', legend=False, xlabel='Month', ylabel='Stats', rot=0)
ax.set_yticks(ax.get_yticks()[:-1], [f"{int(x):,}" for x in ax.get_yticks()[:-1]]);

res

Upvotes: 3

aviss
aviss

Reputation: 2449

Adding this line helps to get numbers in a plain format but with ',' which looks much nicer:

ax.get_yaxis().set_major_formatter(
    matplotlib.ticker.FuncFormatter(lambda x, p: format(int(x), ',')))

enter image description here

And then I can use int(x)/ to convert to million or thousand as I wish:

enter image description here

Upvotes: 25

BENY
BENY

Reputation: 323366

Since you already using pandas

import matplotlib.pyplot as plt
my_df.plot(kind='bar')
plt.ticklabel_format(style='plain', axis='y')

enter image description here

Upvotes: 22

Related Questions