Getting Started
Tables
Painless creation of nice-looking tables of data for Python.
Starting simple
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → Name Age Admission score
# → Alice 25 95.1
# → Bob 30 87.3
# → Carol 28 92.1
# → Michelangelo 35 88.0
Note
The lines prefixed by # → indicate the output of the preceding code block.
They are commented to distinguish them from lines of executable code.
This also means that if you
copy and paste this code, using the icon in the top-right corner of the code block, these
output lines will not be executed.
The result is a table with column widths automatically chosen, headings and column data all right-justified (default).
By default output is written to the console (stdout), but you can also:
write to a specific file by passing the
fileoption to.print()obtain the table as a multi-line string using
str(table)
Borders
You can add borders made up of regular ASCII characters:
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score", border="ascii")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → +--------------+-----+-----------------+
# → | Name | Age | Admission score |
# → +--------------+-----+-----------------+
# → | Alice | 25 | 95.1 |
# → | Bob | 30 | 87.3 |
# → | Carol | 28 | 92.1 |
# → | Michelangelo | 35 | 88.0 |
# → +--------------+-----+-----------------+
Or use ANSI box-drawing characters (supported by most terminal emulators):
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score", border="thick")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┏━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━━━━━━━━━━┓
# → ┃ Name ┃ Age ┃ Admission score ┃
# → ┣━━━━━━━━━━━━━━╋━━━━━╋━━━━━━━━━━━━━━━━━┫
# → ┃ Alice ┃ 25 ┃ 95.1 ┃
# → ┃ Bob ┃ 30 ┃ 87.3 ┃
# → ┃ Carol ┃ 28 ┃ 92.1 ┃
# → ┃ Michelangelo ┃ 35 ┃ 88.0 ┃
# → ┗━━━━━━━━━━━━━━┻━━━━━┻━━━━━━━━━━━━━━━━━┛
Other border options: "thin", "round" (thin with rounded corners), and "double".
Column options
To gain additional control, you can create a table with Column objects, which allow
you to specify formatting, alignment, and width constraints for each column:
from ansitable import ANSITable, Column
table = ANSITable(
Column("Name", headalign="^", colalign="<"),
Column("Age", headalign="^", colalign="<"),
Column("Admission score", headalign="^", colalign=">"),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┌──────────────┬─────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├──────────────┼─────┼─────────────────┤
# → │ Alice │ 25 │ 95.1 │
# → │ Bob │ 30 │ 87.3 │
# → │ Carol │ 28 │ 92.1 │
# → │ Michelangelo │ 35 │ 88.0 │
# → └──────────────┴─────┴─────────────────┘
Control alignment with colalign (data) and headalign (heading):
"<"- left">"- right (default)"^"- center
There is also a shorthand way to control header and column alignment using a format string in the column headers.
from ansitable import ANSITable, Column
table = ANSITable("{<}Name", "{^<}Age", "{^>}Admission score", border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┌──────────────┬─────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├──────────────┼─────┼─────────────────┤
# → │ Alice │ 25 │ 95.1 │
# → │ Bob │ 30 │ 87.3 │
# → │ Carol │ 28 │ 92.1 │
# → │ Michelangelo │ 35 │ 88.0 │
# → └──────────────┴─────┴─────────────────┘
If the header string starts with {XY} where X and Y are alignment
characters, these apply to the header and column alignment respectively.
If the header string starts with {X} then X is used as the alignment character for both the header and the column.
Width constraints
Column width can be limited using the width argument:
from ansitable import ANSITable, Column
table = ANSITable(
Column("Name", headalign="^", colalign="<", width=10),
Column("Age", headalign="^", colalign="<"),
Column("Admission score", headalign="^", colalign=">"),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┌────────────┬─────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├────────────┼─────┼─────────────────┤
# → │ Alice │ 25 │ 95.1 │
# → │ Bob │ 30 │ 87.3 │
# → │ Carol │ 28 │ 92.1 │
# → │ Michelang… │ 35 │ 88.0 │
# → └────────────┴─────┴─────────────────┘
Excess text is truncated with an ellipsis (U+2026). Disable with ellipsis=False which simply
truncates the field.
Field formatting
For numeric columns we can specify Python format strings that control how the cell values are rendered. Here we display age in hexadecimal and the score with 2 digits of precision.
from ansitable import ANSITable, Column
table = ANSITable(
Column("Name", headalign="^", colalign="<", width=10),
Column("Age", "0x{:x}", headalign="^", colalign="<"),
Column("Admission score", "{:.3f}", headalign="^", colalign=">"),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┌────────────┬──────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├────────────┼──────┼─────────────────┤
# → │ Alice │ 0x19 │ 95.100 │
# → │ Bob │ 0x1e │ 87.300 │
# → │ Carol │ 0x1c │ 92.100 │
# → │ Michelang… │ 0x23 │ 88.000 │
# → └────────────┴──────┴─────────────────┘
Dividing lines
A dividing line, spanning the entire table, can be added between rows using .rule().
from ansitable import ANSITable, Column
table = ANSITable(
Column("Name", headalign="^", colalign="<"),
Column("Age", headalign="^", colalign="<"),
Column("Admission score", headalign="^", colalign=">"),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.rule() # dividing line
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
# → ┌──────────────┬─────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├──────────────┼─────┼─────────────────┤
# → │ Alice │ 25 │ 95.1 │
# → │ Bob │ 30 │ 87.3 │
# → ├──────────────┼─────┼─────────────────┤
# → │ Carol │ 28 │ 92.1 │
# → │ Michelangelo │ 35 │ 88.0 │
# → └──────────────┴─────┴─────────────────┘
Sorting
A table can be sorted on any heading string, and the result is a new table with its rows sorted.
from ansitable import ANSITable, Column
table = ANSITable(
Column("Name", headalign="^", colalign="<"),
Column("Age", headalign="^", colalign="<"),
Column("Admission score", headalign="^", colalign=">"),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
newtable = table.sort("Age", key=int)
newtable.print()
# → ┌──────────────┬─────┬─────────────────┐
# → │ Name │ Age │ Admission score │
# → ├──────────────┼─────┼─────────────────┤
# → │ Alice │ 25 │ 95.1 │
# → │ Carol │ 28 │ 92.1 │
# → │ Bob │ 30 │ 87.3 │
# → │ Michelangelo │ 35 │ 88.0 │
# → └──────────────┴─────┴─────────────────┘
Parameters to the .sort() method include:
column- column name (str) or index (int)key- optional function to transform values before comparisonreverse- sort in descending order (default: False)
Horizontal rules (added with .rule()) are silently dropped from sorted output.
Color and styling
If the colored package is installed, you can set foreground/background colors and text styles (bold, reverse, underlined, dim) for header cells, data rows or data cells.
See Color reference for a searchable, swatch-illustrated reference of all 256 color names this package
accepts (or colored’s
own list).
Header cell color and format
Here we use a dict to set the style for the header cells: bold white text on a dark grey background.
from ansitable import ANSITable, Column
heading = dict(headstyle="bold", headcolor="white", headbgcolor="grey_53")
table = ANSITable(
Column("Name", headalign="^", colalign="<", **heading),
Column("Age", headalign="^", colalign="<", **heading),
Column("Admission score", headalign="^", colalign=">", **heading),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
The color escape characters can’t be displayed from an inline documentation code block, they are separately rendered to HTML and included here:
| Name | Age | Admission score |
|---|---|---|
| Alice | 25 | 95.1 |
| Bob | 30 | 87.3 |
| Carol | 28 | 92.1 |
| Michelangelo | 35 | 88.0 |
Column format
Extending the above example so that the Name column has its background color set to light blue
from ansitable import ANSITable, Column
heading = dict(headstyle="bold", headcolor="white", headbgcolor="grey_53")
table = ANSITable(
Column("Name", headalign="^", colalign="<", colbgcolor="sky_blue_3", **heading),
Column("Age", headalign="^", colalign="<", **heading),
Column("Admission score", headalign="^", colalign=">", **heading),
border="thin")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
table.print()
| Name | Age | Admission score |
|---|---|---|
| Alice | 25 | 95.1 |
| Bob | 30 | 87.3 |
| Carol | 28 | 92.1 |
| Michelangelo | 35 | 88.0 |
Row format
We can apply the format flags to an entire row. Here we set rows where the score is greater than 90 to red background with white text.
from ansitable import ANSITable, Column
heading = dict(headstyle="bold", headcolor="white", headbgcolor="grey_53")
table = ANSITable(
Column("Name", headalign="^", colalign="<", colbgcolor="sky_blue_3", **heading),
Column("Age", headalign="^", colalign="<", **heading),
Column("Admission score", headalign="^", colalign=">", **heading),
border="thin")
table.row("Alice", 25, 95.1, bgcolor="red_3b", fgcolor="white")
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1, bgcolor="red_3b", fgcolor="white")
table.row("Michelangelo", 35, 88.0)
table.print()
| Name | Age | Admission score |
|---|---|---|
| Alice | 25 | 95.1 |
| Bob | 30 | 87.3 |
| Carol | 28 | 92.1 |
| Michelangelo | 35 | 88.0 |
These row formats take priority over formats set for a column.
Cell format
We can override the styling of a particular cell using Cell instances.
Extending the example above, we highlight score cells over 90 with a red background and white text.
from ansitable import ANSITable, Column, Cell
heading = dict(headstyle="bold", headcolor="white", headbgcolor="grey_53")
table = ANSITable(
Column("Name", headalign="^", colalign="<", colbgcolor="sky_blue_3", **heading),
Column("Age", headalign="^", colalign="<", **heading),
Column("Admission score", headalign="^", colalign=">", **heading),
border="thin")
table.row("Alice", 25, Cell(95.1, bgcolor="red_3b", fgcolor="white"))
table.row("Bob", 30, 87.3)
table.row("Carol", 28, Cell(92.1, bgcolor="red_3b", fgcolor="white"))
table.row("Michelangelo", 35, 88.0)
table.print()
| Name | Age | Admission score |
|---|---|---|
| Alice | 25 | 95.1 |
| Bob | 30 | 87.3 |
| Carol | 28 | 92.1 |
| Michelangelo | 35 | 88.0 |
Cell formats will take priority over formats set for a row or a column.
Export formats
Tables can be exported, as a string, in a number of common markup languages for use in documents. Capabilities for alignment, text formatting and color vary across these markup formats.
Markdown
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.markdown())
# → | Name | Age | Admission score |
# → | -----------: | --: | --------------: |
# → | Alice | 25 | 95.1 |
# → | Bob | 30 | 87.3 |
# → | Carol | 28 | 92.1 |
# → | Michelangelo | 35 | 88.0 |
HTML
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.html())
# → <table>
# → <tr style=''>
# → <th style='text-align:right;'>Name</th>
# → <th style='text-align:right;'>Age</th>
# → <th style='text-align:right;'>Admission score</th>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;'>Alice</td>
# → <td style='text-align:right;'>25</td>
# → <td style='text-align:right;'>95.1</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;'>Bob</td>
# → <td style='text-align:right;'>30</td>
# → <td style='text-align:right;'>87.3</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;'>Carol</td>
# → <td style='text-align:right;'>28</td>
# → <td style='text-align:right;'>92.1</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;'>Michelangelo</td>
# → <td style='text-align:right;'>35</td>
# → <td style='text-align:right;'>88.0</td>
# → </tr>
# → </table>
The table will be rendered according to the document’s CSS settings.
We can override them for this table by passing additional parameters
to html()
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
html = table.html(
table="border-collapse: collapse; margin: 10px 0; border: 3px solid blue;",
th="padding: 8px; border: 1px solid #ddd; font-weight: bold;",
td="padding: 8px; border: 1px solid #ddd;",
)
print(html)
# → <table style="border-collapse: collapse; margin: 10px 0; border: 3px solid blue;">
# → <tr style=''>
# → <th style='text-align:right;padding: 8px; border: 1px solid #ddd; font-weight: bold;'>Name</th>
# → <th style='text-align:right;padding: 8px; border: 1px solid #ddd; font-weight: bold;'>Age</th>
# → <th style='text-align:right;padding: 8px; border: 1px solid #ddd; font-weight: bold;'>Admission score</th>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>Alice</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>25</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>95.1</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>Bob</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>30</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>87.3</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>Carol</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>28</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>92.1</td>
# → </tr>
# → <tr style=''>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>Michelangelo</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>35</td>
# → <td style='text-align:right;padding: 8px; border: 1px solid #ddd;'>88.0</td>
# → </tr>
# → </table>
reStructuredText (ReST) “simple table”
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.rest())
# → ============ === ===============
# → Name Age Admission score
# → ============ === ===============
# → Alice 25 95.1
# → Bob 30 87.3
# → Carol 28 92.1
# → Michelangelo 35 88.0
# → ============ === ===============
LaTeX
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.latex())
# → \begin{tabular}{ |r|r|r| }\hline
# → \multicolumn{1}{|r|}{Name} & \multicolumn{1}{|r|}{Age} & \multicolumn{1}{|r|}{Admission score}\\\hline\hline
# → Alice & 25 & 95.1 \\
# → Bob & 30 & 87.3 \\
# → Carol & 28 & 92.1 \\
# → Michelangelo & 35 & 88.0 \\
# → \hline
# → \end{tabular}
Alignment options are supported.
Wikitable (Wikipedia)
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.wikitable())
# → {| class="wikitable" col1right col2right col3right
# → |-
# → ! Name !! Age !! Admission score
# → |-
# → | Alice || 25 || 95.1
# → |-
# → | Bob || 30 || 87.3
# → |-
# → | Carol || 28 || 92.1
# → |-
# → | Michelangelo || 35 || 88.0
# → |}
CSV
from ansitable import ANSITable
table = ANSITable("Name", "Age", "Admission score")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
print(table.csv())
# → Name,Age,Admission score
# → Alice,25,95.1
# → Bob,30,87.3
# → Carol,28,92.1
# → Michelangelo,35,88.0
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("Name", "Age", "Admission score", border="ascii")
table.row("Alice", 25, 95.1)
table.row("Bob", 30, 87.3)
table.row("Carol", 28, 92.1)
table.row("Michelangelo", 35, 88.0)
df = table.pandas()
print(df)
# → Name Age Admission_score
# → 0 Alice 25 95.1
# → 1 Bob 30 87.3
# → 2 Carol 28 92.1
# → 3 Michelangelo 35 88.0
Column names are converted to valid Python identifiers (spaces → underscores),
allowing attribute access like df.Admission_score.
Disable this with underscores=False.