253

I have an existing plot that was created with pandas like this:

df['myvar'].plot(kind='bar')

The y axis is format as float and I want to change the y axis to percentages. All of the solutions I found use ax.xyz syntax andI can only place code below the line above that creates the plot (I cannot add ax=ax to the line above.)

How can I format the y axis as percentages without changing the line above?

Here is the solution I foundbut requires that I redefine the plot:

import matplotlib.pyplot as pltimport numpy as npimport matplotlib.ticker as mtickdata = [8,12,15,17,18,18.5]perc = np.linspace(0,100,len(data))fig = plt.figure(1, (7,4))ax = fig.add_subplot(1,1,1)ax.plot(perc, data)fmt = '%.0f%%' # Format you want the ticks, e.g. '40%'xticks = mtick.FormatStrFormatter(fmt)ax.xaxis.set_major_formatter(xticks)plt.show()

Link to the above solution:Pyplot: using percentage on x axis

askedJul 11, 2015 at 13:21
Chris's user avatar
0

10 Answers10

380

This is a few months late, but I have createdPR#6251 with matplotlib to add a newPercentFormatter class. With this class you just need one line to reformat your axis (two if you count the import ofmatplotlib.ticker):

import ...import matplotlib.ticker as mtickax = df['myvar'].plot(kind='bar')ax.yaxis.set_major_formatter(mtick.PercentFormatter())

PercentFormatter() accepts three arguments,xmax,decimals,symbol.xmax allows you to set the value that corresponds to 100% on the axis. This is nice if you have data from 0.0 to 1.0 and you want to display it from 0% to 100%. Just doPercentFormatter(1.0).

The other two parameters allow you to set the number of digits after the decimal point and the symbol. They default toNone and'%', respectively.decimals=None will automatically set the number of decimal points based on how much of the axes you are showing.

Update

PercentFormatter was introduced into Matplotlib proper in version 2.1.0.

answeredMar 30, 2016 at 21:16
Mad Physicist's user avatar
Sign up to request clarification or add additional context in comments.

17 Comments

Oh, i think it's because the default is decimal=None, which auto-generates the number of decimals depending on the range. So if the range is less than 50%, it does 10.0%. More than 50% it does 10%. So apologies - your answer is correct, depending on other parameters.
Nive answer! In my case, I usedplt.gca().yaxis.set_major_formatter(mtick.PercentFormatter()) to avoid saving aax plot first.
Very strange: For me, this simply added '%', but without multiplying by 100. OTOH, the answer by erwanp worked as expected. I didn't explore the issue further.
@MadPhysicist - I was using it with no arguments, as you posted. UsingPercentFormatter(1.0) did the job. It seems the default isPercentFormatter(100), which is most unexpected.
@MadPhysicist - Because 1.0=100%, they are the samenumber! It's as if changing to scientific notation usingx1e6 changed1000000 into1000000x1e6... it changes the numbers. I would have never thought otherwise, and that is certainly the reason why the answer by erwanp works as expected without needing any further input parameters, as inPercentFormatter. It's possibly nice to have the further flexibility ofxmax, but the default is clearly wrong, it is not "no transformation". I must be missing something...
|
171

pandas dataframe plot will return theax for you, And then you can start to manipulate the axes whatever you want.

import pandas as pdimport numpy as npdf = pd.DataFrame(np.random.randn(100,5))# you get ax from hereax = df.plot()type(ax)  # matplotlib.axes._subplots.AxesSubplot# manipulatevals = ax.get_yticks()ax.set_yticklabels(['{:,.2%}'.format(x) for x in vals])

enter image description here

miriamsimone's user avatar
miriamsimone
36.7k12 gold badges97 silver badges121 bronze badges
answeredJul 11, 2015 at 13:36
Jianxun Li's user avatar

5 Comments

This will have undesired effects as soon as you pan/zoom the graph interactively
Million times easier than trying to usematplotlib.ticker function formatters!
How do you then limit the y axis to say (0,100%)? I tried ax.set_ylim(0,100) but that doesn't seem to work!!
@mpour only the labels of the yticks are changed, so the limits are still in natural units. Setting ax.set_ylim(0, 1) will do the trick.
Not sure why but this answer mislabelled the ticks whereas erwanp's correctly labeled axross the entire axis.
100

Jianxun's solution did the job for me but broke the y value indicator at the bottom left of the window.

I ended up usingFuncFormatterinstead (and also stripped the uneccessary trailing zeroes as suggestedhere):

import pandas as pdimport numpy as npfrom matplotlib.ticker import FuncFormatterdf = pd.DataFrame(np.random.randn(100,5))ax = df.plot()ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:.0%}'.format(y)))

Generally speaking I'd recommend usingFuncFormatter for label formatting: it's reliable, and versatile.

enter image description here

answeredFeb 17, 2016 at 1:39
erwanp's user avatar

3 Comments

You can simplify the code even more:ax.yaxis.set_major_formatter(FuncFormatter('{0:.0%}'.format)). AKA no need for the lambda, let format do the work.
@DanielHimmelstein can you explain this a little bit? Particularly inside the { }. Not sure how my 0.06 gets turned into 6% using that with the python format. Also great solution. Seems to work much more reliably than using .set_ticklabels
@DChaps'{0:.0%}'.format creates aformatting function. The0 before the colon tells the formatter to replace the curly-brackets and its contents with the first argument passed to the function. The part after the colon,.0%, tells the formatter how to render the value. The.0 specifies 0 decimal places and% specifies rendering as a percent.
61

For those who are looking for the quick one-liner:

plt.gca().set_yticklabels([f'{x:.0%}' for x in plt.gca().get_yticks()])

or, if you've saved yourmatplotlib.axes.Axes object into variableax:

ax.set_yticklabels([f'{x:.0f}%' for x in ax.get_yticks()])

this assumes

  • import:from matplotlib import pyplot as plt
  • Python >=3.6 for f-String formatting. For older versions, replacef'{x:.0%}' with'{:.0%}'.format(x)
answeredOct 26, 2016 at 8:35
Niko Fohr's user avatar

2 Comments

For me, Daniel Himmelstein's answer worked whereas this answer changed the scale
If setting tick labels changes the scale, you should issue the scale selection command after this.
23

I'm late to the game but I just realize this:ax can be replaced withplt.gca() for those who are not using axes and just subplots.

Echoing @Mad Physicist answer, using the packagePercentFormatter it would be:

import matplotlib.ticker as mtickplt.gca().yaxis.set_major_formatter(mtick.PercentFormatter(1))#if you already have ticks in the 0 to 1 range. Otherwise see their answer
answeredJun 26, 2020 at 13:53
Cat Mai's user avatar

Comments

6

I propose an alternative method usingseaborn

Working code:

import pandas as pdimport seaborn as snsdata=np.random.rand(10,2)*100df = pd.DataFrame(data, columns=['A', 'B'])ax= sns.lineplot(data=df, markers= True)ax.set(xlabel='xlabel', ylabel='ylabel', title='title')#changing ylables ticksy_value=['{:,.2f}'.format(x) + '%' for x in ax.get_yticks()]ax.set_yticklabels(y_value)

enter image description here

answeredJun 23, 2019 at 11:31
Dr. Arslan's user avatar

Comments

5

You can do this in one line without importing anything:plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{}%'.format))

If you want integer percentages, you can do:plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{:.0f}%'.format))

You can use eitherax.yaxis orplt.gca().yaxis.FuncFormatter is still part ofmatplotlib.ticker, but you can also doplt.FuncFormatter as a shortcut.

Trenton McKinney's user avatar
Trenton McKinney
63.2k41 gold badges170 silver badges213 bronze badges
answeredAug 29, 2021 at 2:05
1'''s user avatar

Comments

3

Based on the answer of @erwanp, you can use theformatted string literals of Python 3,

x = '2'percentage = f'{x}%' # 2%

inside theFuncFormatter() and combined with alambda expression.

All wrapped:

ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f'{y}%'))
answeredNov 6, 2020 at 20:45
Juan CA's user avatar

Comments

3

Another one line solution if the yticks are between 0 and 1:

plt.yticks(plt.yticks()[0], ['{:,.0%}'.format(x) for x in plt.yticks()[0]])
answeredJan 17, 2022 at 11:33
Joles's user avatar

Comments

1

add a line of code

ax.yaxis.set_major_formatter(ticker.PercentFormatter())

answeredFeb 13, 2023 at 2:33
Zeeshan Ch's user avatar

Comments

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.