Getting Started

Tables

Painless creation of nice-looking tables of data for Python.

Starting simple

from ansitable import ANSITable

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()
# →           col1   column 2 has a big header   column 3  
# →      aaaaaaaaa                         2.2          3  
# →  bbbbbbbbbbbbb                         5.5          6  
# →        ccccccc                         8.8          9

This produces a table with column widths automatically chosen, headings and column data all right-justified (default).

By default output is printed to the console (stdout), but you can:

  • Provide a file option to .print() to write to a specified output stream

  • Obtain a multi-line string version with str(table)

Borders

You can add borders made up of regular ASCII characters:

from ansitable import ANSITable, Column
table = ANSITable(
        Column("col1"),
        Column("column 2 has a big header"),
        Column("column 3"),
        border="ascii"
    )
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
# → +---------------+---------------------------+----------+
# → |          col1 | column 2 has a big header | column 3 |
# → +---------------+---------------------------+----------+
# → |     aaaaaaaaa |                       2.2 |        3 |
# → | bbbbbbbbbbbbb |                       5.5 |        6 |
# → |       ccccccc |                       8.8 |        9 |
# → +---------------+---------------------------+----------+

Or use ANSI box-drawing characters (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"
    )
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", 5.5, 6)
table.row("ccccccc", 8.8, 9)
table.print()
# → ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
# → ┃          col1 ┃ column 2 has a big header ┃ column 3 ┃
# → ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫
# → ┃     aaaaaaaaa ┃                       2.2 ┃        3 ┃
# → ┃ bbbbbbbbbbbbb ┃                       5.5 ┃        6 ┃
# → ┃       ccccccc ┃                       8.8 ┃        9 ┃
# → ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛

Other border options: "thin", "round" (thin with rounded corners), and "double".

Formatting and alignment

Specify Python format strings for columns:

from ansitable import ANSITable, Column
table = ANSITable(
        Column("col1"),
        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()
# →           col1   column 2 has a big header     column 3  
# →      aaaaaaaaa                         2.2       3.0000  
# →  bbbbbbbbbbbbb                         5.5       6.0000  
# →        ccccccc                         8.8       9.0000

Control alignment with colalign (data) and headalign (heading): - "<" — left - ">" — right (default) - "^" — center

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.row("ccccccc", 8.8, 9)
table.print()
# → ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
# → ┃ col1          ┃ column 2 has a big header ┃ column 3 ┃
# → ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫
# → ┃     aaaaaaaaa ┃            2.2            ┃ 3        ┃
# → ┃ bbbbbbbbbbbbb ┃           -5.5            ┃ 6        ┃
# → ┃       ccccccc ┃            8.8            ┃ 9        ┃
# → ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛

Add dividing lines with .rule():

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()
table.row("ccccccc", 8.8, -9)
table.print()
# → ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
# → ┃ col1          ┃ column 2 has a big header ┃ column 3 ┃
# → ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫
# → ┃     aaaaaaaaa ┃            2.2            ┃ 3        ┃
# → ┃ bbbbbbbbbbbbb ┃           -5.5            ┃ 6        ┃
# → ┣━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━┫
# → ┃       ccccccc ┃            8.8            ┃ -9       ┃
# → ┗━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━┛

Width constraints

Limit column width with the width argument:

from ansitable import ANSITable, Column
table = ANSITable(
        Column("col1", width=10),
        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()
# →        col1   column 2 has a big header     column 3  
# →   aaaaaaaaa                         2.2       3.0000  
# →  bbbbbbbbb…                         5.5       6.0000  
# →     ccccccc                         8.8       9.0000

Excess text is truncated with an ellipsis (U+2026). Disable with ellipsis=False.

Color and styling

If you have the colored package installed, you can set foreground/background colors and text styles (bold, reverse, underlined, dim):

from ansitable import ANSITable, Column, Cell
table = ANSITable(
    Column("col1", headalign="<", colcolor="red", headstyle="underlined"),
    Column("column 2 has a big header", colalign="^", colstyle="bold"),
    Column("column 3", colalign="<", colbgcolor="green"),
    border="thick", bordercolor="blue"
)
table.row("aaaaaaaaa", 2.2, 3)
table.row("bbbbbbbbbbbbb", -5.5, 6)
table.row("ccccccc", 8.8, -9)
print(table)

Rendered output with colors:

col1 column 2 has a big header column 3
aaaaaaaaa 2.2 3
bbbbbbbbbbbbb -5.5 6
ccccccc 8.8 -9

Override styles per-row or per-cell:

from ansitable import ANSITable, Column, Cell
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", Cell(-5.5, bgcolor="blue"), 6, bgcolor="yellow")
table.row("ccccccc", 8.8, 9)
print(table)

Rendered output with per-cell colors:

col1 column 2 has a big header column 3
aaaaaaaaa 2.2 3
bbbbbbbbbbbbb -5.5 6
ccccccc 8.8 9

Sorting

Sort table rows by a column:

from ansitable import ANSITable
table = ANSITable("name", "score")
table.row("alice", 3)
table.row("bob", 1)
table.row("carol", 2)
table.sort("score", key=int)
# → ANSITable: 3 x 2:  name; score
table.print()
# →   name   score  
# →    bob       1  
# →  carol       2  
# →  alice       3

The .sort() method supports:

  • column — column name (str) or index (int)

  • key — optional function to transform values before comparison

  • reverse — sort in descending order (default: False)

Horizontal rules (added with .rule()) are silently dropped from sorted output.

Export formats

Export tables to markup languages for use in documents:

Markdown:

from ansitable import ANSITable
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)
print(table.markdown())
# → |           col1  |  column 2 has a big header  |  column 3  |
# → | ------------: | ------------------------: | -------: |
# → |      aaaaaaaaa  |                        2.2  |         3  |
# → |  bbbbbbbbbbbbb  |                       -5.5  |         6  |
# → |        ccccccc  |                        8.8  |        -9  |

HTML:

Supports CSS styling of cells and colors.

from ansitable import ANSITable
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)
print(table.html()[:200] + "...")
# → <table>
# →   <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...

reStructuredText (ReST) “simple table”:

from ansitable import ANSITable
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)
print(table.rest())
# → =============  =========================  ========  
# →          col1  column 2 has a big header  column 3  
# → =============  =========================  ========  
# →     aaaaaaaaa                        2.2         3  
# → bbbbbbbbbbbbb                       -5.5         6  
# →       ccccccc                        8.8        -9  
# → =============  =========================  ========

LaTeX:

Alignment options supported.

from ansitable import ANSITable
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)
print(table.latex()[:150] + "...")
# → \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...

Wikitable (Wikipedia):

from ansitable import ANSITable
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)
print(table.wikitable())
# → {| 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  
# → |}

CSV:

from ansitable import ANSITable
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)
print(table.csv())
# → col1,column 2 has a big header,column 3
# → aaaaaaaaa,2.2,3
# → bbbbbbbbbbbbb,-5.5,6
# → ccccccc,8.8,-9

Matrices

Display NumPy arrays as formatted matrices:

from ansitable import ANSIMatrix
import numpy as np

np.random.seed(42)
formatter = ANSIMatrix(style='thick')
m = np.random.rand(4, 4) - 0.5
formatter.print(m)
# → ┏                                           ┓
# → ┃-0.125      0.451      0.232      0.0987   ┃
# → ┃-0.344     -0.344     -0.442      0.366    ┃
# → ┃ 0.101      0.208     -0.479      0.47     ┃
# → ┃ 0.332     -0.288     -0.318     -0.317    ┃
# → ┗                                           ┛

Add superscript and subscript suffixes:

from ansitable import ANSIMatrix
import numpy as np

np.random.seed(42)
formatter = ANSIMatrix(style='thick')
m = np.random.rand(4, 4) - 0.5
formatter.print(m, suffix_super='T', suffix_sub='3')
# → ┏                                           ┓T
# → ┃-0.125      0.451      0.232      0.0987   ┃
# → ┃-0.344     -0.344     -0.442      0.366    ┃
# → ┃ 0.101      0.208     -0.479      0.47     ┃
# → ┃ 0.332     -0.288     -0.318     -0.317    ┃
# → ┗                                           ┛3

Pandas integration

Convert Pandas DataFrames to ANSITable:

import pandas as pd
from ansitable import ANSITable

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 │
# → └──────────┴──────────┘

Convert ANSITable back to DataFrame:

from ansitable import ANSITable
import pandas as pd

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 column_2_has_a_big_header column_3
# → 0      aaaaaaaaa                       2.2        3
# → 1  bbbbbbbbbbbbb                      -5.5        6
# → 2        ccccccc                       8.8       -9

Column names are converted to valid Python identifiers (spaces → underscores), allowing attribute access like df.column_2_has_a_big_header. Disable this with underscores=False.