Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/statsmodels/iolib/table.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Provides a simple table class. A SimpleTable is essentially
3a list of lists plus some formatting functionality.
5Dependencies: the Python 2.5+ standard library.
7Installation: just copy this module into your working directory (or
8 anywhere in your pythonpath).
10Basic use::
12 mydata = [[11,12],[21,22]] # data MUST be 2-dimensional
13 myheaders = [ "Column 1", "Column 2" ]
14 mystubs = [ "Row 1", "Row 2" ]
15 tbl = SimpleTable(mydata, myheaders, mystubs, title="Title")
16 print( tbl )
17 print( tbl.as_csv() )
19A SimpleTable is inherently (but not rigidly) rectangular.
20You should create it from a *rectangular* (2d!) iterable of data.
21Each item in your rectangular iterable will become the data
22of a single Cell. In principle, items can be any object,
23not just numbers and strings. However, default conversion
24during table production is by simple string interpolation.
25(So you cannot have a tuple as a data item *and* rely on
26the default conversion.)
28A SimpleTable allows only one column (the first) of stubs at
29initilization, concatenation of tables allows you to produce tables
30with interior stubs. (You can also assign the datatype 'stub' to the
31cells in any column, or use ``insert_stubs``.) A SimpleTable can be
32concatenated with another SimpleTable or extended by another
33SimpleTable. ::
35 table1.extend_right(table2)
36 table1.extend(table2)
39A SimpleTable can be initialized with `datatypes`: a list of ints that
40provide indexes into `data_fmts` and `data_aligns`. Each data cell is
41assigned a datatype, which will control formatting. If you do not
42specify the `datatypes` list, it will be set to ``range(ncols)`` where
43`ncols` is the number of columns in the data. (I.e., cells in a
44column have their own datatype.) This means that you can just specify
45`data_fmts` without bothering to provide a `datatypes` list. If
46``len(datatypes)<ncols`` then datatype assignment will cycle across a
47row. E.g., if you provide 10 columns of data with ``datatypes=[0,1]``
48then you will have 5 columns of datatype 0 and 5 columns of datatype
491, alternating. Correspoding to this specification, you should provide
50a list of two ``data_fmts`` and a list of two ``data_aligns``.
52Cells can be assigned labels as their `datatype` attribute.
53You can then provide a format for that lable.
54Us the SimpleTable's `label_cells` method to do this. ::
56 def mylabeller(cell):
57 if cell.data is np.nan:
58 return 'missing'
60 mytable.label_cells(mylabeller)
61 print(mytable.as_text(missing='-'))
64Potential problems for Python 3
65-------------------------------
67- Calls ``next`` instead of ``__next__``.
68 The 2to3 tool should handle that no problem.
69 (We will switch to the `next` function if 2.5 support is ever dropped.)
70- Let me know if you find other problems.
72:contact: alan dot isaac at gmail dot com
73:requires: Python 2.5.1+
74:note: current version
75:note: HTML data format currently specifies tags
76:todo: support a bit more of http://www.oasis-open.org/specs/tr9503.html
77:todo: add labels2formatters method, that associates a cell formatter with a
78 datatype
79:todo: add colspan support to Cell
80:since: 2008-12-21
81:change: 2010-05-02 eliminate newlines that came before and after table
82:change: 2010-05-06 add `label_cells` to `SimpleTable`
83"""
85from statsmodels.compat.python import lmap, lrange, iteritems
87from itertools import cycle, zip_longest
88import csv
91def csv2st(csvfile, headers=False, stubs=False, title=None):
92 """Return SimpleTable instance,
93 created from the data in `csvfile`,
94 which is in comma separated values format.
95 The first row may contain headers: set headers=True.
96 The first column may contain stubs: set stubs=True.
97 Can also supply headers and stubs as tuples of strings.
98 """
99 rows = list()
100 with open(csvfile, 'r') as fh:
101 reader = csv.reader(fh)
102 if headers is True:
103 headers = next(reader)
104 elif headers is False:
105 headers = ()
106 if stubs is True:
107 stubs = list()
108 for row in reader:
109 if row:
110 stubs.append(row[0])
111 rows.append(row[1:])
112 else: # no stubs, or stubs provided
113 for row in reader:
114 if row:
115 rows.append(row)
116 if stubs is False:
117 stubs = ()
118 nrows = len(rows)
119 ncols = len(rows[0])
120 if any(nrows != ncols for row in rows):
121 raise IOError('All rows of CSV file must have same length.')
122 return SimpleTable(data=rows, headers=headers, stubs=stubs)
125class SimpleTable(list):
126 """Produce a simple ASCII, CSV, HTML, or LaTeX table from a
127 *rectangular* (2d!) array of data, not necessarily numerical.
128 Directly supports at most one header row,
129 which should be the length of data[0].
130 Directly supports at most one stubs column,
131 which must be the length of data.
132 (But see `insert_stubs` method.)
133 See globals `default_txt_fmt`, `default_csv_fmt`, `default_html_fmt`,
134 and `default_latex_fmt` for formatting options.
136 Sample uses::
138 mydata = [[11,12],[21,22]] # data MUST be 2-dimensional
139 myheaders = [ "Column 1", "Column 2" ]
140 mystubs = [ "Row 1", "Row 2" ]
141 tbl = text.SimpleTable(mydata, myheaders, mystubs, title="Title")
142 print( tbl )
143 print( tbl.as_html() )
144 # set column specific data formatting
145 tbl = text.SimpleTable(mydata, myheaders, mystubs,
146 data_fmts=["%3.2f","%d"])
147 print( tbl.as_csv() )
148 with open('c:/temp/temp.tex','w') as fh:
149 fh.write( tbl.as_latex_tabular() )
150 """
151 def __init__(self, data, headers=None, stubs=None, title='',
152 datatypes=None, csv_fmt=None, txt_fmt=None, ltx_fmt=None,
153 html_fmt=None, celltype=None, rowtype=None, **fmt_dict):
154 """
155 Parameters
156 ----------
157 data : list of lists or 2d array (not matrix!)
158 R rows by K columns of table elements
159 headers : list (or tuple) of str
160 sequence of K strings, one per header
161 stubs : list (or tuple) of str
162 sequence of R strings, one per stub
163 title : str
164 title of the table
165 datatypes : list of int
166 indexes to `data_fmts`
167 txt_fmt : dict
168 text formatting options
169 ltx_fmt : dict
170 latex formatting options
171 csv_fmt : dict
172 csv formatting options
173 hmtl_fmt : dict
174 hmtl formatting options
175 celltype : class
176 the cell class for the table (default: Cell)
177 rowtype : class
178 the row class for the table (default: Row)
179 fmt_dict : dict
180 general formatting options
181 """
182 self.title = title
183 self._datatypes = datatypes or lrange(len(data[0]))
184 # start with default formatting
185 self._txt_fmt = default_txt_fmt.copy()
186 self._latex_fmt = default_latex_fmt.copy()
187 self._csv_fmt = default_csv_fmt.copy()
188 self._html_fmt = default_html_fmt.copy()
189 # substitute any general user specified formatting
190 # :note: these will be overridden by output specific arguments
191 self._csv_fmt.update(fmt_dict)
192 self._txt_fmt.update(fmt_dict)
193 self._latex_fmt.update(fmt_dict)
194 self._html_fmt.update(fmt_dict)
195 # substitute any output-type specific formatting
196 self._csv_fmt.update(csv_fmt or dict())
197 self._txt_fmt.update(txt_fmt or dict())
198 self._latex_fmt.update(ltx_fmt or dict())
199 self._html_fmt.update(html_fmt or dict())
200 self.output_formats = dict(
201 txt=self._txt_fmt,
202 csv=self._csv_fmt,
203 html=self._html_fmt,
204 latex=self._latex_fmt
205 )
206 self._Cell = celltype or Cell
207 self._Row = rowtype or Row
208 rows = self._data2rows(data) # a list of Row instances
209 list.__init__(self, rows)
210 self._add_headers_stubs(headers, stubs)
211 self._colwidths = dict()
213 def __str__(self):
214 return self.as_text()
216 def __repr__(self):
217 return str(type(self))
219 def _repr_html_(self, **fmt_dict):
220 return self.as_html(**fmt_dict)
222 def _add_headers_stubs(self, headers, stubs):
223 """Return None. Adds headers and stubs to table,
224 if these were provided at initialization.
225 Parameters
226 ----------
227 headers : list[str]
228 K strings, where K is number of columns
229 stubs : list[str]
230 R strings, where R is number of non-header rows
232 :note: a header row does not receive a stub!
233 """
234 if headers:
235 self.insert_header_row(0, headers, dec_below='header_dec_below')
236 if stubs:
237 self.insert_stubs(0, stubs)
239 def insert(self, idx, row, datatype=None):
240 """Return None. Insert a row into a table.
241 """
242 if datatype is None:
243 try:
244 datatype = row.datatype
245 except AttributeError:
246 pass
247 row = self._Row(row, datatype=datatype, table=self)
248 list.insert(self, idx, row)
250 def insert_header_row(self, rownum, headers, dec_below='header_dec_below'):
251 """Return None. Insert a row of headers,
252 where ``headers`` is a sequence of strings.
253 (The strings may contain newlines, to indicated multiline headers.)
254 """
255 header_rows = [header.split('\n') for header in headers]
256 # rows in reverse order
257 rows = list(zip_longest(*header_rows, **dict(fillvalue='')))
258 rows.reverse()
259 for i, row in enumerate(rows):
260 self.insert(rownum, row, datatype='header')
261 if i == 0:
262 self[rownum].dec_below = dec_below
263 else:
264 self[rownum].dec_below = None
266 def insert_stubs(self, loc, stubs):
267 """Return None. Insert column of stubs at column `loc`.
268 If there is a header row, it gets an empty cell.
269 So ``len(stubs)`` should equal the number of non-header rows.
270 """
271 _Cell = self._Cell
272 stubs = iter(stubs)
273 for row in self:
274 if row.datatype == 'header':
275 empty_cell = _Cell('', datatype='empty')
276 row.insert(loc, empty_cell)
277 else:
278 try:
279 row.insert_stub(loc, next(stubs))
280 except StopIteration:
281 raise ValueError('length of stubs must match table length')
283 def _data2rows(self, raw_data):
284 """Return list of Row,
285 the raw data as rows of cells.
286 """
288 _Cell = self._Cell
289 _Row = self._Row
290 rows = []
291 for datarow in raw_data:
292 dtypes = cycle(self._datatypes)
293 newrow = _Row(datarow, datatype='data', table=self, celltype=_Cell)
294 for cell in newrow:
295 cell.datatype = next(dtypes)
296 cell.row = newrow # a cell knows its row
297 rows.append(newrow)
299 return rows
301 def pad(self, s, width, align):
302 """DEPRECATED: just use the pad function"""
303 return pad(s, width, align)
305 def _get_colwidths(self, output_format, **fmt_dict):
306 """Return list, the calculated widths of each column."""
307 output_format = get_output_format(output_format)
308 fmt = self.output_formats[output_format].copy()
309 fmt.update(fmt_dict)
310 ncols = max(len(row) for row in self)
311 request = fmt.get('colwidths')
312 if request == 0: # assume no extra space desired (e.g, CSV)
313 return [0] * ncols
314 elif request is None: # assume no extra space desired (e.g, CSV)
315 request = [0] * ncols
316 elif isinstance(request, int):
317 request = [request] * ncols
318 elif len(request) < ncols:
319 request = [request[i % len(request)] for i in range(ncols)]
320 min_widths = []
321 for col in zip(*self):
322 maxwidth = max(len(c.format(0, output_format, **fmt)) for c in col)
323 min_widths.append(maxwidth)
324 result = lmap(max, min_widths, request)
325 return result
327 def get_colwidths(self, output_format, **fmt_dict):
328 """Return list, the widths of each column."""
329 call_args = [output_format]
330 for k, v in sorted(iteritems(fmt_dict)):
331 if isinstance(v, list):
332 call_args.append((k, tuple(v)))
333 elif isinstance(v, dict):
334 call_args.append((k, tuple(sorted(iteritems(v)))))
335 else:
336 call_args.append((k, v))
337 key = tuple(call_args)
338 try:
339 return self._colwidths[key]
340 except KeyError:
341 self._colwidths[key] = self._get_colwidths(output_format,
342 **fmt_dict)
343 return self._colwidths[key]
345 def _get_fmt(self, output_format, **fmt_dict):
346 """Return dict, the formatting options.
347 """
348 output_format = get_output_format(output_format)
349 # first get the default formatting
350 try:
351 fmt = self.output_formats[output_format].copy()
352 except KeyError:
353 raise ValueError('Unknown format: %s' % output_format)
354 # then, add formatting specific to this call
355 fmt.update(fmt_dict)
356 return fmt
358 def as_csv(self, **fmt_dict):
359 """Return string, the table in CSV format.
360 Currently only supports comma separator."""
361 # fetch the format, which may just be default_csv_format
362 fmt = self._get_fmt('csv', **fmt_dict)
363 return self.as_text(**fmt)
365 def as_text(self, **fmt_dict):
366 """Return string, the table as text."""
367 # fetch the text format, override with fmt_dict
368 fmt = self._get_fmt('txt', **fmt_dict)
369 # get rows formatted as strings
370 formatted_rows = [row.as_string('text', **fmt) for row in self]
371 rowlen = len(formatted_rows[-1]) # do not use header row
373 # place decoration above the table body, if desired
374 table_dec_above = fmt.get('table_dec_above', '=')
375 if table_dec_above:
376 formatted_rows.insert(0, table_dec_above * rowlen)
377 # next place a title at the very top, if desired
378 # :note: user can include a newlines at end of title if desired
379 title = self.title
380 if title:
381 title = pad(self.title, rowlen, fmt.get('title_align', 'c'))
382 formatted_rows.insert(0, title)
383 # add decoration below the table, if desired
384 table_dec_below = fmt.get('table_dec_below', '-')
385 if table_dec_below:
386 formatted_rows.append(table_dec_below * rowlen)
387 return '\n'.join(formatted_rows)
389 def as_html(self, **fmt_dict):
390 """Return string.
391 This is the default formatter for HTML tables.
392 An HTML table formatter must accept as arguments
393 a table and a format dictionary.
394 """
395 # fetch the text format, override with fmt_dict
396 fmt = self._get_fmt('html', **fmt_dict)
397 formatted_rows = ['<table class="simpletable">']
398 if self.title:
399 title = '<caption>%s</caption>' % self.title
400 formatted_rows.append(title)
401 formatted_rows.extend(row.as_string('html', **fmt) for row in self)
402 formatted_rows.append('</table>')
403 return '\n'.join(formatted_rows)
405 def as_latex_tabular(self, center=True, **fmt_dict):
406 '''Return string, the table as a LaTeX tabular environment.
407 Note: will require the booktabs package.'''
408 # fetch the text format, override with fmt_dict
409 fmt = self._get_fmt('latex', **fmt_dict)
411 formatted_rows = []
412 if center:
413 formatted_rows.append(r'\begin{center}')
415 table_dec_above = fmt['table_dec_above'] or ''
416 table_dec_below = fmt['table_dec_below'] or ''
418 prev_aligns = None
419 last = None
420 for row in self + [last]:
421 if row == last:
422 aligns = None
423 else:
424 aligns = row.get_aligns('latex', **fmt)
426 if aligns != prev_aligns:
427 # When the number/type of columns changes...
428 if prev_aligns:
429 # ... if there is a tabular to close, close it...
430 formatted_rows.append(table_dec_below)
431 formatted_rows.append(r'\end{tabular}')
432 if aligns:
433 # ... and if there are more lines, open a new one:
434 formatted_rows.append(r'\begin{tabular}{%s}' % aligns)
435 if not prev_aligns:
436 # (with a nice line if it's the top of the whole table)
437 formatted_rows.append(table_dec_above)
438 if row != last:
439 formatted_rows.append(
440 row.as_string(output_format='latex', **fmt))
441 prev_aligns = aligns
442 # tabular does not support caption, but make it available for
443 # figure environment
444 if self.title:
445 title = r'%%\caption{%s}' % self.title
446 formatted_rows.append(title)
447 if center:
448 formatted_rows.append(r'\end{center}')
450 # Replace $$ due to bug in GH 5444
451 return '\n'.join(formatted_rows).replace('$$', ' ')
453 def extend_right(self, table):
454 """Return None.
455 Extend each row of `self` with corresponding row of `table`.
456 Does **not** import formatting from ``table``.
457 This generally makes sense only if the two tables have
458 the same number of rows, but that is not enforced.
459 :note: To extend append a table below, just use `extend`,
460 which is the ordinary list method. This generally makes sense
461 only if the two tables have the same number of columns,
462 but that is not enforced.
463 """
464 for row1, row2 in zip(self, table):
465 row1.extend(row2)
467 def label_cells(self, func):
468 """Return None. Labels cells based on `func`.
469 If ``func(cell) is None`` then its datatype is
470 not changed; otherwise it is set to ``func(cell)``.
471 """
472 for row in self:
473 for cell in row:
474 label = func(cell)
475 if label is not None:
476 cell.datatype = label
478 @property
479 def data(self):
480 return [row.data for row in self]
483def pad(s, width, align):
484 """Return string padded with spaces,
485 based on alignment parameter."""
486 if align == 'l':
487 s = s.ljust(width)
488 elif align == 'r':
489 s = s.rjust(width)
490 else:
491 s = s.center(width)
492 return s
495class Row(list):
496 """Provides a table row as a list of cells.
497 A row can belong to a SimpleTable, but does not have to.
498 """
499 def __init__(self, seq, datatype='data', table=None, celltype=None,
500 dec_below='row_dec_below', **fmt_dict):
501 """
502 Parameters
503 ----------
504 seq : sequence of data or cells
505 table : SimpleTable
506 datatype : str ('data' or 'header')
507 dec_below : str
508 (e.g., 'header_dec_below' or 'row_dec_below')
509 decoration tag, identifies the decoration to go below the row.
510 (Decoration is repeated as needed for text formats.)
511 """
512 self.datatype = datatype
513 self.table = table
514 if celltype is None:
515 if table is None:
516 celltype = Cell
517 else:
518 celltype = table._Cell
519 self._Cell = celltype
520 self._fmt = fmt_dict
521 self.special_fmts = dict() # special formatting for any output format
522 self.dec_below = dec_below
523 list.__init__(self, (celltype(cell, row=self) for cell in seq))
525 def add_format(self, output_format, **fmt_dict):
526 """
527 Return None. Adds row-instance specific formatting
528 for the specified output format.
529 Example: myrow.add_format('txt', row_dec_below='+-')
530 """
531 output_format = get_output_format(output_format)
532 if output_format not in self.special_fmts:
533 self.special_fmts[output_format] = dict()
534 self.special_fmts[output_format].update(fmt_dict)
536 def insert_stub(self, loc, stub):
537 """Return None. Inserts a stub cell
538 in the row at `loc`.
539 """
540 _Cell = self._Cell
541 if not isinstance(stub, _Cell):
542 stub = stub
543 stub = _Cell(stub, datatype='stub', row=self)
544 self.insert(loc, stub)
546 def _get_fmt(self, output_format, **fmt_dict):
547 """Return dict, the formatting options.
548 """
549 output_format = get_output_format(output_format)
550 # first get the default formatting
551 try:
552 fmt = default_fmts[output_format].copy()
553 except KeyError:
554 raise ValueError('Unknown format: %s' % output_format)
555 # second get table specific formatting (if possible)
556 try:
557 fmt.update(self.table.output_formats[output_format])
558 except AttributeError:
559 pass
560 # finally, add formatting for this row and this call
561 fmt.update(self._fmt)
562 fmt.update(fmt_dict)
563 special_fmt = self.special_fmts.get(output_format, None)
564 if special_fmt is not None:
565 fmt.update(special_fmt)
566 return fmt
568 def get_aligns(self, output_format, **fmt_dict):
569 """Return string, sequence of column alignments.
570 Ensure comformable data_aligns in `fmt_dict`."""
571 fmt = self._get_fmt(output_format, **fmt_dict)
572 return ''.join(cell.alignment(output_format, **fmt) for cell in self)
574 def as_string(self, output_format='txt', **fmt_dict):
575 """Return string: the formatted row.
576 This is the default formatter for rows.
577 Override this to get different formatting.
578 A row formatter must accept as arguments
579 a row (self) and an output format,
580 one of ('html', 'txt', 'csv', 'latex').
581 """
582 fmt = self._get_fmt(output_format, **fmt_dict)
584 # get column widths
585 try:
586 colwidths = self.table.get_colwidths(output_format, **fmt)
587 except AttributeError:
588 colwidths = fmt.get('colwidths')
589 if colwidths is None:
590 colwidths = (0,) * len(self)
592 colsep = fmt['colsep']
593 row_pre = fmt.get('row_pre', '')
594 row_post = fmt.get('row_post', '')
595 formatted_cells = []
596 for cell, width in zip(self, colwidths):
597 content = cell.format(width, output_format=output_format, **fmt)
598 formatted_cells.append(content)
599 formatted_row = row_pre + colsep.join(formatted_cells) + row_post
600 formatted_row = self._decorate_below(formatted_row, output_format,
601 **fmt)
602 return formatted_row
604 def _decorate_below(self, row_as_string, output_format, **fmt_dict):
605 """This really only makes sense for the text and latex output formats.
606 """
607 dec_below = fmt_dict.get(self.dec_below, None)
608 if dec_below is None:
609 result = row_as_string
610 else:
611 output_format = get_output_format(output_format)
612 if output_format == 'txt':
613 row0len = len(row_as_string)
614 dec_len = len(dec_below)
615 repeat, addon = divmod(row0len, dec_len)
616 result = row_as_string + "\n" + (dec_below * repeat +
617 dec_below[:addon])
618 elif output_format == 'latex':
619 result = row_as_string + "\n" + dec_below
620 else:
621 raise ValueError("I cannot decorate a %s header." %
622 output_format)
623 return result
625 @property
626 def data(self):
627 return [cell.data for cell in self]
630class Cell(object):
631 """Provides a table cell.
632 A cell can belong to a Row, but does not have to.
633 """
634 def __init__(self, data='', datatype=None, row=None, **fmt_dict):
635 if isinstance(data, Cell):
636 # might have passed a Cell instance
637 self.data = data.data
638 self._datatype = data.datatype
639 self._fmt = data._fmt
640 else:
641 self.data = data
642 self._datatype = datatype
643 self._fmt = dict()
644 self._fmt.update(fmt_dict)
645 self.row = row
647 def __str__(self):
648 return '%s' % self.data
650 def _get_fmt(self, output_format, **fmt_dict):
651 """Return dict, the formatting options.
652 """
653 output_format = get_output_format(output_format)
654 # first get the default formatting
655 try:
656 fmt = default_fmts[output_format].copy()
657 except KeyError:
658 raise ValueError('Unknown format: %s' % output_format)
659 # then get any table specific formtting
660 try:
661 fmt.update(self.row.table.output_formats[output_format])
662 except AttributeError:
663 pass
664 # then get any row specific formtting
665 try:
666 fmt.update(self.row._fmt)
667 except AttributeError:
668 pass
669 # finally add formatting for this instance and call
670 fmt.update(self._fmt)
671 fmt.update(fmt_dict)
672 return fmt
674 def alignment(self, output_format, **fmt_dict):
675 fmt = self._get_fmt(output_format, **fmt_dict)
676 datatype = self.datatype
677 data_aligns = fmt.get('data_aligns', 'c')
678 if isinstance(datatype, int):
679 align = data_aligns[datatype % len(data_aligns)]
680 elif datatype == 'stub':
681 # still support deprecated `stubs_align`
682 align = fmt.get('stubs_align') or fmt.get('stub_align', 'l')
683 elif datatype in fmt:
684 label_align = '%s_align' % datatype
685 align = fmt.get(label_align, 'c')
686 else:
687 raise ValueError('Unknown cell datatype: %s' % datatype)
688 return align
690 @staticmethod
691 def _latex_escape(data, fmt, output_format):
692 if output_format != 'latex':
693 return data
694 if "replacements" in fmt:
695 if isinstance(data, str):
696 for repl in sorted(fmt["replacements"]):
697 data = data.replace(repl, fmt["replacements"][repl])
698 return data
700 def format(self, width, output_format='txt', **fmt_dict):
701 """Return string.
702 This is the default formatter for cells.
703 Override this to get different formating.
704 A cell formatter must accept as arguments
705 a cell (self) and an output format,
706 one of ('html', 'txt', 'csv', 'latex').
707 It will generally respond to the datatype,
708 one of (int, 'header', 'stub').
709 """
710 fmt = self._get_fmt(output_format, **fmt_dict)
712 data = self.data
713 datatype = self.datatype
714 data_fmts = fmt.get('data_fmts')
715 if data_fmts is None:
716 # chk allow for deprecated use of data_fmt
717 data_fmt = fmt.get('data_fmt')
718 if data_fmt is None:
719 data_fmt = '%s'
720 data_fmts = [data_fmt]
721 if isinstance(datatype, int):
722 datatype = datatype % len(data_fmts) # constrain to indexes
723 content = data_fmts[datatype] % (data,)
724 if datatype == 0:
725 content = self._latex_escape(content, fmt, output_format)
726 elif datatype in fmt:
727 data = self._latex_escape(data, fmt, output_format)
729 dfmt = fmt.get(datatype)
730 try:
731 content = dfmt % (data,)
732 except TypeError: # dfmt is not a substitution string
733 content = dfmt
734 else:
735 raise ValueError('Unknown cell datatype: %s' % datatype)
736 align = self.alignment(output_format, **fmt)
737 return pad(content, width, align)
739 def get_datatype(self):
740 if self._datatype is None:
741 dtype = self.row.datatype
742 else:
743 dtype = self._datatype
744 return dtype
746 def set_datatype(self, val):
747 # TODO: add checking
748 self._datatype = val
749 datatype = property(get_datatype, set_datatype)
752# begin: default formats for SimpleTable
753""" Some formatting suggestions:
755- if you want rows to have no extra spacing,
756 set colwidths=0 and colsep=''.
757 (Naturally the columns will not align.)
758- if you want rows to have minimal extra spacing,
759 set colwidths=1. The columns will align.
760- to get consistent formatting, you should leave
761 all field width handling to SimpleTable:
762 use 0 as the field width in data_fmts. E.g., ::
764 data_fmts = ["%#0.6g","%#0.6g","%#0.4g","%#0.4g"],
765 colwidths = 14,
766 data_aligns = "r",
767"""
768default_txt_fmt = dict(
769 fmt='txt',
770 # basic table formatting
771 table_dec_above='=',
772 table_dec_below='-',
773 title_align='c',
774 # basic row formatting
775 row_pre='',
776 row_post='',
777 header_dec_below='-',
778 row_dec_below=None,
779 colwidths=None,
780 colsep=' ',
781 data_aligns="r", # GH 1477
782 # data formats
783 # data_fmt="%s", #deprecated; use data_fmts
784 data_fmts=["%s"],
785 # labeled alignments
786 # stubs_align='l', #deprecated; use data_fmts
787 stub_align='l',
788 header_align='c',
789 # labeled formats
790 header_fmt='%s', # deprecated; just use 'header'
791 stub_fmt='%s', # deprecated; just use 'stub'
792 header='%s',
793 stub='%s',
794 empty_cell='', # deprecated; just use 'empty'
795 empty='',
796 missing='--',
797)
799default_csv_fmt = dict(
800 fmt='csv',
801 table_dec_above=None, # '',
802 table_dec_below=None, # '',
803 # basic row formatting
804 row_pre='',
805 row_post='',
806 header_dec_below=None, # '',
807 row_dec_below=None,
808 title_align='',
809 data_aligns="l",
810 colwidths=None,
811 colsep=',',
812 # data formats
813 data_fmt='%s', # deprecated; use data_fmts
814 data_fmts=['%s'],
815 # labeled alignments
816 # stubs_align='l', # deprecated; use data_fmts
817 stub_align="l",
818 header_align='c',
819 # labeled formats
820 header_fmt='"%s"', # deprecated; just use 'header'
821 stub_fmt='"%s"', # deprecated; just use 'stub'
822 empty_cell='', # deprecated; just use 'empty'
823 header='%s',
824 stub='%s',
825 empty='',
826 missing='--',
827)
829default_html_fmt = dict(
830 # basic table formatting
831 table_dec_above=None,
832 table_dec_below=None,
833 header_dec_below=None,
834 row_dec_below=None,
835 title_align='c',
836 # basic row formatting
837 colwidths=None,
838 colsep=' ',
839 row_pre='<tr>\n ',
840 row_post='\n</tr>',
841 data_aligns="c",
842 # data formats
843 data_fmts=['<td>%s</td>'],
844 data_fmt="<td>%s</td>", # deprecated; use data_fmts
845 # labeled alignments
846 # stubs_align='l', #deprecated; use data_fmts
847 stub_align='l',
848 header_align='c',
849 # labeled formats
850 header_fmt='<th>%s</th>', # deprecated; just use `header`
851 stub_fmt='<th>%s</th>', # deprecated; just use `stub`
852 empty_cell='<td></td>', # deprecated; just use `empty`
853 header='<th>%s</th>',
854 stub='<th>%s</th>',
855 empty='<td></td>',
856 missing='<td>--</td>',
857)
859default_latex_fmt = dict(
860 fmt='ltx',
861 # basic table formatting
862 table_dec_above=r'\toprule',
863 table_dec_below=r'\bottomrule',
864 header_dec_below=r'\midrule',
865 row_dec_below=None,
866 strip_backslash=True, # NotImplemented
867 # row formatting
868 row_post=r' \\',
869 data_aligns='c',
870 colwidths=None,
871 colsep=' & ',
872 # data formats
873 data_fmts=['%s'],
874 data_fmt='%s', # deprecated; use data_fmts
875 # labeled alignments
876 # stubs_align='l', # deprecated; use data_fmts
877 stub_align='l',
878 header_align='c',
879 empty_align='l',
880 # labeled formats
881 header_fmt=r'\textbf{%s}', # deprecated; just use 'header'
882 stub_fmt=r'\textbf{%s}', # deprecated; just use 'stub'
883 empty_cell='', # deprecated; just use 'empty'
884 header=r'\textbf{%s}',
885 stub=r'\textbf{%s}',
886 empty='',
887 missing='--',
888 # replacements will be processed in lexicographical order
889 replacements={"#": r"\#",
890 "$": r"\$",
891 "%": r"\%",
892 "&": r"\&",
893 ">": r"$>$",
894 "_": r"\_",
895 "|": r"$|$"}
896)
898default_fmts = dict(
899 html=default_html_fmt,
900 txt=default_txt_fmt,
901 latex=default_latex_fmt,
902 csv=default_csv_fmt
903)
904output_format_translations = dict(
905 htm='html',
906 text='txt',
907 ltx='latex'
908)
911def get_output_format(output_format):
912 if output_format not in ('html', 'txt', 'latex', 'csv'):
913 try:
914 output_format = output_format_translations[output_format]
915 except KeyError:
916 raise ValueError('unknown output format %s' % output_format)
917 return output_format