|
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 |
|
|
|
|
|
if isinstance(data, Matrix): |
|
data = data.tolist() |
|
_h = len(data) |
|
|
|
|
|
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): |
|
|
|
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): |
|
|
|
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) |
|
|
|
|
|
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): |
|
|
|
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] |
|
|
|
def _latex(self, printer): |
|
""" |
|
Returns the string representation of 'self'. |
|
""" |
|
|
|
if self._headings[1]: |
|
new_line = [] |
|
for i in range(self._w): |
|
|
|
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 |
|
|