Quick, easy and pretty display of tabular data or matrices, with optional ANSI color and borders
![]() |
Pretty tables and matrices for Python
|
Synopsis
A package for quick and simple creation of nicely formatted tables or matrices.Create a table:
- programatically from Python, or
- from a Pandas dataframe which is also an easy way to read an Excel or CSV file.
- on the console using Unicode box drawing characters (U+2500 to U+257F) and ANSI color codes
- in a markup language: HTML, Markdown, reStructured text, LaTeX or wikitable.
Tables
Painless creation of nice-looking tables of data for Python.

Starting simple
1 | from ansitable import ANSITable
2 |
3 | table = ANSITable("col1", "column 2 has a big header", "column 3")
4 | table.row("aaaaaaaaa", 2.2, 3)
5 | table.row("bbbbbbbbbbbbb", 5.5, 6)
6 | table.row("ccccccc", 8.8, 9)
7 | table.print()
Line 3 constructs an ANSITable object and the arguments are a sequence of
column names followed by ANSITable keyword arguments - there are none in this first example. Since there are three column names this this will be
a 3-column table.
Lines 4-6 add rows, 3 data values for each row.
Line 7 prints the table and yields a tabular display with column widths automatically chosen, and headings and column data all right-justified (default)
col1 column 2 has a big header column 3
aaaaaaaaa 2.2 3
bbbbbbbbbbbbb 5.5 6
ccccccc 8.8 9
By default output is printed to the console (stdout) but we can also:
- provide a
fileoption to.print()to allow writing to a specified output stream, the
stdout.
- obtain a multi-line string version of the entire table as
str(table).
Column objects which
allows many column specific options to be given, as we shall see later.
For now though, we could rewrite the example above as:
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1"),
Column("column 2 has a big header"),
Column("column 3")
)
or as
from ansitable import ANSITable
table = ANSITable()
table.addcolumn("col1")
table.addcolumn("column 2 has a big header")
table.addcolumn("column 3")
where the keyword arguments to .addcolumn() are the same as those for
Column and are given below.
We can specify a Python
format() style format string for any column - by default it
is the general formatting option "{}".
You may choose to left or right justify values via the format string, ansitable provides control over how those resulting strings are justified within the column.
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1"),
Column("column 2 has a big header", "{:.3g}"), # CHANGE
Column("column 3", "{:-10.4f}")
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
which yields
col1 column 2 has a big header column 3
aaaaaaaaa 2.2 3.0000
bbbbbbbbbbbbb 5.5 6.0000
ccccccc 8.8 9.0000
Alternatively we can specify the format argument as a function that converts
the value to a string.
The data in column 1 is quite long, we might wish to set a maximum column width which we can do using the
width argument
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1", width=10), # CHANGE
Column("column 2 has a big header", "{:.3g}"),
Column("column 3", "{:-10.4f}")
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
which yields
col1 column 2 has a big header column 3
aaaaaaaaa 2.2 3.0000
bbbbbbbbbโฆ 5.5 6.0000
ccccccc 8.8 9.0000
where we see that the data in column 1 has been truncated.
If you don't like the ellipsis you can turn it off, and get to see one more character, with the ANSITable option ellipsis=False. The Unicode ellipsis character u+2026 is used.
Borders
We can add a table border made up of regular ASCII charactersfrom ansitable import ANSITable, Column
table = ANSITable(
Column("col1"),
Column("column 2 has a big header"),
Column("column 3"),
border="ascii" # CHANGE
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
which yields
+--------------+---------------------------+----------+
| col1 | column 2 has a big header | column 3 |
+--------------+---------------------------+----------+
| aaaaaaaaa | 2.2 | 3 |
|bbbbbbbbbbbbb | 5.5 | 6 |
| ccccccc | 8.8 | 9 |
+--------------+---------------------------+----------+
Or we can construct a border using the ANSI box-drawing characters which are supported by most terminal emulators
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1"),
Column("column 2 has a big header"),
Column("column 3"),
border="thick" # CHANGE
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
which yields
โโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโโโโโโโโโโโโณโโโโโโโโโโโ
โ col1 โ column 2 has a big header โ column 3 โ
โฃโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโซ
โ aaaaaaaaa โ 2.2 โ 3 โ
โbbbbbbbbbbbbb โ 5.5 โ 6 โ
โ ccccccc โ 8.8 โ 9 โ
โโโโโโโโโโโโโโโโปโโโโโโโโโโโโโโโโโโโโโโโโโโโโปโโโโโโโโโโโ
Note: this actually looks better on the console than it does in GitHub markdown.
Other border options include "thin", "rounded" (thin with round corners) and "double".
Header and column alignment
We can change the alignment of data and heading for any column with the alignment flags"<" (left),
">" (right) and "^" (centered).
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1"),
Column("column 2 has a big header", colalign="^"), # CHANGE
Column("column 3"),
border="thick"
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
which yields
โโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโโโโโโโโโโโโณโโโโโโโโโโโ
โ col1 โ column 2 has a big header โ column 3 โ
โฃโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโซ
โ aaaaaaaaa โ 2.2 โ 3 โ
โbbbbbbbbbbbbb โ 5.5 โ 6 โ
โ ccccccc โ 8.8 โ 9 โ
โโโโโโโโโโโโโโโโปโโโโโโโโโโโโโโโโโโโโโโโโโโโโปโโโโโโโโโโโ
where the data for column 2 has been centered.
Heading and data alignment for any column can be set independently
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1", headalign="<"), # CHANGE
Column("column 2 has a big header", colalign="^"),
Column("column 3", colalign="<"), # CHANGE
border="thick"
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", -5.5, 6)
table.row("ccccccc", 8.8, -9)
table.print()
yields
โโโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโโโโโโโโโโโโณโโโโโโโโโโโ
โ col1 โ column 2 has a big header โ column 3 โ
โฃโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโซ
โ aaaaaaaaa โ 2.2 โ 3 โ
โ bbbbbbbbbbbbb โ -5.5 โ 6 โ
โ ccccccc โ 8.8 โ -9 โ
โโโโโโโโโโโโโโโโโปโโโโโโโโโโโโโโโโโโโโโโโโโโโโปโโโโโโโโโโโ
where we have left-justified the heading for column 1 and the data for column 3.
We can easily add a dividing line
from ansitable import ANSITable, Column table = ANSITable( Column("col1", headalign="<"), Column("column 2 has a big header", colalign="^"), Column("column 3", colalign="<"), border="thick" ) table.row("aaaaaaaaa", 2.2, 3) table.row("bbbbbbbbbbbbb", -5.5, 6) table.rule() # CHANGE table.row("ccccccc", 8.8, -9) table.print() yields
โโโโโโโโโโโโโโโโโณโโโโโโโโโโโโโโโโโโโโโโโโโโโโณโโโโโโโโโโโ
โ col1 โ column 2 has a big header โ column 3 โ
โฃโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโซ
โ aaaaaaaaa โ 2.2 โ 3 โ
โ bbbbbbbbbbbbb โ -5.5 โ 6 โ
โฃโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโซ
โ ccccccc โ 8.8 โ -9 โ
โโโโโโโโโโโโโโโโโปโโโโโโโโโโโโโโโโโโโโโโโโโโโโปโโโโโโโโโโโ
Color
If you have thecolored package installed then you can set the foreground and
background color and style (bold, reverse, underlined, dim) of the header and column data, as well as the border color.
from ansitable import ANSITable, Column
table = ANSITable(
Column("col1", headalign="<", colcolor="red", headstyle="underlined"), # CHANGE
Column("column 2 has a big header", colalign="^", colstyle="bold"), # CHANGE
Column("column 3", colalign="<", colbgcolor="green"), # CHANGE
border="thick", bordercolor="blue" # CHANGE
)
table.row("aaaaaaaaa", 2.2, 3) table.row("bbbbbbbbbbbbb", -5.5, 6) # CHANGE table.row("ccccccc", 8.8, -9) table.print()
which yields

It is possible to the change the color of a single row of the table, overriding the column defaults, by
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, -9)
which yields

It is also possible to the change the color of a single cell of the table, overriding the column and row defaults, by passing a Cell instance
from ansitable import ANSITable, Column, Cell
table = ANSITable("col1", "column 2 has a big header", "column 3")
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", Cell(-5.5, bgcolor="blue"), 6, bgcolor="yellow") # CHANGE
table.row("ccccccc", 8.8, 9)
table.print()
which yields

The older method (deprecated) of doing this is by prefixing the value with a color enclosed in double angle brackets, for example <<red>>. This does not allow changing the background color or style of the cell.
from ansitable import ANSITable
table = ANSITable("col1", "column 2 has a big header", "column 3")
table.row("aaaaaaaaa", 2.2, 3)
table.row("<<red>>bbbbbbbbbbbbb", 5.5, 6)
table.row("<<blue>>ccccccc", 8.8, 9)
table.print()
All options
ANSITable
These keyword arguments control the styling of the entire table.| Keyword | Default | Purpose | |---- |---- |---- | colsep | 2 | Gap between columns (in spaces) offset | 0 | Gap at start of each row, shifts the table to the left border | no border | Border style: 'ascii', 'thin', 'thick', 'double' bordercolor | |Border color, see possible values ellipsis | True | Add an ellipsis if a wide column is truncated header | True | Include the column header row columns | | Specify the number of columns if header=False and no header name or Column arguments are given color | True | Enable color
- Color is only possible if the
coloredpackage is installed - If
coloris False then no color escape sequences will be emitted, useful
Column
These keyword arguments control the styling of a single column.| Keyword | Default | Purpose | |---- |---- |---- | fmt | "{}" | format string for the column value, or a callable that maps the column value to a string width || maximum column width, excess will be truncated colcolor || Text color, see possible values colbgcolor || Text background color, see possible values colstyle || Text style: "bold", "underlined", "reverse", "dim", "blink" colalign | ">" | Text alignment: ">" (left), "<" (right), "^" (centered) headcolor || Heading text color, see possible values headbgcolor || Heading text background color, see possible values headstyle || Heading text style: "bold", "underlined", "reverse", "dim", "blink" headalign | ">" | Heading text alignment: ">" (left), "<" (right), "^" (centered)
Note that many terminal emulators do not support the "blink" style.
Row
These keyword arguments control the styling of a single row.| Keyword | Default | Purpose | |---- |---- |---- | fgcolor || Text color, see possible values bgcolor || Text background color, see possible values style || Text style: "bold", "underlined", "reverse", "dim", "blink"
Row styling overrides column styling.
Cell
These keyword arguments control the styling of a single cell.| Keyword | Default | Purpose | |---- |---- |---- | fgcolor || Text color, see possible values bgcolor || Text background color, see possible values style || Text style: "bold", "underlined", "reverse", "dim", "blink"
Cell styling overrides row and column styling.
Render to markup language
Now that you can visualize your data as a beautiful table on the console, you might want the table in a different format to include in a document or website. ANSItable supports rendering a table into one of a number of common markup languages.
We start by creating a simple table
table = ANSITable("col1", "column 2 has a big header", "column 3")
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", -5.5, 6)
table.row("ccccccc", 8.8, -9)
table.print()
Support for alignment and color options depends on the capability of the markup language that is being exported to.
Markdown
The table can be rendered into Markdown format by
table.markdown()
which generates
| col1 | column 2 has a big header | column 3 |
| ------------: | ------------------------: | -------: |
| aaaaaaaaa | 2.2 | 3 |
| bbbbbbbbbbbbb | -5.5 | 6 |
| ccccccc | 8.8 | -9 |
Column alignment is supported, but MarkDown doesn't allow the header to have different alignment to the data.
HTML
The table can be rendered into HTML format by
table.html()
which generates
<table style=''>
<tr style=''>
<th style='text-align:right;'>col1</th>
<th style='text-align:right;'>column 2 has a big header</th>
<th style='text-align:right;'>column 3</th>
</tr>
<tr style=''>
<td style='text-align:right;'>aaaaaaaaa</td>
<td style='text-align:right;'>2.2</td>
<td style='text-align:right;'>3</td>
</tr>
<tr style=''>
<td style='text-align:right;'>bbbbbbbbbbbbb</td>
<td style='text-align:right;'>-5.5</td>
<td style='text-align:right;'>6</td>
</tr>
<tr style=''>
<td style='text-align:right;'>ccccccc</td>
<td style='text-align:right;'>8.8</td>
<td style='text-align:right;'>-9</td>
</tr>
</table>
which renders as
| col1 | column 2 has a big header | column 3 |
|---|---|---|
| aaaaaaaaa | 2.2 | 3 |
| bbbbbbbbbbbbb | -5.5 | 6 |
| ccccccc | 8.8 | -9 |
CSS styling options can be applied to the table, rows and cells. This format supports ANSItable header and column foreground and background color options.
ReStructedText
The table can be rendered into reStructedText (ReST) "simple table" format by
table.rest()
which generates
============= ========================= ========
col1 column 2 has a big header column 3
============= ========================= ========
aaaaaaaaa 2.2 3
bbbbbbbbbbbbb -5.5 6
ccccccc 8.8 -9
============= ========================= ========
Header and column alignment options are not supported in the ReST simple table format.
LaTex
The table can be rendered into LaTeX format by
table.latex()
which generates
\begin{tabular}{ |r|r|r| }\hline
\multicolumn{1}{|r|}{col1} & \multicolumn{1}{|r|}{column 2 has a big header} & \multicolumn{1}{|r|}{column 3}\\\hline\hline
aaaaaaaaa & 2.2 & 3 \\
bbbbbbbbbbbbb & -5.5 & 6 \\
ccccccc & 8.8 & -9 \\
\hline
\end{tabular}
Header and column alignment options are supported.
Wikitable
The table can be rendered into wikitable markup format, as used for tables in Wikipedia, by
table.wikitable()
which generates
{| class="wikitable" col1right col2right col3right
|-
! col1 !! column 2 has a big header !! column 3
|-
| aaaaaaaaa || 2.2 || 3
|-
| bbbbbbbbbbbbb || -5.5 || 6
|-
| ccccccc || 8.8 || -9
|}
Column alignment is supported, but wikitable headers are always centred.
CSV
The table can be rendered into CSV format by
table.csv()
which generates
col1,column 2 has a big header,column 3
aaaaaaaaa,2.2,3
bbbbbbbbbbbbb,-5.5,6
ccccccc,8.8,-9
The delimiter character defaults to comma, but can be set.
CSV format data can be quickly visualized on the desktop using any spreadsheet program, or included in ReST documentation using the csv-table directive.
Pandas integration
Pandas is THE tool to use for tabular data so we support conversions in both directions.
To convert a Pandas DataFrame to an ANSItable is just
import pandas as pd
df = pd.DataFrame({"calories": [420, 380, 390], "duration": [50, 40, 45]}) table = ANSITable.Pandas(df, border="thin") table.print()
โโโโโโโโโโโโฌโโโโโโโโโโโ โ calories โ duration โ โโโโโโโโโโโโผโโโโโโโโโโโค โ 420 โ 50 โ โ 380 โ 40 โ โ 390 โ 45 โ โโโโโโโโโโโโดโโโโโโโโโโโ
`Pandas() is a static method that acts like a constructor. This is the simplest way to display CSV format data in an ANSItable by using Pandas read_csv() to load the data into a DataFrame.
To export an ANSItable as a Pandas DataFrame is simply
<pre><code class="lang-">table = ANSITable("col1", "column 2 has a big header", "column 3") table.row("aaaaaaaaa", 2.2, 3) table.row("bbbbbbbbbbbbb", -5.5, 6) table.row("ccccccc", 8.8, -9)
df = table.pandas() print(df)
col1 column2hasabigheader column3 0 aaaaaaaaa 2.2 3 1 bbbbbbbbbbbbb -5.5 6 2 ccccccc 8.8 -9</code></pre> Note that the column names have been modified, spaces changed to underscores, which allows the columns to be accessed as attributes:
<pre><code class="lang-">print(df.column2hasabigheader.tostring())
0 2.2 1 -5.5 2 8.8</code></pre> which shows the column as a Pandas
Series object. This column name-changing behaviour can be disabled by passing underscores=False.
Matrices
Painless creation of nice-looking matrices for Python.
We can create a formatter for NumPy arrays (1D or 2D)
<pre><code class="lang-python">from ansitable import ANSIMatrix formatter = ANSIMatrix(style='thick')</code></pre>
and then use it to format a NumPy array
<pre><code class="lang-python">m = np.random.rand(4,4) - 0.5 m[0,0] = 1.23456e-14 formatter.print(m)</code></pre>
yields
<pre><code class="lang-">โ โ โ 0 -0.385 -0.106 0.296 โ โ 0.0432 0.339 0.119 -0.468 โ โ 0.405 -0.306 0.0165 -0.439 โ โ 0.203 0.4 -0.499 -0.487 โ โ โ</code></pre>
we can also add suffixes
<pre><code class="lang-python">formatter.print(m, suffixsuper='T', suffixsub='3')</code></pre>
yields
<pre><code class="lang-">โ โT โ 0 -0.239 0.186 -0.414 โ โ 0.49 0.215 -0.0148 0.0529 โ โ 0.0473 0.0311 0.45 0.394 โ โ-0.192 0.193 -0.455 0.0302 โ โ โ3</code></pre>
By default output is printed to the console (stdout) but we can also:
- provide a
file option to .print() to allow writing to a specified output stream, the default is stdout.
obtain a multi-line string version of the entire table using the .str() method
instead of .print().
The formatter takes additional arguments to control the numeric format and to control the suppression of very small values.
ANSIMatrix
These keyword arguments control the overall styling and operation of the formatter.
| Keyword | Default | Purpose | |---- |---- |---- | style |
"thin" | "thin", "round", "thick", "double" fmt | "{:< 10.3g}" | format for each element squish | True | set small elements to zero squishtol | 100 | elements less than squishtol * eps are set to zero
Formatter
A formatter takes additional arguments to the styling for a particular call.
| Keyword | Default | Purpose | |---- |---- |---- | suffix_super |
"" | superscript suffix text suffix_sub | ""` | subscript suffix text