8000 ENH: EngFormatter new kwarg 'sep' by afvincent · Pull Request #6542 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

ENH: EngFormatter new kwarg 'sep' #6542

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

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5a550f2
ENH: Add the space_sep option to EngFormatter, and docstring updates
afvincent Jun 6, 2016
722f5d6
FIX: force format_eng(-0.0) to be consistent with format_eng(0)
afvincent Jun 6, 2016
3c221ff
Fix docstrings and comments (remove leading spaces)
afvincent Jun 7, 2016
122cfa2
Simplify and move cleaning strip op. from 'format_eng' to '__call__'
afvincent Jun 7, 2016
07f022d
DOC: update the api example engineering_formatter.py
afvincent Jun 19, 2016
e371e5a
More extensive testing of EngFormatter (including 'space_sep' param)
afvincent Feb 11, 2017
3049a2d
space_sep=bool <- sep=string, and adapt tests
afvincent Feb 12, 2017
e0a2ec8
remove unnecessary trailing 'u' characters in strings
afvincent Feb 12, 2017
1621a2d
fix EngFormatter docstring: escape Unicode character codes
afvincent Feb 12, 2017
c82dcff
'fix' docstring: unindent and reorder bullet list + mention regular t…
afvincent Feb 12, 2017
f09a1b4
Update the example
afvincent Feb 12, 2017
dc9409b
fix test docstring PEP8 errors in test_ticker.py
afvincent Feb 12, 2017
0195beb
Add a 'whats_new' entry
afvincent Feb 12, 2017
3ec1dbd
docstring overhaul: fix bullet list and merge duplicated infos betwee…
afvincent Feb 13, 2017
cd2ac88
use named entities instead of raw unicode codes
afvincent Aug 17, 2017
7f1422c
stop mixing C-style and format-based formatting
afvincent Aug 17, 2017
aba7a7f
Small example tweaking to avoid label cluttering
afvincent Aug 17, 2017
b75f20d
fix an issue with {:g} and str format
afvincent Aug 17, 2017
41a69da
get rid of decimal.Decimal + fix rounding special cases like 999.9...
afvincent Aug 19, 2017
556ec20
Fix the related rounding discrepancies in test_ticker
afvincent Aug 19, 2017
a9af431
(try) fix(ing) an exception about raising an int to a negative power
afvincent Aug 19, 2017
0bde03a
fix some anntzer's comments
afvincent Aug 20, 2017
c98ba91
more complete handling of corner-case roundings + add proper tests
afvincent Aug 20, 2017
60b95ab
Fix some additional remarks made by anntzer
afvincent Aug 21, 2017
ac42b94
Deprecate passing a string as *num* argument
afvincent Aug 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docstring overhaul: fix bullet list and merge duplicated infos betwee…
…n class and init docstrings
  • Loading branch information
afvincent committed Aug 25, 2017
commit 3ec1dbd3f2ba8e255243807eebe651ad982f10eb
2 changes: 1 addition & 1 deletion examples/api/engineering_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Create artificial data to plot.
# The x data span over several decades to demonstrate several SI prefixes.
xs = np.logspace(1, 9, 100)
ys = (0.8 + 0.4*prng.uniform(size=100))*np.log10(xs)**2
ys = (0.8 + 0.4 * prng.uniform(size=100)) * np.log10(xs)**2

# Figure width is doubled (2*6.4) to display nicely 2 subplots side by side.
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(12.8, 4.8))
Expand Down
22 changes: 12 additions & 10 deletions lib/matplotlib/tests/test_ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,18 +576,19 @@ def test_params(self, input, expected):
"""
Test the formatting of EngFormatter for various values of the 'places'
argument, in several cases:
0. without a unit symbol but with a (default) space separator;
1. with both a unit symbol and a (default) space separator;
2x. with both a unit symbol and some non default separators;
3x. without a unit symbol but with some non default separators.
0. without a unit symbol but with a (default) space separator;
1. with both a unit symbol and a (default) space separator;
2. with both a unit symbol and some non default separators;
3. without a unit symbol but with some non default separators.
Note that cases 2. and 3. are looped over several separator strings.
"""

UNIT = 's' # seconds
DIGITS = '0123456789' # %timeit showed 10-20% faster search than set

# Case 0: unit='' (default) and sep=' ' (default).
# 'expected' already corresponds to this reference case.
exp_outputs = (_s for _s in expected) # simple copy of 'expected'
exp_outputs = expected
formatters = (
mticker.EngFormatter(), # places=None (default)
mticker.EngFormatter(places=0),
Expand All @@ -612,8 +613,9 @@ def test_params(self, input, expected):
# Test several non default separators: no separator, a narrow
# no-break space (unicode character) and an extravagant string.
for _sep in ("", "\u202f", "@_@"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\N{THIN SPACE} here too? (not a big deal)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ec92520

# Case 2x: unit=UNIT and sep=_sep.
# Remove the space separator from the reference case.
# Case 2: unit=UNIT and sep=_sep.
# Replace the default space separator from the reference case
# with the tested one `_sep` and append a unit symbol to it.
exp_outputs = (_s + _sep + UNIT if _s[-1] in DIGITS # no prefix
else _s.replace(" ", _sep) + UNIT
for _s in expected)
Expand All @@ -625,9 +627,9 @@ def test_params(self, input, expected):
for _formatter, _exp_output in zip(formatters, exp_outputs):
assert _formatter(input) == _exp_output

# Case 3x: unit='' (default) and sep=_sep.
# Remove the space separator from the reference case and append
# a unit symbol to it.
# Case 3: unit='' (default) and sep=_sep.
# Replace the default space separator from the reference case
# with the tested one `_sep`. Reference case is already unitless.
exp_outputs = (_s.replace(" ", _sep) for _s in expected)
formatters = (
mticker.EngFormatter(sep=_sep), # places=None (default)
Expand Down
50 changes: 23 additions & 27 deletions 8000 lib/matplotlib/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1184,19 +1184,8 @@ class EngFormatter(Formatter):
"""
Formats axis values using engineering prefixes to represent powers
of 1000, plus a specified unit, e.g., 10 MHz instead of 1e7.

`unit` is a string containing the abbreviated name of the unit,
suitable for use with single-letter representations of powers of
1000. For example, 'Hz' or 'm'.

`places` is the precision with which to display the number,
specified in digits after the decimal point (there will be between
one and three digits before the decimal point).

`sep` is the separator (a string) that is used between the number
and the prefix/unit. For example, '3.14 mV' if `sep` is " " (default)
and '3.14mV' if `sep` is "".
"""

# The SI engineering prefixes
ENG_PREFIXES = {
-24: "y",
Expand All @@ -1222,20 +1211,26 @@ def __init__(self, unit="", places=None, sep=" "):
"""
Parameters
----------
unit: string (default: "")
Unit symbol to use.

places: int (default: None)
Precision, i.e. number of digits after the decimal point.
If it is None, falls back to the floating point format '%g'.

sep: string (default: " ")
String used between the value and the prefix/unit. Beside the
default behavior, some other useful use cases may be:
* sep="" to append directly the prefix/unit to the value;
* sep="\\u2009" to use a thin space;
* sep="\\u202f" to use a narrow no-break space;
* sep="\\u00a0" to use a no-break space.
unit : str (default: "")
Unit symbol to use, suitable for use with single-letter
representations of powers of 1000. For example, 'Hz' or 'm'.

places : int (default: None)
Precision with which to display the number, specified in
digits after the decimal point (there will be between one
and three digits before the decimal point). If it is None,
falls back to the floating point format '%g'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"%g" defaults to a precision of 6. Does that mean that the default is simply places=6? Otherwise the statement is a bit unclear.

Copy link
Contributor Author
@afvincent afvincent Aug 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, if I am correct, "%g" results in up to 6 significant digits. This means that with it, the equivalent value for places will be between 0 and 5. I'll try to make the docstring clearer.

Edit: updated docstring in ec92520


sep : str (default: " ")
Separator used between the value and the prefix/unit. For
example, one get '3.14 mV' if ``sep`` is " " (default) and
'3.14mV' if ``sep`` is "". Besides the default behavior, some
other useful options may be:

* ``sep=""`` to append directly the prefix/unit to the value;
* ``sep="\\u2009"`` to use a thin space;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, named entities ("\N{...}") are more self-documenting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed by 8616a04

* ``sep="\\u202f"`` to use a narrow no-break space;
* ``sep="\\u00a0"`` to use a no-break space.
"""
self.unit = unit
self.places = places
Expand All @@ -1244,7 +1239,8 @@ def __init__(self, unit="", places=None, sep=" "):
def __call__(self, x, pos=None):
s = "%s%s" % (self.format_eng(x), self.unit)
# Remove the trailing separator when there is neither prefix nor unit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be rewritten as s = re.sub(re.escape(self.sep) + "$", "", s) (without the need for a conditional) but I'm not going to block the PR on the fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well regex are black magic to me, but as you kindly gave the answer plus the tests still passed locally, here it is with 033111f ;).

s = s.strip(self.sep)
if len(self.sep) > 0 and s.endswith(self.sep):
s = s[:-len(self.sep)]
return self.fix_minus(s)

def format_eng(self, num):
Expand Down
0