Spaces:
Sleeping
Sleeping
from sympy.core.containers import Tuple | |
from sympy.core.singleton import S | |
from sympy.core.symbol import Symbol | |
from sympy.core.sympify import SympifyError | |
from types import FunctionType | |
class TableForm: | |
r""" | |
Create a nice table representation of data. | |
Examples | |
======== | |
>>> from sympy import TableForm | |
>>> t = TableForm([[5, 7], [4, 2], [10, 3]]) | |
>>> print(t) | |
5 7 | |
4 2 | |
10 3 | |
You can use the SymPy's printing system to produce tables in any | |
format (ascii, latex, html, ...). | |
>>> print(t.as_latex()) | |
\begin{tabular}{l l} | |
$5$ & $7$ \\ | |
$4$ & $2$ \\ | |
$10$ & $3$ \\ | |
\end{tabular} | |
""" | |
def __init__(self, data, **kwarg): | |
""" | |
Creates a TableForm. | |
Parameters: | |
data ... | |
2D data to be put into the table; data can be | |
given as a Matrix | |
headings ... | |
gives the labels for rows and columns: | |
Can be a single argument that applies to both | |
dimensions: | |
- None ... no labels | |
- "automatic" ... labels are 1, 2, 3, ... | |
Can be a list of labels for rows and columns: | |
The labels for each dimension can be given | |
as None, "automatic", or [l1, l2, ...] e.g. | |
["automatic", None] will number the rows | |
[default: None] | |
alignments ... | |
alignment of the columns with: | |
- "left" or "<" | |
- "center" or "^" | |
- "right" or ">" | |
When given as a single value, the value is used for | |
all columns. The row headings (if given) will be | |
right justified unless an explicit alignment is | |
given for it and all other columns. | |
[default: "left"] | |
formats ... | |
a list of format strings or functions that accept | |
3 arguments (entry, row number, col number) and | |
return a string for the table entry. (If a function | |
returns None then the _print method will be used.) | |
wipe_zeros ... | |
Do not show zeros in the table. | |
[default: True] | |
pad ... | |
the string to use to indicate a missing value (e.g. | |
elements that are None or those that are missing | |
from the end of a row (i.e. any row that is shorter | |
than the rest is assumed to have missing values). | |
When None, nothing will be shown for values that | |
are missing from the end of a row; values that are | |
None, however, will be shown. | |
[default: None] | |
Examples | |
======== | |
>>> from sympy import TableForm, Symbol | |
>>> TableForm([[5, 7], [4, 2], [10, 3]]) | |
5 7 | |
4 2 | |
10 3 | |
>>> TableForm([list('.'*i) for i in range(1, 4)], headings='automatic') | |
| 1 2 3 | |
--------- | |
1 | . | |
2 | . . | |
3 | . . . | |
>>> TableForm([[Symbol('.'*(j if not i%2 else 1)) for i in range(3)] | |
... for j in range(4)], alignments='rcl') | |
. | |
. . . | |
.. . .. | |
... . ... | |
""" | |
from sympy.matrices.dense import Matrix | |
# We only support 2D data. Check the consistency: | |
if isinstance(data, Matrix): | |
data = data.tolist() | |
_h = len(data) | |
# fill out any short lines | |
pad = kwarg.get('pad', None) | |
ok_None = False | |
if pad is None: | |
pad = " " | |
ok_None = True | |
pad = Symbol(pad) | |
_w = max(len(line) for line in data) | |
for i, line in enumerate(data): | |
if len(line) != _w: | |
line.extend([pad]*(_w - len(line))) | |
for j, lj in enumerate(line): | |
if lj is None: | |
if not ok_None: | |
lj = pad | |
else: | |
try: | |
lj = S(lj) | |
except SympifyError: | |
lj = Symbol(str(lj)) | |
line[j] = lj | |
data[i] = line | |
_lines = Tuple(*[Tuple(*d) for d in data]) | |
headings = kwarg.get("headings", [None, None]) | |
if headings == "automatic": | |
_headings = [range(1, _h + 1), range(1, _w + 1)] | |
else: | |
h1, h2 = headings | |
if h1 == "automatic": | |
h1 = range(1, _h + 1) | |
if h2 == "automatic": | |
h2 = range(1, _w + 1) | |
_headings = [h1, h2] | |
allow = ('l', 'r', 'c') | |
alignments = kwarg.get("alignments", "l") | |
def _std_align(a): | |
a = a.strip().lower() | |
if len(a) > 1: | |
return {'left': 'l', 'right': 'r', 'center': 'c'}.get(a, a) | |
else: | |
return {'<': 'l', '>': 'r', '^': 'c'}.get(a, a) | |
std_align = _std_align(alignments) | |
if std_align in allow: | |
_alignments = [std_align]*_w | |
else: | |
_alignments = [] | |
for a in alignments: | |
std_align = _std_align(a) | |
_alignments.append(std_align) | |
if std_align not in ('l', 'r', 'c'): | |
raise ValueError('alignment "%s" unrecognized' % | |
alignments) | |
if _headings[0] and len(_alignments) == _w + 1: | |
_head_align = _alignments[0] | |
_alignments = _alignments[1:] | |
else: | |
_head_align = 'r' | |
if len(_alignments) != _w: | |
raise ValueError( | |
'wrong number of alignments: expected %s but got %s' % | |
(_w, len(_alignments))) | |
_column_formats = kwarg.get("formats", [None]*_w) | |
_wipe_zeros = kwarg.get("wipe_zeros", True) | |
self._w = _w | |
self._h = _h | |
self._lines = _lines | |
self._headings = _headings | |
self._head_align = _head_align | |
self._alignments = _alignments | |
self._column_formats = _column_formats | |
self._wipe_zeros = _wipe_zeros | |
def __repr__(self): | |
from .str import sstr | |
return sstr(self, order=None) | |
def __str__(self): | |
from .str import sstr | |
return sstr(self, order=None) | |
def as_matrix(self): | |
"""Returns the data of the table in Matrix form. | |
Examples | |
======== | |
>>> from sympy import TableForm | |
>>> t = TableForm([[5, 7], [4, 2], [10, 3]], headings='automatic') | |
>>> t | |
| 1 2 | |
-------- | |
1 | 5 7 | |
2 | 4 2 | |
3 | 10 3 | |
>>> t.as_matrix() | |
Matrix([ | |
[ 5, 7], | |
[ 4, 2], | |
[10, 3]]) | |
""" | |
from sympy.matrices.dense import Matrix | |
return Matrix(self._lines) | |
def as_str(self): | |
# XXX obsolete ? | |
return str(self) | |
def as_latex(self): | |
from .latex import latex | |
return latex(self) | |
def _sympystr(self, p): | |
""" | |
Returns the string representation of 'self'. | |
Examples | |
======== | |
>>> from sympy import TableForm | |
>>> t = TableForm([[5, 7], [4, 2], [10, 3]]) | |
>>> s = t.as_str() | |
""" | |
column_widths = [0] * self._w | |
lines = [] | |
for line in self._lines: | |
new_line = [] | |
for i in range(self._w): | |
# Format the item somehow if needed: | |
s = str(line[i]) | |
if self._wipe_zeros and (s == "0"): | |
s = " " | |
w = len(s) | |
if w > column_widths[i]: | |
column_widths[i] = w | |
new_line.append(s) | |
lines.append(new_line) | |
# Check heading: | |
if self._headings[0]: | |
self._headings[0] = [str(x) for x in self._headings[0]] | |
_head_width = max(len(x) for x in self._headings[0]) | |
if self._headings[1]: | |
new_line = [] | |
for i in range(self._w): | |
# Format the item somehow if needed: | |
s = str(self._headings[1][i]) | |
w = len(s) | |
if w > column_widths[i]: | |
column_widths[i] = w | |
new_line.append(s) | |
self._headings[1] = new_line | |
format_str = [] | |
def _align(align, w): | |
return '%%%s%ss' % ( | |
("-" if align == "l" else ""), | |
str(w)) | |
format_str = [_align(align, w) for align, w in | |
zip(self._alignments, column_widths)] | |
if self._headings[0]: | |
format_str.insert(0, _align(self._head_align, _head_width)) | |
format_str.insert(1, '|') | |
format_str = ' '.join(format_str) + '\n' | |
s = [] | |
if self._headings[1]: | |
d = self._headings[1] | |
if self._headings[0]: | |
d = [""] + d | |
first_line = format_str % tuple(d) | |
s.append(first_line) | |
s.append("-" * (len(first_line) - 1) + "\n") | |
for i, line in enumerate(lines): | |
d = [l if self._alignments[j] != 'c' else | |
l.center(column_widths[j]) for j, l in enumerate(line)] | |
if self._headings[0]: | |
l = self._headings[0][i] | |
l = (l if self._head_align != 'c' else | |
l.center(_head_width)) | |
d = [l] + d | |
s.append(format_str % tuple(d)) | |
return ''.join(s)[:-1] # don't include trailing newline | |
def _latex(self, printer): | |
""" | |
Returns the string representation of 'self'. | |
""" | |
# Check heading: | |
if self._headings[1]: | |
new_line = [] | |
for i in range(self._w): | |
# Format the item somehow if needed: | |
new_line.append(str(self._headings[1][i])) | |
self._headings[1] = new_line | |
alignments = [] | |
if self._headings[0]: | |
self._headings[0] = [str(x) for x in self._headings[0]] | |
alignments = [self._head_align] | |
alignments.extend(self._alignments) | |
s = r"\begin{tabular}{" + " ".join(alignments) + "}\n" | |
if self._headings[1]: | |
d = self._headings[1] | |
if self._headings[0]: | |
d = [""] + d | |
first_line = " & ".join(d) + r" \\" + "\n" | |
s += first_line | |
s += r"\hline" + "\n" | |
for i, line in enumerate(self._lines): | |
d = [] | |
for j, x in enumerate(line): | |
if self._wipe_zeros and (x in (0, "0")): | |
d.append(" ") | |
continue | |
f = self._column_formats[j] | |
if f: | |
if isinstance(f, FunctionType): | |
v = f(x, i, j) | |
if v is None: | |
v = printer._print(x) | |
else: | |
v = f % x | |
d.append(v) | |
else: | |
v = printer._print(x) | |
d.append("$%s$" % v) | |
if self._headings[0]: | |
d = [self._headings[0][i]] + d | |
s += " & ".join(d) + r" \\" + "\n" | |
s += r"\end{tabular}" | |
return s | |