Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Bug]: unintended space between comma and digit when using useMathText = True together with comma as decimal separator #23626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
der-zi opened this issue Aug 15, 2022 · 11 comments · Fixed by #25226
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones! topic: text/mathtext
Milestone

Comments

@der-zi
Copy link

der-zi commented Aug 15, 2022

Bug summary

When I turn on "useMathText" in matplotlib in the intention to replace 1e6 by $\times 10^6$, the formatting of the commas in the tick labels themselves get wrong. Before they are correct in the form "3,5", with "useMathText" they become "3, 5", with (half?) a whitespace between the comma and the following digit.
correct_but_ugly_dec_power
incorrect

Code for reproduction

%matplotlib inline
import locale
locale.setlocale(locale.LC_ALL, "deu_deu")
import matplotlib.pyplot as plt

plt.rcParams['axes.formatter.use_locale'] = True

fig, ax = plt.subplots()
x = [1000, 2000, 3000, 3.5e6]
y = [1, 4, 3, 5]
ax.plot(x, y, linestyle = "", marker = "o")
ax.set_xlabel("the x-axis")
ax.ticklabel_format(axis = 'x', useMathText = True)

Actual outcome

the formatting of the decimal power is altered, but the ticklabels themselves are rendered incorrectly (additional space between comma and following digit)

Expected outcome

the formatting of the decimal power is altered, and the ticklabels themselves are rendered correctly (as before adding useMathText = True)

Additional information

apparently, matplotlib uses LaTeX to render the mathematical notation of power, and also changes the way the normal ticklabels are created to TeX mode.
however, when using comma as decimal separator in TeX you have to consider that it is written by americans, using the comma as an element separator, not decimal separator, and therefore in standard configuration automatically adds a space behind the comma.

this can be fixed by using
3{,}5
or
3,\!5
or
\num{3,5} or \num{3.5} with siunitx configured correctly
or with the package icomma.

(Also, when already using the locale set to german, one could be expecting \cdot rather than \times. There should be an option to choose between these)

Operating system

No response

Matplotlib Version

3.5.3

Matplotlib Backend

module://matplotlib_inline.backend_inline

Python version

3.9.7

Jupyter version

6.4.10

Installation

pip

@oscargus
Copy link
Member

oscargus commented Aug 15, 2022

This is probably the place to fix it:

def _format_maybe_minus_and_locale(self, fmt, arg):
"""
Format *arg* with *fmt*, applying Unicode minus and locale if desired.
"""
return self.fix_minus(locale.format_string(fmt, (arg,), True)
if self._useLocale else fmt % arg)

The main issue is most likely how to deal with format strings that already includes a ,... Probably one should try and escape those somehow, replace ',' with '{,}' and get rid of the escapes. Given that self._useMathText is True.

@oscargus
Copy link
Member

For \cdot vs \times, probably the most feasible way is to provide configuration using something like rcParams['text.formatter.exponent_separator'] (or whatever a better name is).

@anntzer
Copy link
Contributor

anntzer commented Aug 31, 2022

The main issue is most likely how to deal with format strings that already includes a ,

A possibility is perhaps to just copypaste the logic of python's format_string (https://github.com/python/cpython/blob/f7e7bf161aaec5a5cffdcec7c97e1f09e445421b/Lib/locale.py#L213), split out format specifiers and non-specifiers and brace-escape the non-specifiers.

@oscargus oscargus added Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones! labels Sep 12, 2022
@oscargus
Copy link
Member

Marking as a good first issue, but medium, as it is well defined what should happen, but maybe not so straightforward to solve.

@evanandresen
Copy link

Currently working on this issue.

@evanandresen
Copy link

This is probably the place to fix it:

def _format_maybe_minus_and_locale(self, fmt, arg):
"""
Format *arg* with *fmt*, applying Unicode minus and locale if desired.
"""
return self.fix_minus(locale.format_string(fmt, (arg,), True)
if self._useLocale else fmt % arg)

The main issue is most likely how to deal with format strings that already includes a ,... Probably one should try and escape those somehow, replace ',' with '{,}' and get rid of the escapes. Given that self._useMathText is True.

I have a quick fix PR ready to submit that changes the "," to "{,}" under the given conditions. I'm unsure how I would implement a pytest for this change. Is it necessary because it is a small and mostly visual change and if so does anyone have advice on what to assert?

@evanandresen
Copy link

image

It looks like my fix actually messes up the xy values of the cursor location. What exactly is this feature of the plot called so I can find it in the code? (The plot element that shows the coordinates of the cursor)

@oscargus
Copy link
Member

It is set with set_message and the formatting is done using _mouse_event_to_message.

@staticmethod
def _mouse_event_to_message(event):
if event.inaxes and event.inaxes.get_navigate():
try:
s = event.inaxes.format_coord(event.xdata, event.ydata)
except (ValueError, OverflowError):
pass
else:
s = s.rstrip()
artists = [a for a in event.inaxes._mouseover_set
if a.contains(event)[0] and a.get_visible()]
if artists:
a = cbook._topmost_artist(artists)
if a is not event.inaxes.patch:
data = a.get_cursor_data(event)
if data is not None:
data_str = a.format_cursor_data(data).rstrip()
if data_str:
s = s + '\n' + data_str
return s
def mouse_move(self, event):
self._update_cursor(event)
s = self._mouse_event_to_message(event)
if s is not None:
self.set_message(s)
else:
self.set_message(self.mode)

@oscargus
Copy link
Member

For the testing, it probably makes sense to have a parameterized test for a few locales (one using , and one using .).
https://docs.python.org/3/library/locale.html
Set up the locale, set up an Axes with relevant limits and check the tick labels.

@pytest.mark.parametrize("loc, result", (('DE', (r'3,5 \times 10^3', ...))
def test_locale_comma(loc, result):
   locale.setlocale(locale.LC_ALL, loc)
  ...
  assert ... == result

Maybe even create a formatter that uses a , elsewhere and use that to check that it becomes correct (although it is, sort of, a quite unusual case).

@devRD
Copy link
Contributor

devRD commented Feb 11, 2023

Is anyone working on this issue? I would like to work on this issue following the discussion here and the discussion from #24079

@rcomer
Copy link
Member

rcomer commented Feb 11, 2023

Thanks @devRD, it seems likely that #24079 has stalled, so I think it’s fine to pick this up if you are interested. It would be good to bring it to a conclusion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues Good first issue Open a pull request against these issues if there are no active ones! topic: text/mathtext
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants