summaryrefslogtreecommitdiff
path: root/numpy/polynomial/_polybase.py
diff options
context:
space:
mode:
authorRoss Barnowski <rossbar@berkeley.edu>2020-02-27 16:30:54 -0800
committerRoss Barnowski <rossbar@berkeley.edu>2020-05-07 10:26:32 -0700
commit9c83b13ce1b08aed8181d284566d002086393a89 (patch)
treee0d8e8316290306ab381e6997399630f0ff1ef01 /numpy/polynomial/_polybase.py
parent965b41d418e6100c1afae0b6f818a7ef152bc25d (diff)
downloadnumpy-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.py110
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):