tableform.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. from sympy.core.containers import Tuple
  2. from sympy.core.singleton import S
  3. from sympy.core.symbol import Symbol
  4. from sympy.core.sympify import SympifyError
  5. from types import FunctionType
  6. class TableForm:
  7. r"""
  8. Create a nice table representation of data.
  9. Examples
  10. ========
  11. >>> from sympy import TableForm
  12. >>> t = TableForm([[5, 7], [4, 2], [10, 3]])
  13. >>> print(t)
  14. 5 7
  15. 4 2
  16. 10 3
  17. You can use the SymPy's printing system to produce tables in any
  18. format (ascii, latex, html, ...).
  19. >>> print(t.as_latex())
  20. \begin{tabular}{l l}
  21. $5$ & $7$ \\
  22. $4$ & $2$ \\
  23. $10$ & $3$ \\
  24. \end{tabular}
  25. """
  26. def __init__(self, data, **kwarg):
  27. """
  28. Creates a TableForm.
  29. Parameters:
  30. data ...
  31. 2D data to be put into the table; data can be
  32. given as a Matrix
  33. headings ...
  34. gives the labels for rows and columns:
  35. Can be a single argument that applies to both
  36. dimensions:
  37. - None ... no labels
  38. - "automatic" ... labels are 1, 2, 3, ...
  39. Can be a list of labels for rows and columns:
  40. The labels for each dimension can be given
  41. as None, "automatic", or [l1, l2, ...] e.g.
  42. ["automatic", None] will number the rows
  43. [default: None]
  44. alignments ...
  45. alignment of the columns with:
  46. - "left" or "<"
  47. - "center" or "^"
  48. - "right" or ">"
  49. When given as a single value, the value is used for
  50. all columns. The row headings (if given) will be
  51. right justified unless an explicit alignment is
  52. given for it and all other columns.
  53. [default: "left"]
  54. formats ...
  55. a list of format strings or functions that accept
  56. 3 arguments (entry, row number, col number) and
  57. return a string for the table entry. (If a function
  58. returns None then the _print method will be used.)
  59. wipe_zeros ...
  60. Don't show zeros in the table.
  61. [default: True]
  62. pad ...
  63. the string to use to indicate a missing value (e.g.
  64. elements that are None or those that are missing
  65. from the end of a row (i.e. any row that is shorter
  66. than the rest is assumed to have missing values).
  67. When None, nothing will be shown for values that
  68. are missing from the end of a row; values that are
  69. None, however, will be shown.
  70. [default: None]
  71. Examples
  72. ========
  73. >>> from sympy import TableForm, Symbol
  74. >>> TableForm([[5, 7], [4, 2], [10, 3]])
  75. 5 7
  76. 4 2
  77. 10 3
  78. >>> TableForm([list('.'*i) for i in range(1, 4)], headings='automatic')
  79. | 1 2 3
  80. ---------
  81. 1 | .
  82. 2 | . .
  83. 3 | . . .
  84. >>> TableForm([[Symbol('.'*(j if not i%2 else 1)) for i in range(3)]
  85. ... for j in range(4)], alignments='rcl')
  86. .
  87. . . .
  88. .. . ..
  89. ... . ...
  90. """
  91. from sympy.matrices.dense import Matrix
  92. # We only support 2D data. Check the consistency:
  93. if isinstance(data, Matrix):
  94. data = data.tolist()
  95. _h = len(data)
  96. # fill out any short lines
  97. pad = kwarg.get('pad', None)
  98. ok_None = False
  99. if pad is None:
  100. pad = " "
  101. ok_None = True
  102. pad = Symbol(pad)
  103. _w = max(len(line) for line in data)
  104. for i, line in enumerate(data):
  105. if len(line) != _w:
  106. line.extend([pad]*(_w - len(line)))
  107. for j, lj in enumerate(line):
  108. if lj is None:
  109. if not ok_None:
  110. lj = pad
  111. else:
  112. try:
  113. lj = S(lj)
  114. except SympifyError:
  115. lj = Symbol(str(lj))
  116. line[j] = lj
  117. data[i] = line
  118. _lines = Tuple(*[Tuple(*d) for d in data])
  119. headings = kwarg.get("headings", [None, None])
  120. if headings == "automatic":
  121. _headings = [range(1, _h + 1), range(1, _w + 1)]
  122. else:
  123. h1, h2 = headings
  124. if h1 == "automatic":
  125. h1 = range(1, _h + 1)
  126. if h2 == "automatic":
  127. h2 = range(1, _w + 1)
  128. _headings = [h1, h2]
  129. allow = ('l', 'r', 'c')
  130. alignments = kwarg.get("alignments", "l")
  131. def _std_align(a):
  132. a = a.strip().lower()
  133. if len(a) > 1:
  134. return {'left': 'l', 'right': 'r', 'center': 'c'}.get(a, a)
  135. else:
  136. return {'<': 'l', '>': 'r', '^': 'c'}.get(a, a)
  137. std_align = _std_align(alignments)
  138. if std_align in allow:
  139. _alignments = [std_align]*_w
  140. else:
  141. _alignments = []
  142. for a in alignments:
  143. std_align = _std_align(a)
  144. _alignments.append(std_align)
  145. if std_align not in ('l', 'r', 'c'):
  146. raise ValueError('alignment "%s" unrecognized' %
  147. alignments)
  148. if _headings[0] and len(_alignments) == _w + 1:
  149. _head_align = _alignments[0]
  150. _alignments = _alignments[1:]
  151. else:
  152. _head_align = 'r'
  153. if len(_alignments) != _w:
  154. raise ValueError(
  155. 'wrong number of alignments: expected %s but got %s' %
  156. (_w, len(_alignments)))
  157. _column_formats = kwarg.get("formats", [None]*_w)
  158. _wipe_zeros = kwarg.get("wipe_zeros", True)
  159. self._w = _w
  160. self._h = _h
  161. self._lines = _lines
  162. self._headings = _headings
  163. self._head_align = _head_align
  164. self._alignments = _alignments
  165. self._column_formats = _column_formats
  166. self._wipe_zeros = _wipe_zeros
  167. def __repr__(self):
  168. from .str import sstr
  169. return sstr(self, order=None)
  170. def __str__(self):
  171. from .str import sstr
  172. return sstr(self, order=None)
  173. def as_matrix(self):
  174. """Returns the data of the table in Matrix form.
  175. Examples
  176. ========
  177. >>> from sympy import TableForm
  178. >>> t = TableForm([[5, 7], [4, 2], [10, 3]], headings='automatic')
  179. >>> t
  180. | 1 2
  181. --------
  182. 1 | 5 7
  183. 2 | 4 2
  184. 3 | 10 3
  185. >>> t.as_matrix()
  186. Matrix([
  187. [ 5, 7],
  188. [ 4, 2],
  189. [10, 3]])
  190. """
  191. from sympy.matrices.dense import Matrix
  192. return Matrix(self._lines)
  193. def as_str(self):
  194. # XXX obsolete ?
  195. return str(self)
  196. def as_latex(self):
  197. from .latex import latex
  198. return latex(self)
  199. def _sympystr(self, p):
  200. """
  201. Returns the string representation of 'self'.
  202. Examples
  203. ========
  204. >>> from sympy import TableForm
  205. >>> t = TableForm([[5, 7], [4, 2], [10, 3]])
  206. >>> s = t.as_str()
  207. """
  208. column_widths = [0] * self._w
  209. lines = []
  210. for line in self._lines:
  211. new_line = []
  212. for i in range(self._w):
  213. # Format the item somehow if needed:
  214. s = str(line[i])
  215. if self._wipe_zeros and (s == "0"):
  216. s = " "
  217. w = len(s)
  218. if w > column_widths[i]:
  219. column_widths[i] = w
  220. new_line.append(s)
  221. lines.append(new_line)
  222. # Check heading:
  223. if self._headings[0]:
  224. self._headings[0] = [str(x) for x in self._headings[0]]
  225. _head_width = max([len(x) for x in self._headings[0]])
  226. if self._headings[1]:
  227. new_line = []
  228. for i in range(self._w):
  229. # Format the item somehow if needed:
  230. s = str(self._headings[1][i])
  231. w = len(s)
  232. if w > column_widths[i]:
  233. column_widths[i] = w
  234. new_line.append(s)
  235. self._headings[1] = new_line
  236. format_str = []
  237. def _align(align, w):
  238. return '%%%s%ss' % (
  239. ("-" if align == "l" else ""),
  240. str(w))
  241. format_str = [_align(align, w) for align, w in
  242. zip(self._alignments, column_widths)]
  243. if self._headings[0]:
  244. format_str.insert(0, _align(self._head_align, _head_width))
  245. format_str.insert(1, '|')
  246. format_str = ' '.join(format_str) + '\n'
  247. s = []
  248. if self._headings[1]:
  249. d = self._headings[1]
  250. if self._headings[0]:
  251. d = [""] + d
  252. first_line = format_str % tuple(d)
  253. s.append(first_line)
  254. s.append("-" * (len(first_line) - 1) + "\n")
  255. for i, line in enumerate(lines):
  256. d = [l if self._alignments[j] != 'c' else
  257. l.center(column_widths[j]) for j, l in enumerate(line)]
  258. if self._headings[0]:
  259. l = self._headings[0][i]
  260. l = (l if self._head_align != 'c' else
  261. l.center(_head_width))
  262. d = [l] + d
  263. s.append(format_str % tuple(d))
  264. return ''.join(s)[:-1] # don't include trailing newline
  265. def _latex(self, printer):
  266. """
  267. Returns the string representation of 'self'.
  268. """
  269. # Check heading:
  270. if self._headings[1]:
  271. new_line = []
  272. for i in range(self._w):
  273. # Format the item somehow if needed:
  274. new_line.append(str(self._headings[1][i]))
  275. self._headings[1] = new_line
  276. alignments = []
  277. if self._headings[0]:
  278. self._headings[0] = [str(x) for x in self._headings[0]]
  279. alignments = [self._head_align]
  280. alignments.extend(self._alignments)
  281. s = r"\begin{tabular}{" + " ".join(alignments) + "}\n"
  282. if self._headings[1]:
  283. d = self._headings[1]
  284. if self._headings[0]:
  285. d = [""] + d
  286. first_line = " & ".join(d) + r" \\" + "\n"
  287. s += first_line
  288. s += r"\hline" + "\n"
  289. for i, line in enumerate(self._lines):
  290. d = []
  291. for j, x in enumerate(line):
  292. if self._wipe_zeros and (x in (0, "0")):
  293. d.append(" ")
  294. continue
  295. f = self._column_formats[j]
  296. if f:
  297. if isinstance(f, FunctionType):
  298. v = f(x, i, j)
  299. if v is None:
  300. v = printer._print(x)
  301. else:
  302. v = f % x
  303. d.append(v)
  304. else:
  305. v = printer._print(x)
  306. d.append("$%s$" % v)
  307. if self._headings[0]:
  308. d = [self._headings[0][i]] + d
  309. s += " & ".join(d) + r" \\" + "\n"
  310. s += r"\end{tabular}"
  311. return s