-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Changes from all commits
5a550f2
722f5d6
3c221ff
122cfa2
07f022d
e371e5a
3049a2d
e0a2ec8
1621a2d
c82dcff
f09a1b4
dc9409b
0195beb
3ec1dbd
cd2ac88
7f1422c
aba7a7f
b75f20d
41a69da
556ec20
a9af431
0bde03a
c98ba91
60b95ab
ac42b94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Deprecation in EngFormatter | ||
``````````````````````````` | ||
|
||
Passing a string as *num* argument when calling an instance of | ||
`matplotlib.ticker.EngFormatter` is deprecated and will be removed in 2.3. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
New keyword argument 'sep' for EngFormatter | ||
------------------------------------------- | ||
|
||
A new "sep" keyword argument has been added to | ||
:class:`~matplotlib.ticker.EngFormatter` and provides a means to define | ||
the string that will be used between the value and its unit. The default | ||
string is " ", which preserves the former behavior. Besides, the separator is | ||
now present between the value and its unit even in the absence of SI prefix. | ||
There was formerly a bug that was causing strings like "3.14V" to be returned | ||
instead of the expected "3.14 V" (with the default behavior). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -173,7 +173,6 @@ | |
|
||
import six | ||
|
||
import decimal | ||
import itertools | ||
import locale | ||
import math | ||
|
@@ -1184,15 +1183,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). | ||
""" | ||
|
||
# The SI engineering prefixes | ||
ENG_PREFIXES = { | ||
-24: "y", | ||
|
@@ -1214,12 +1206,42 @@ class EngFormatter(Formatter): | |
24: "Y" | ||
} | ||
|
||
def __init__(self, unit="", places=None): | ||
def __init__(self, unit="", places=None, sep=" "): | ||
""" | ||
Parameters | ||
---------- | ||
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, | ||
the formatting falls back to the floating point format '%g', | ||
which displays up to 6 *significant* digits, i.e. the equivalent | ||
value for *places* varies between 0 and 5 (inclusive). | ||
|
||
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="\\N{THIN SPACE}"`` (``U+2009``); | ||
* ``sep="\\N{NARROW NO-BREAK SPACE}"`` (``U+202F``); | ||
* ``sep="\\N{NO-BREAK SPACE}"`` (``U+00A0``). | ||
""" | ||
self.unit = unit | ||
self.places = places | ||
self.sep = 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 | ||
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): | ||
|
@@ -1238,40 +1260,47 @@ def format_eng(self, num): | |
u'-1.00 \N{GREEK SMALL LETTER MU}' | ||
|
||
`num` may be a numeric value or a string that can be converted | ||
to a numeric value with the `decimal.Decimal` constructor. | ||
to a numeric value with ``float(num)``. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we actually have a use case for supporting this? Could this behavior be deprecated? (this is not strictly needed for the PR) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. String were accepted before so I let it here. But other formatter classes do not seems to support such a feature, so if one thinks it is not worth supporting it anymore, it could be deprecated indeed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to myself: add a deprecation warning for 2.1 (=> removing in 2.3?). (Have a look at #8040 to remember how to do it...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deprecated in f207c89 |
||
""" | ||
dnum = decimal.Decimal(str(num)) | ||
if isinstance(num, six.string_types): | ||
warnings.warn( | ||
"Passing a string as *num* argument is deprecated since" | ||
"Matplotlib 2.1, and is expected to be removed in 2.3.", | ||
mplDeprecation) | ||
|
||
dnum = float(num) | ||
sign = 1 | ||
fmt = "g" if self.places is None else ".{:d}f".format(self.places) | ||
|
||
if dnum < 0: | ||
sign = -1 | ||
dnum = -dnum | ||
|
||
if dnum != 0: | ||
pow10 = decimal.Decimal(int(math.floor(dnum.log10() / 3) * 3)) | ||
pow10 = int(math.floor(math.log10(dnum) / 3) * 3) | ||
else: | ||
pow10 = decimal.Decimal(0) | ||
|
||
pow10 = pow10.min(max(self.ENG_PREFIXES)) | ||
pow10 = pow10.max(min(self.ENG_PREFIXES)) | ||
pow10 = 0 | ||
# Force dnum to zero, to avoid inconsistencies like | ||
# format_eng(-0) = "0" and format_eng(0.0) = "0" | ||
# but format_eng(-0.0) = "-0.0" | ||
dnum = 0.0 | ||
|
||
pow10 = np.clip(pow10, min(self.ENG_PREFIXES), max(self.ENG_PREFIXES)) | ||
|
||
mant = sign * dnum / (10.0 ** pow10) | ||
# Taking care of the cases like 999.9..., which | ||
# may be rounded to 1000 instead of 1 k. Beware | ||
# of the corner case of values that are beyond | ||
# the range of SI prefixes (i.e. > 'Y'). | ||
_fmant = float("{mant:{fmt}}".format(mant=mant, fmt=fmt)) | ||
if _fmant >= 1000 and pow10 != max(self.ENG_PREFIXES): | ||
mant /= 1000 | ||
pow10 += 3 | ||
|
||
prefix = self.ENG_PREFIXES[int(pow10)] | ||
|
||
mant = sign * dnum / (10 ** pow10) | ||
|
||
if self.places is None: | ||
format_str = "%g %s" | ||
elif self.places == 0: | ||
format_str = "%i %s" | ||
elif self.places > 0: | ||
format_str = ("%%.%if %%s" % self.places) | ||
|
||
formatted = format_str % (mant, prefix) | ||
|
||
formatted = formatted.strip() | ||
if (self.unit != "") and (prefix == self.ENG_PREFIXES[0]): | ||
formatted = formatted + " " | ||
formatted = "{mant:{fmt}}{sep}{prefix}".format( | ||
mant=mant, sep=self.sep, prefix=prefix, fmt=fmt) | ||
|
||
return formatted | ||
|
||
|
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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 ;).