----------------------------------------------------------------------
This is the API documentation for the tinytab library.
----------------------------------------------------------------------
## Formatting values
Turn single values into display strings.
fmt_number(x: 'float', *, decimals: 'int' = 2, sep_mark: 'str' = ',') -> 'str'
Format a number with a fixed number of decimal places and a thousands separator.
Parameters
----------
x
The number to format.
decimals
The number of decimal places.
sep_mark
The character used to separate groups of thousands.
Returns
-------
str
The formatted number.
Examples
--------
By default, numbers get two decimal places and a comma between groups of thousands:
```{python}
from tinytab import fmt_number
fmt_number(1234567.891)
```
Use `decimals=` to change the precision, and `sep_mark=` for a different separator:
```{python}
fmt_number(1234567.891, decimals=0, sep_mark=" ")
```
fmt_percent(x: 'float', *, decimals: 'int' = 1) -> 'str'
Format a proportion as a percentage.
Parameters
----------
x
The proportion to format, where `1` is 100%.
decimals
The number of decimal places.
Returns
-------
str
The formatted percentage.
Examples
--------
A proportion of `0.256` is 25.6%:
```{python}
from tinytab import fmt_percent
fmt_percent(0.256)
```
Use `decimals=` to change the precision:
```{python}
fmt_percent(0.256, decimals=0)
```
fmt_currency(x: 'float', *, currency: 'str' = 'USD') -> 'str'
Format a number as a currency value.
Parameters
----------
x
The amount to format.
currency
A three-letter currency code (case-insensitive). Yen values are shown without
decimal places.
Returns
-------
str
The formatted amount, with the currency symbol.
Examples
--------
Amounts are in US dollars by default:
```{python}
from tinytab import fmt_currency
fmt_currency(1234.5)
```
Choose another currency with its three-letter code, in any case. Negative amounts put the sign
before the symbol:
```{python}
fmt_currency(-5, currency="gbp")
```
Yen are shown without decimal places:
```{python}
fmt_currency(1234.5, currency="JPY")
```
## Tables
Build a table, format its columns, and render it as text or HTML.
Table(data: 'Mapping[str, Sequence[Any]]')
A small, column-oriented table.
Parameters
----------
data
A dictionary mapping column names to sequences of values. All columns must have the
same length.
Examples
--------
Create a table from a dictionary of columns. In a notebook, or on a page like this one, a table
displays as HTML:
```{python}
from tinytab import Table
tbl = Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]})
tbl
```
fmt(self, column: 'str', fn: 'Callable[[Any], str]') -> 'Table'
Attach a formatting function to a column.
Parameters
----------
column
The column to format.
fn
A function that takes a single value and returns a string. Missing values (`None`)
are rendered as empty cells and are never passed to `fn`.
Returns
-------
Table
A new table; the original is not modified.
Examples
--------
Format the `price` column as US dollars:
```{python}
from tinytab import Table, fmt_currency
tbl = Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]})
tbl.fmt("price", fmt_currency)
```
Any function that takes a value and returns a string works as a formatter:
```{python}
tbl.fmt("price", lambda x: f"{x:.1f} USD")
```
as_text(self) -> 'str'
Render the table as a Markdown-style text table.
Returns
-------
str
The rendered table.
Examples
--------
```{python}
from tinytab import Table, fmt_currency
tbl = Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]})
print(tbl.fmt("price", fmt_currency).as_text())
```
as_html(self) -> 'str'
Render the table as an HTML `
`.
Returns
-------
str
The rendered table, with all values HTML-escaped.
Examples
--------
```{python}
from tinytab import Table
tbl = Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]})
print(tbl.as_html())
```
to_csv(self, path: 'str | Path') -> 'None'
Write the table's unformatted values to a CSV file.
Parameters
----------
path
The file to write.
Examples
--------
Save a table, then read it back with `Table.from_csv()`:
```{python}
import tempfile
from pathlib import Path
from tinytab import Table
path = Path(tempfile.mkdtemp()) / "menu.csv"
Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]}).to_csv(path)
print(path.read_text())
```
from_csv(path: 'str | Path') -> 'Table'
Read a table from a CSV file. All values are read as strings.
Parameters
----------
path
The file to read.
Returns
-------
Table
The table read from the file.
Examples
--------
Save a table with `Table.to_csv()`, then read it back. CSV files don't record types, so
every value comes back as a string:
```{python}
import tempfile
from pathlib import Path
from tinytab import Table
path = Path(tempfile.mkdtemp()) / "menu.csv"
Table({"item": ["coffee", "bagel"], "price": [3.5, 2.25]}).to_csv(path)
Table.from_csv(path)
```
## Summary statistics
Summarize a set of numbers.
summarize(values: 'Sequence[float | None]') -> 'SummaryResult'
Compute summary statistics for a sequence of numbers.
Missing values (`None` and NaN) are dropped with a warning.
Parameters
----------
values
The numbers to summarize.
Returns
-------
SummaryResult
The count, mean, standard deviation (with `n - 1` denominator), minimum and maximum.
Examples
--------
Summarize a list of numbers:
```{python}
from tinytab import summarize
result = summarize([3.5, 2.25, 4.0, 1.75])
result
```
Each statistic is an attribute of the result:
```{python}
result.mean
```
SummaryResult(n: 'int', mean: 'float', sd: 'float', min: 'float', max: 'float') -> None
Summary statistics for a set of numbers, as returned by `summarize()`.
----------------------------------------------------------------------
This is the User Guide documentation for the package.
----------------------------------------------------------------------
## Getting Started
### Introduction
tinytab is a small library for formatting values and rendering small tables, as text or as
HTML. It has no dependencies beyond Python itself.
It exists as an example: a small but complete package that shows how to structure, test, and
document a Python library. It isn't published on PyPI.
## Installation
Install tinytab from its GitHub repository:
```{.bash filename="Terminal"}
pip install git+https://github.com/rich-iannone/example-python-package.git
```
## A first look
tinytab does three things. It formats single values as strings:
```{python}
from tinytab import fmt_currency
fmt_currency(1234.5)
```
It builds tables, with a formatter attached to any column:
```{python}
from tinytab import Table
menu = Table(
{
"item": ["coffee", "bagel", "croissant"],
"price": [3.5, 2.25, 4.0],
}
)
menu.fmt("price", fmt_currency)
```
And it summarizes sets of numbers:
```{python}
from tinytab import summarize
summarize([3.5, 2.25, 4.0])
```
The rest of this guide takes each of these in turn:
- [Formatting Values](formatting-values.qmd): numbers, percentages, and currencies
- [Tables](tables.qmd): building, formatting, rendering, and saving tables
- [Summary Statistics](summary-statistics.qmd): `summarize()` and its result object
## Using tinytab
### Formatting Values
tinytab has three functions for turning a single number into a display string. They share the
same rules: the value is the first argument, and every option is a keyword argument.
## Numbers
[`fmt_number()`](../reference/fmt_number.qmd) rounds to a fixed number of decimal places (2 by
default) and groups thousands with a separator:
```{python}
from tinytab import fmt_number
fmt_number(1234567.891)
```
Use `decimals=` to change the precision:
```{python}
fmt_number(1234567.891, decimals=0)
```
The thousands separator is a comma by default. Set another with `sep_mark=`:
```{python}
fmt_number(1234567.891, sep_mark=" ")
```
## Percentages
[`fmt_percent()`](../reference/fmt_percent.qmd) takes a proportion, where `1` means 100%, and
shows one decimal place by default:
```{python}
from tinytab import fmt_percent
fmt_percent(0.256)
```
```{python}
fmt_percent(0.256, decimals=0)
```
## Currencies
[`fmt_currency()`](../reference/fmt_currency.qmd) adds a currency symbol and uses the right
number of decimal places for the currency. US dollars are the default:
```{python}
from tinytab import fmt_currency
fmt_currency(1234.5)
```
Choose another currency with a three-letter code (in any case). Yen are shown without decimal
places, and negative amounts put the sign before the symbol:
```{python}
[
fmt_currency(1234.5, currency="eur"),
fmt_currency(1234.5, currency="JPY"),
fmt_currency(-5, currency="GBP"),
]
```
The supported codes are USD, EUR, GBP, and JPY. Any other code is an error, and the message
lists the valid ones:
```{python}
#| error: true
fmt_currency(10, currency="XYZ")
```
## Only numbers
All three functions accept only numbers (integers and floats). Anything else, including a
number written as a string, raises a `TypeError`:
```{python}
#| error: true
fmt_number("10")
```
`True` and `False` are rejected too, even though Python treats them as the integers 1 and 0.
Formatting a boolean as "1.00" is almost always a mistake.
### Tables
A [`Table`](../reference/Table.qmd) holds data in columns, and renders it as text or HTML.
## Creating a table
Pass a dictionary that maps each column name to a list of values. All the columns must have the
same length:
```{python}
from tinytab import Table
menu = Table(
{
"item": ["coffee", "bagel", "croissant"],
"price": [3.5, 2.25, 4.0],
"sold": [120, 45, None],
}
)
menu.columns, menu.n_rows
```
In a notebook, or on a page like this one, a table displays as HTML. Missing values (`None`)
show as empty cells:
```{python}
menu
```
## Formatting columns
[`Table.fmt()`](../reference/Table.fmt.qmd) attaches a formatting function to a column. Any
function that takes a value and returns a string works, including tinytab's own formatters:
```{python}
from tinytab import fmt_currency
menu.fmt("price", fmt_currency)
```
`fmt()` returns a *new* table and leaves the original unchanged, so calls can be chained, one
per column:
```{python}
from tinytab import fmt_number
menu.fmt("price", fmt_currency).fmt("sold", lambda x: fmt_number(x, decimals=0))
```
Formatters are never given missing values, so a formatter doesn't need to handle `None`.
## Rendering as text
[`Table.as_text()`](../reference/Table.as_text.qmd) renders a Markdown-style text table, ready to
print or paste into a Markdown document:
```{python}
print(menu.fmt("price", fmt_currency).as_text())
```
## Rendering as HTML
[`Table.as_html()`](../reference/Table.as_html.qmd) returns the HTML as a string, for use in a
web page or report:
```{python}
print(menu.fmt("price", fmt_currency).as_html())
```
Every value is HTML-escaped, so text in the data can never be mistaken for markup:
```{python}
Table({"name": ["bold", "Tom & Jerry"]})
```
## Saving and loading
[`Table.to_csv()`](../reference/Table.to_csv.qmd) writes the table's values to a CSV file, and
[`Table.from_csv()`](../reference/Table.from_csv.qmd) reads one back:
```{python}
import tempfile
from pathlib import Path
path = Path(tempfile.mkdtemp()) / "menu.csv"
menu.to_csv(path)
Table.from_csv(path)
```
Two things to know about CSV files:
- Formatting isn't saved. `to_csv()` writes the original values, not the formatted ones.
- CSV files don't record types, so `from_csv()` reads every value as a string.
### Summary Statistics
[`summarize()`](../reference/summarize.qmd) computes the basic summary statistics of a set of
numbers:
```{python}
from tinytab import summarize
result = summarize([3.5, 2.25, 4.0, 1.75])
result
```
## The result object
`summarize()` returns a [`SummaryResult`](../reference/SummaryResult.qmd), with one attribute per
statistic:
```{python}
result.mean, result.sd
```
| Attribute | Meaning |
|---|---|
| `n` | the number of values summarized |
| `mean` | the arithmetic mean |
| `sd` | the sample standard deviation (dividing by `n - 1`) |
| `min`, `max` | the smallest and largest values |
The printed form rounds to four significant figures, to keep it readable. The attributes hold
the full values.
A result can't be changed after it's created. Assigning to an attribute is an error:
```{python}
#| error: true
result.mean = 0
```
## Missing values
Missing values, written as `None` or NaN, are left out of the calculation. Since dropping data
silently could hide a problem, `summarize()` issues a warning saying how many were dropped:
```{python}
import math
summarize([3.5, None, 4.0, math.nan])
```
If there are no values left to summarize, `summarize()` raises an error:
```{python}
#| error: true
summarize([None, None])
```
## A single value
With only one value, there's no spread to measure, so the standard deviation is 0:
```{python}
summarize([23])
```