|
131 | 131 | :class:`LogFormatter`
|
132 | 132 | formatter for log axes
|
133 | 133 |
|
| 134 | +:class:`PercentFormatter` |
| 135 | + Format labels as a percentage |
134 | 136 |
|
135 | 137 | You can derive your own formatter from the Formatter base class by
|
136 | 138 | simply overriding the ``__call__`` method. The formatter class has access
|
|
152 | 154 | from __future__ import (absolute_import, division, print_function,
|
153 | 155 | unicode_literals)
|
154 | 156 |
|
| 157 | + |
| 158 | +__all__ = ('TickHelper', 'Formatter', 'FixedFormatter', |
| 159 | + 'NullFormatter', 'FuncFormatter', 'FormatStrFormatter', |
| 160 | + 'StrMethodFormatter', 'ScalarFormatter', 'LogFormatter', |
| 161 | + 'LogFormatterExponent', 'LogFormatterMathtext', |
| 162 | + 'LogitFormatter', 'EngFormatter', 'PercentFormatter', |
| 163 | + 'Locator', 'IndexLocator', 'FixedLocator', 'NullLocator', |
| 164 | + 'LinearLocator', 'LogLocator', 'AutoLocator', |
| 165 | + 'MultipleLocator', 'MaxNLocator', 'AutoMinorLocator', |
| 166 | + 'SymmetricalLogLocator') |
| 167 | + |
| 168 | + |
155 | 169 | from matplotlib.externals import six
|
156 | 170 |
|
157 | 171 | import decimal
|
@@ -973,6 +987,87 @@ def format_eng(self, num):
|
973 | 987 | return formatted.strip()
|
974 | 988 |
|
975 | 989 |
|
| 990 | +class PercentFormatter(Formatter): |
| 991 | + """ |
| 992 | + Format numbers as a percentage. |
| 993 | +
|
| 994 | + How the number is converted into a percentage is determined by the |
| 995 | + `max` parameter. `max` is the data value that corresponds to 100%. |
| 996 | + Percentages are computed as ``x / max * 100``. So if the data is |
| 997 | + already scaled to be percentages, `max` will be 100. Another common |
| 998 | + situation is where `max` is 1.0. |
| 999 | + """ |
| 1000 | + def __init__(self, max=100, decimals=None, symbol='%'): |
| 1001 | + """ |
| 1002 | + Initializes the formatter. |
| 1003 | +
|
| 1004 | + `max` is the data value that corresponds to 100%. `symbol` is |
| 1005 | + a string which will be appended to the label. It may be `None` |
| 1006 | + or empty to indicate that no symbol should be used. `decimals` |
| 1007 | + is the number of decimal places to place after the |
| 1008 | + """ |
| 1009 | + self.max = max + 0.0 |
| 1010 | + self.decimals = decimals |
| 1011 | + self.symbol = symbol |
| 1012 | + |
| 1013 | + def __call__(self, x, pos=None): |
| 1014 | + """ |
| 1015 | + Formats the tick as a percentage with the appropriate scaling. |
| 1016 | + """ |
| 1017 | + xmin, xmax = self.axis.get_view_interval() |
| 1018 | + d = abs(xmax - xmin) |
| 1019 | + |
| 1020 | + return self.fix_minus(format_pct(x, d)) |
| 1021 | + |
| 1022 | + def format_pct(self, x, d): |
| 1023 | + """ |
| 1024 | + Formats the number as a percentage number with the correct |
| 1025 | + number of decimals and adds the percent symbol, if any. |
| 1026 | +
|
| 1027 | + If `self.decimals` is `None`, the number of digits after the |
| 1028 | + decimal point is set based on the width of the domain `d` as |
| 1029 | + follows: |
| 1030 | +
|
| 1031 | + +-------+----------+------------------------+ |
| 1032 | + | d | decimals | sample | |
| 1033 | + +-------+----------+------------------------+ |
| 1034 | + + >50 | 0 | ``x = 34.5`` => 34% | |
| 1035 | + +-------+----------+------------------------+ |
| 1036 | + | >5 | 1 | ``x = 34.5`` => 34.5% | |
| 1037 | + +-------+----------+------------------------+ |
| 1038 | + | >0.5 | 2 | ``x = 34.5`` => 34.50% | |
| 1039 | + +-------+----------+------------------------+ |
| 1040 | + | ... | ... | ... | |
| 1041 | + +-------+----------+------------------------+ |
| 1042 | +
|
| 1043 | + This method will not be very good for tiny ranges or extremely |
| 1044 | + large ranges. It assumes that the values on the chart are |
| 1045 | + percentages displayed on a reasonable scale. |
| 1046 | + """ |
| 1047 | + x = self.convert_to_pct(x) |
| 1048 | + if self.decimals is None: |
| 1049 | + # Luckily Python's built-in `ceil` rounds to +inf, not away |
| 1050 | + # from zero. This is very important since the equation for |
| 1051 | + # `decimals` starts out as `d > 0.5 * 10**(2 - decimals)` |
| 1052 | + # and ends up with `decimals > 2 - log10(2 * d)`. |
| 1053 | + d = self.convert_to_pct(d) # d is a difference, so this works fine |
| 1054 | + decimals = math.ceil(2.0 - math.log10(2.0 * d)) |
| 1055 | + if decimals > 5: |
| 1056 | + decimals = 5 |
| 1057 | + elif decimals < 0: |
| 1058 | + decimals = 0 |
| 1059 | + else: |
| 1060 | + decimals = self.decimals |
| 1061 | + s = '{x:0.{decimals}f}'.format(x=x, decimals=int(decimals)) |
| 1062 | + |
| 1063 | + if self.symbol: |
| 1064 | + return s + self.symbol |
| 1065 | + return s |
| 1066 | + |
| 1067 | + def convert_to_pct(self, x): |
| 1068 | + return 100.0 * (x / self.max) |
| 1069 | + |
| 1070 | + |
976 | 1071 | class Locator(TickHelper):
|
977 | 1072 | """
|
978 | 1073 | Determine the tick locations;
|
@@ -2056,12 +2151,3 @@ def get_locator(self, d):
|
2056 | 2151 |
|
2057 | 2152 | return locator
|
2058 | 2153 |
|
2059 |
| - |
2060 |
| -__all__ = ('TickHelper', 'Formatter', 'FixedFormatter', |
2061 |
| - 'NullFormatter', 'FuncFormatter', 'FormatStrFormatter', |
2062 |
| - 'StrMethodFormatter', 'ScalarFormatter', 'LogFormatter', |
2063 |
| - 'LogFormatterExponent', 'LogFormatterMathtext', 'Locator', |
2064 |
| - 'IndexLocator', 'FixedLocator', 'NullLocator', |
2065 |
| - 'LinearLocator', 'LogLocator', 'AutoLocator', |
2066 |
| - 'MultipleLocator', 'MaxNLocator', 'AutoMinorLocator', |
2067 |
| - 'SymmetricalLogLocator') |
|
0 commit comments