armi.utils.tabulate module
Pretty-print tabular data.
This file started out as the MIT-licensed “tabulate”. Though we have made, and will continue to make, many arbitrary changes as we need. Thanks to the tabulate team.
https://github.com/astanin/python-tabulate
Usage
The module provides just one function, tabulate, which takes a list of lists or other tabular data type as the first argument, and outputs anicely-formatted plain-text table:
>>> from armi.utils.tabulate import tabulate
>>> table = [["Sun",696000,1989100000],["Earth",6371,5973.6],
... ["Moon",1737,73.5],["Mars",3390,641.85]]
>>> print(tabulate(table))
----- ------ -------------
Sun 696000 1.9891e+09
Earth 6371 5973.6
Moon 1737 73.5
Mars 3390 641.85
----- ------ -------------
The following tabular data types are supported:
list of lists or another iterable of iterables
list or another iterable of dicts (keys as columns)
dict of iterables (keys as columns)
list of dataclasses (field names as columns)
two-dimensional NumPy array
NumPy record arrays (names as columns)
Table headers
To print nice column headers, supply the second argument (headers):
headers can be an explicit list of column headers
if headers=”firstrow”, then the first row of data is used
if headers=”keys”, then dictionary keys or column indices are used
Otherwise a headerless table is produced.
If the number of headers is less than the number of columns, they are supposed to be names of the last columns. This is consistent with the plain-text format of R:
>>> print(tabulate([["sex","age"],["Alice","F",24],["Bob","M",19]],
... headers="firstrow"))
sex age
----- ----- -----
Alice F 24
Bob M 19
Column and Headers alignment
tabulate tries to detect column types automatically, and aligns the values properly. By default it aligns decimal points of the numbers (or flushes integer numbers to the right), and flushes everything else to the left. Possible column alignments (numAlign, strAlign) are: “right”, “center”, “left”, “decimal” (only for numAlign), and None (to disable alignment).
- colGlobalAlign allows for global alignment of columns, before any specific override from
colAlign. Possible values are: None (defaults according to coltype), “right”, “center”, “decimal”, “left”.
- colAlign allows for column-wise override starting from left-most column. Possible values are:
“global” (no override), “right”, “center”, “decimal”, “left”.
- headersGlobalAlign allows for global headers alignment, before any specific override from
headersAlign. Possible values are: None (follow columns alignment), “right”, “center”, “left”.
- headersAlign allows for header-wise override starting from left-most given header. Possible
values are: “global” (no override), “same” (follow column alignment), “right”, “center”, “left”.
Note on intended behaviour: If there is no data, any column alignment argument is ignored. Hence, in this case, header alignment cannot be inferred from column alignment.
Table formats
intFmt is a format specification used for columns which contain numeric data without a decimal point. This can also be a list or tuple of format strings, one per column.
floatFmt is a format specification used for columns which contain numeric data with a decimal point. This can also be a list or tuple of format strings, one per column.
None values are replaced with a missingVal string (like floatFmt, this can also be a list of values for different columns):
>>> print(tabulate([["spam", 1, None],
... ["eggs", 42, 3.14],
... ["other", None, 2.7]], missingVal="?"))
----- -- ----
spam 1 ?
eggs 42 3.14
other ? 2.7
----- -- ----
Various plain-text table formats (tableFmt) are supported: ‘plain’, ‘simple’, ‘grid’, ‘rst’, and tsv. Variable tabulateFormats contains the list of currently supported formats.
“plain” format doesn’t use any pseudographics to draw tables, it separates columns with a double space:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "plain"))
strings numbers
spam 41.9999
eggs 451
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tableFmt="plain"))
spam 41.9999
eggs 451
“simple” format is like Pandoc simple_tables:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "simple"))
strings numbers
--------- ---------
spam 41.9999
eggs 451
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tableFmt="simple"))
---- --------
spam 41.9999
eggs 451
---- --------
“grid” is similar to tables produced by Emacs table.el package or Pandoc grid_tables:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "grid"))
+-----------+-----------+
| strings | numbers |
+===========+===========+
| spam | 41.9999 |
+-----------+-----------+
| eggs | 451 |
+-----------+-----------+
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tableFmt="grid"))
+------+----------+
| spam | 41.9999 |
+------+----------+
| eggs | 451 |
+------+----------+
“rst” is like a simple table format from reStructuredText; please note that reStructuredText accepts also “grid” tables:
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]],
... ["strings", "numbers"], "rst"))
========= =========
strings numbers
========= =========
spam 41.9999
eggs 451
========= =========
>>> print(tabulate([["spam", 41.9999], ["eggs", "451.0"]], tableFmt="rst"))
==== ========
spam 41.9999
eggs 451
==== ========
Number parsing
By default, anything which can be parsed as a number is a number. This ensures numbers represented as strings are aligned properly. This can lead to weird results for particular strings such as specific git SHAs e.g. “42992e1” will be parsed into the number 429920 and aligned as such.
To completely disable number parsing (and alignment), use disableNumParse=True. For more fine grained control, a list column indices is used to disable number parsing only on those columns e.g. disableNumParse=[0, 2] would disable number parsing only on the first and third columns.
Column Widths and Auto Line Wrapping
Tabulate will, by default, set the width of each column to the length of the longest element in that column. However, in situations where fields are expected to reasonably be too long to look good as a single line, tabulate can help automate word wrapping long fields for you. Use the parameter maxcolwidth to provide a list of maximal column widths:
>>> print(tabulate( \
[('1', 'John Smith', \
'This is a rather long description that might look better if it is wrapped a bit')], \
headers=("Issue Id", "Author", "Description"), \
maxColWidths=[None, None, 30], \
tableFmt="grid" \
))
+------------+------------+-------------------------------+
| Issue Id | Author | Description |
+============+============+===============================+
| 1 | John Smith | This is a rather long |
| | | description that might look |
| | | better if it is wrapped a bit |
+------------+------------+-------------------------------+
Header column width can be specified in a similar way using maxheadercolwidth.
- armi.utils.tabulate.tabulate(data, headers=(), tableFmt='simple', floatFmt='g', intFmt='', numAlign='default', strAlign='default', missingVal='', showIndex='default', disableNumParse=False, colGlobalAlign=None, colAlign=None, maxColWidths=None, headersGlobalAlign=None, headersAlign=None, rowAlign=None, maxHeaderColWidths=None)[source]
Format a fixed width table for pretty printing.
- Parameters:
data (object) – The tabular data you want to print. This can be a list-of-lists/iterables, dict-of-lists/ iterables, 2D numpy arrays, or list of dataclasses.
headers=() – Nice column names. If this is “firstrow”, the first row of the data will be used. If it is “keys”m, then dictionary keys or column indices are used.
optional – Nice column names. If this is “firstrow”, the first row of the data will be used. If it is “keys”m, then dictionary keys or column indices are used.
tableFmt (str, optional) – There are custom table formats defined in this file, and you can choose between them with this string: “armi”, “simple”, “plain”, “grid”, “github”, “pretty”, “psql”, “rst”, “tsv”.
floatFmt (str, optional) – A format specification used for columns which contain numeric data with a decimal point. This can also be a list or tuple of format strings, one per column.
intFmt (str, optional) – A format specification used for columns which contain numeric data without a decimal point. This can also be a list or tuple of format strings, one per column.
numAlign (str, optional) – Specially align numbers, options: “right”, “center”, “left”, “decimal”.
strAlign (str, optional) – Specially align strings, options: “right”, “center”, “left”.
missingVal (str, optional) – None values are replaced with a missingVal string.
showIndex (str, optional) – Show these rows of data. If “always”, show row indices for all types of data. If “never”, don’t show row indices for all types of data. If showIndex is an iterable, show its values..
disableNumParse (bool, optional) – To disable number parsing (and alignment), use disableNumParse=True. For more fine grained control, [0, 2] would disable number parsing on the first and third columns.
colGlobalAlign (str, optional) – Allows for global alignment of columns, before any specific override from colAlign. Possible values are: None, “right”, “center”, “decimal”, “left”.
colAlign (str, optional) – Allows for column-wise override starting from left-most column. Possible values are: “global” (no override), “right”, “center”, “decimal”, “left”.
maxColWidths (list, optional) – A list of the maximum column widths.
headersGlobalAlign (str, optional) – Allows for global headers alignment, before any specific override from headersAlign. Possible values are: None (follow columns alignment), “right”, “center”, “left”.
headersAlign (str, optional) – Allows for header-wise override starting from left-most given header. Possible values are: “global” (no override), “same” (follow column alignment), “right”, “center”, “left”.
rowAlign (str, optional) – How do you want to align rows: “right”, “center”, “decimal”, “left”.
maxHeaderColWidths (list, optional) – List of column widths for the header.
- Returns:
A text representation of the tabular data.
- Return type:
- class armi.utils.tabulate.DataRow(begin, sep, end)
Bases:
tuple
Create new instance of DataRow(begin, sep, end)
- begin
Alias for field number 0
- end
Alias for field number 2
- sep
Alias for field number 1
- class armi.utils.tabulate.Line(begin, hline, sep, end)
Bases:
tuple
Create new instance of Line(begin, hline, sep, end)
- begin
Alias for field number 0
- end
Alias for field number 3
- hline
Alias for field number 1
- sep
Alias for field number 2
- class armi.utils.tabulate.TableFormat(lineabove, linebelowheader, linebetweenrows, linebelow, headerrow, datarow, padding, withHeaderHide)
Bases:
tuple
Create new instance of TableFormat(lineabove, linebelowheader, linebetweenrows, linebelow, headerrow, datarow, padding, withHeaderHide)
- datarow
Alias for field number 5
- headerrow
Alias for field number 4
- lineabove
Alias for field number 0
- linebelow
Alias for field number 3
- linebelowheader
Alias for field number 1
- linebetweenrows
Alias for field number 2
- padding
Alias for field number 6
- withHeaderHide
Alias for field number 7
- class armi.utils.tabulate.TextWrapper(width=70, initial_indent='', subsequent_indent='', expand_tabs=True, replace_whitespace=True, fix_sentence_endings=False, break_long_words=True, drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None, placeholder=' [...]')[source]
Bases:
object
Object for wrapping/filling text. The public interface consists of the wrap() and fill() methods; the other methods are just there for subclasses to override in order to tweak the default behaviour. If you want to completely replace the main wrapping algorithm, you’ll probably have to override _wrap_chunks().
- Several instance attributes control various aspects of wrapping:
- width (default: 70)
the maximum width of wrapped lines (unless break_long_words is false)
- initial_indent (default: “”)
string that will be prepended to the first line of wrapped output. Counts towards the line’s width.
- subsequent_indent (default: “”)
string that will be prepended to all lines save the first of wrapped output; also counts towards each line’s width.
- expand_tabs (default: true)
Expand tabs in input text to spaces before further processing. Each tab will become 0 .. ‘tabsize’ spaces, depending on its position in its line. If false, each tab is treated as a single character.
- tabsize (default: 8)
Expand tabs in input text to 0 .. ‘tabsize’ spaces, unless ‘expand_tabs’ is false.
- replace_whitespace (default: true)
Replace all whitespace characters in the input text by spaces after tab expansion. Note that if expand_tabs is false and replace_whitespace is true, every tab will be converted to a single space!
- fix_sentence_endings (default: false)
Ensure that sentence-ending punctuation is always followed by two spaces. Off by default because the algorithm is (unavoidably) imperfect.
- break_long_words (default: true)
Break words longer than ‘width’. If false, those words will not be broken, and some lines might be longer than ‘width’.
- break_on_hyphens (default: true)
Allow breaking hyphenated words. If true, wrapping will occur preferably on whitespaces and right after hyphens part of compound words.
- drop_whitespace (default: true)
Drop leading and trailing whitespace from lines.
- max_lines (default: None)
Truncate wrapped lines.
- placeholder (default: ‘ […]’)
Append to the last line of truncated text.
- unicode_whitespace_trans = {9: 32, 10: 32, 11: 32, 12: 32, 13: 32, 32: 32}
- uspace = 32
- wordsep_re = re.compile('\n ( # any whitespace\n [\\\t\\\n\\\x0b\\\x0c\\\r\\ ]+\n | # em-dash between words\n (?<=[\\w!"\\\'&.,?]) -{2,} (?=\\w)\n | # word, possibly hyphenated\n , re.VERBOSE)
- wordsep_simple_re = re.compile('([\\\t\\\n\\\x0b\\\x0c\\\r\\ ]+)')
- sentence_end_re = re.compile('[a-z][\\.\\!\\?][\\"\\\']?\\Z')
- wrap(text: string) [string] [source]
Reformat the single paragraph in ‘text’ so it fits in lines of no more than ‘self.width’ columns, and return a list of wrapped lines. Tabs in ‘text’ are expanded with string.expandtabs(), and all other whitespace characters (including newline) are converted to space.
- fill(text: string) string [source]
Reformat the single paragraph in ‘text’ to fit in lines of no more than ‘self.width’ columns, and return a new string containing the entire wrapped paragraph.
- x = ' '
- class armi.utils.tabulate.chain
Bases:
object
chain(*iterables) –> chain object
Return a chain object whose .__next__() method returns elements from the first iterable until it is exhausted, then elements from the next iterable, until all of the iterables are exhausted.
- from_iterable()
Alternative chain() constructor taking a single iterable argument that evaluates lazily.
- armi.utils.tabulate.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)[source]
Returns a new subclass of tuple with named fields.
>>> Point = namedtuple('Point', ['x', 'y']) >>> Point.__doc__ # docstring for the new class 'Point(x, y)' >>> p = Point(11, y=22) # instantiate with positional args or keywords >>> p[0] + p[1] # indexable like a plain tuple 33 >>> x, y = p # unpack like a regular tuple >>> x, y (11, 22) >>> p.x + p.y # fields also accessible by name 33 >>> d = p._asdict() # convert to a dictionary >>> d['x'] 11 >>> Point(**d) # convert from a dictionary Point(x=11, y=22) >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields Point(x=100, y=22)
- class armi.utils.tabulate.partial[source]
Bases:
object
partial(func, *args, **keywords) - new function with partial application of the given arguments and keywords.
- args
tuple of arguments to future partial calls
- func
function object to use in future partial calls
- keywords
dictionary of keyword arguments to future partial calls
- armi.utils.tabulate.reduce(function, sequence[, initial]) value
Apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). If initial is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty.
- class armi.utils.tabulate.zip_longest
Bases:
object
zip_longest(iter1 [,iter2 […]], [fillvalue=None]) –> zip_longest object
Return a zip_longest object whose .__next__() method returns a tuple where the i-th element comes from the i-th iterable argument. The .__next__() method continues until the longest iterable in the argument sequence is exhausted and then it raises StopIteration. When the shorter iterables are exhausted, the fillvalue is substituted in their place. The fillvalue defaults to None or can be specified by a keyword argument.