diff options
author | Ross Barnowski <rossbar@berkeley.edu> | 2020-02-27 16:30:54 -0800 |
---|---|---|
committer | Ross Barnowski <rossbar@berkeley.edu> | 2020-05-07 10:26:32 -0700 |
commit | 9c83b13ce1b08aed8181d284566d002086393a89 (patch) | |
tree | e0d8e8316290306ab381e6997399630f0ff1ef01 /numpy/polynomial/_polybase.py | |
parent | 965b41d418e6100c1afae0b6f818a7ef152bc25d (diff) | |
download | numpy-9c83b13ce1b08aed8181d284566d002086393a89.tar.gz |
ENH: Improved __str__, __format__ for polynomials
Changes the printing style of instances of the convenience classes in
the polynomial package to a more "human-readable" format.
__str__ has been modified and __format__ added to ABCPolyBase, modifying
the string representation of polynomial instances, e.g. when printed.
__repr__ and the _repr_latex method (which is used in the Jupyter
environment are unchanged.
Two print formats have been added: 'unicode' and 'ascii'. 'unicode' is
the default mode on *nix systems, and uses unicode values for numeric
subscripts and superscripts in the polynomial expression. The 'ascii'
format is the default on Windows (due to font considerations) and uses
Python-style syntax to represent powers, e.g. x**2. The default
printing style can be controlled at the package-level with the
set_default_printstyle function.
The ABCPolyBase.__str__ has also been made to respect the linewidth
printoption. Other parameters from the printoptions dictionary are not
used.
Co-Authored-By: Warren Weckesser <warren.weckesser@gmail.com>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
Diffstat (limited to 'numpy/polynomial/_polybase.py')
-rw-r--r-- | numpy/polynomial/_polybase.py | 110 |
1 files changed, 107 insertions, 3 deletions
diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index 53efbb90f..253aa74e8 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -6,6 +6,7 @@ for the various polynomial classes. It operates as a mixin, but uses the abc module from the stdlib, hence it is only available for Python >= 2.6. """ +import os import abc import numbers @@ -67,6 +68,37 @@ class ABCPolyBase(abc.ABC): # Limit runaway size. T_n^m has degree n*m maxpower = 100 + # Unicode character mappings for improved __str__ + _superscript_mapping = str.maketrans({ + "0": "⁰", + "1": "¹", + "2": "²", + "3": "³", + "4": "⁴", + "5": "⁵", + "6": "⁶", + "7": "⁷", + "8": "⁸", + "9": "⁹" + }) + _subscript_mapping = str.maketrans({ + "0": "₀", + "1": "₁", + "2": "₂", + "3": "₃", + "4": "₄", + "5": "₅", + "6": "₆", + "7": "₇", + "8": "₈", + "9": "₉" + }) + # Some fonts don't support full unicode character ranges necessary for + # the full set of superscripts and subscripts, including common/default + # fonts in Windows shells/terminals. Therefore, default to ascii-only + # printing on windows. + _use_unicode = not os.name == 'nt' + @property @abc.abstractmethod def domain(self): @@ -283,10 +315,82 @@ class ABCPolyBase(abc.ABC): name = self.__class__.__name__ return f"{name}({coef}, domain={domain}, window={window})" + def __format__(self, fmt_str): + if fmt_str == '': + return self.__str__() + if fmt_str not in ('ascii', 'unicode'): + raise ValueError( + f"Unsupported format string '{fmt_str}' passed to " + f"{self.__class__}.__format__. Valid options are " + f"'ascii' and 'unicode'" + ) + if fmt_str == 'ascii': + return self._generate_string(self._str_term_ascii) + return self._generate_string(self._str_term_unicode) + def __str__(self): - coef = str(self.coef) - name = self.nickname - return f"{name}({coef})" + if self._use_unicode: + return self._generate_string(self._str_term_unicode) + return self._generate_string(self._str_term_ascii) + + def _generate_string(self, term_method): + """ + Generate the full string representation of the polynomial, using + ``term_method`` to generate each polynomial term. + """ + # Get configuration for line breaks + linewidth = np.get_printoptions().get('linewidth', 75) + if linewidth < 1: + linewidth = 1 + out = f"{self.coef[0]}" + for i, coef in enumerate(self.coef[1:]): + out += " " + power = str(i + 1) + # Polynomial coefficient + if coef >= 0: + next_term = f"+ {coef}" + else: + next_term = f"- {-coef}" + # Polynomial term + next_term += term_method(power, "x") + # Length of the current line with next term added + line_len = len(out.split('\n')[-1]) + len(next_term) + # If not the last term in the polynomial, it will be two + # characters longer due to the +/- with the next term + if i < len(self.coef[1:]) - 1: + line_len += 2 + # Handle linebreaking + if line_len >= linewidth: + next_term = next_term.replace(" ", "\n", 1) + out += next_term + return out + + @classmethod + def _str_term_unicode(cls, i, arg_str): + """ + String representation of single polynomial term using unicode + characters for superscripts and subscripts. + """ + if cls.basis_name is None: + raise NotImplementedError( + "Subclasses must define either a basis_name, or override " + "_str_term_unicode(cls, i, arg_str)" + ) + return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}" + f"({arg_str})") + + @classmethod + def _str_term_ascii(cls, i, arg_str): + """ + String representation of a single polynomial term using ** and _ to + represent superscripts and subscripts, respectively. + """ + if cls.basis_name is None: + raise NotImplementedError( + "Subclasses must define either a basis_name, or override " + "_str_term_ascii(cls, i, arg_str)" + ) + return f" {cls.basis_name}_{i}({arg_str})" @classmethod def _repr_latex_term(cls, i, arg_str, needs_parens): |