17

I am attempting to change the format of my axis to be comma seperated in Matplotlib running under Python 2.7 but am unable to do so.

I suspect that I need to use a FuncFormatter but I am at a bit of a loss.

Can anyone help?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Patrick A
  • 277
  • 1
  • 3
  • 12

5 Answers5

18

Yes, you can use matplotlib.ticker.FuncFormatter to do this.

Here is the example:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr

def func(x, pos):  # formatter function takes tick label and tick position
    s = str(x)
    ind = s.index('.')
    return s[:ind] + ',' + s[ind+1:]   # change dot to comma

y_format = tkr.FuncFormatter(func)  # make formatter

x = np.linspace(0,10,501)
y = np.sin(x)
ax = plt.subplot(111)
ax.plot(x,y)
ax.yaxis.set_major_formatter(y_format)  # set formatter to needed axis

plt.show()

This results in the following plot:

funcformatter plot

Andrey Sobolev
  • 12,353
  • 3
  • 48
  • 52
  • 6
    In Python 2.7, instead of defining the `func` above, you can simply use the built-in comma separation feature of the string format function, like this: `FuncFormatter('{:,.0f}'.format)` – mhowison May 04 '13 at 20:43
  • Such formatting truncates the fractional part of a floating point number, resulting in three `0` ticks on the example above. – Andrey Sobolev May 06 '13 at 06:13
  • 3
    Instead of definig a sepcial function `func(x, pos)` you could simply write `ax.get_yaxis().set_major_formatter( ticker.FuncFormatter(lambda x, pos: str(x).replace('.',',')) )` – petru Mar 27 '15 at 11:03
  • @petru Your solution replaces `0.05` by `0,05`, but `0.10` by `0,1`. How to obtain `0,10`? – Karlo Aug 13 '17 at 17:25
  • 2
    @Karlo I guess the simplest solution would be to modify the `lambda` function as `lambda x, pos: '{:.2f}'.format(x).replace('.', ',')` – petru Aug 14 '17 at 18:09
17

I know the question is old, but as I currently am searching for similar solutions, I decided to leave a comment for future reference if others need this.

For an alternative solution, use the locale module and activate locale-formatting in matplotlib.

E.g., in major parts of Europe, comma is the desired separator. You can use

#Locale settings
import locale
locale.setlocale(locale.LC_ALL, "deu_deu")
import matplotlib as mpl
mpl.rcParams['axes.formatter.use_locale'] = True

#Generate sample plot
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,10,501)
y = np.sin(x)
ax = plt.subplot(111)
ax.plot(x,y)
ax.yaxis.set_major_formatter(y_format)  # set formatter to needed axis

plt.show()

to produce the same plot as in Andrey's solution, but you can be sure it behaves correctly also in corner-cases.

Thorsten Kranz
  • 12,492
  • 2
  • 39
  • 56
  • 1
    really, in R one can just call library(scales) and add to the plot scale_y_continuous(labels=comma) - I am trying to learn Python for data analysis but for all I find appealing, I then find things like this that make no sense to me - like comma formatting was not going to be something people want – user1617979 Oct 15 '14 at 00:00
  • 2
    Thank you. I think this should be the preferred way. Note that for Linux the locale string might be different ('de_DE.utf8' in my case). Check `locale -a` output in console for possible values. Also there is no need to set the formatter (C&P error) – Flamefire Sep 12 '16 at 19:33
  • Awesome, saved me! – Karlo Aug 13 '17 at 17:27
  • A major advantage of this answer is that many of the built-in formatters use a proper mathematical minus in place of the short hyphen-minus. That's a very common corner-case that will be broken by just using a format-string but preserved in this approach. – Joooeey May 31 '23 at 14:29
6

I think the question really refers to presenting say 300000 on the y-axis as 300,000.

To borrow from Andrey's answer, with a minor tweak,

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr

def func(x, pos):  # formatter function takes tick label and tick position
   s = '{:0,d}'.format(int(x))
   return s


y_format = tkr.FuncFormatter(func)  # make formatter

x = np.linspace(0,10,501)
y = np.sin(x)
ax = plt.subplot(111)
ax.plot(x,y)
ax.yaxis.set_major_formatter(y_format)  # set formatter to needed axis

plt.show()
Liran Funaro
  • 2,750
  • 2
  • 22
  • 33
pythOnometrist
  • 6,531
  • 6
  • 30
  • 50
1

I would like to expand on Thorsten Kranz answer, it seems that matplotlib (2.02) may have a bug, in that it does not use the thousand sep field of the locale to do the separation of thousands. This happens even if set_locale(True) is used.

Therefore if you set the locale to the UK locale, it should still separate the thousands by a comma but it does not. It does it for the German locale since the decimal point is used.

UK ('English_United Kingdom.1252') Locale:

{'currency_symbol': '\xa3',
 'decimal_point': '.',
 'frac_digits': 2,
 'grouping': [3, 0],
 'int_curr_symbol': 'GBP',
 'int_frac_digits': 2,
 'mon_decimal_point': '.',
 'mon_grouping': [3, 0],
 'mon_thousands_sep': ',',
 'n_cs_precedes': 1,
 'n_sep_by_space': 0,
 'n_sign_posn': 3,
 'negative_sign': '-',
 'p_cs_precedes': 1,
 'p_sep_by_space': 0,
 'p_sign_posn': 3,
 'positive_sign': '',
 'thousands_sep': ','}

German ('German_Germany.1252') locale:

{'currency_symbol': '\x80',
 'decimal_point': ',',
 'frac_digits': 2,
 'grouping': [3, 0],
 'int_curr_symbol': 'EUR',
 'int_frac_digits': 2,
 'mon_decimal_point': ',',
 'mon_grouping': [3, 0],
 'mon_thousands_sep': '.',
 'n_cs_precedes': 0,
 'n_sep_by_space': 1,
 'n_sign_posn': 1,
 'negative_sign': '-',
 'p_cs_precedes': 0,
 'p_sep_by_space': 1,
 'p_sign_posn': 1,
 'positive_sign': '',
 'thousands_sep': '.'}

Edit: Looking at the code in the Scalar formatter, Matplotlib does not use the grouping flag:

def pprint_val(self, x):
"""The last argument should be True"""
    xp = (x - self.offset) / (10. ** self.orderOfMagnitude)
    if np.absolute(xp) < 1e-8:
        xp = 0
    if self._useLocale:
        return locale.format_string(self.format, (xp,)) # <-- there should be a True as the last argument to this method which sets to grouping to True
    else:
        return self.format % xp
Har
  • 3,727
  • 10
  • 41
  • 75
0

I would like to post another solution here similar to the one presented by Thorsten Kranz.

Use the following first lines in your code:

import locale
locale.setlocale(locale.LC_ALL, "Portuguese_Brazil.1252")
import matplotlib as mpl
mpl.rcParams['axes.formatter.use_locale'] = True

This way, your code will be settled to Brazil standard text formating. I believe it could help you out.

Philipe Riskalla Leal
  • 954
  • 1
  • 10
  • 28