Skip to content

API reference

Generated from the docstrings in the source code, so it always matches the installed version. Everything below is available as tabsight.<name> (imported as ts).

Loading

load

load(
    source: Any,
    *,
    encoding: str | None = None,
    sheet: str | int | None = None,
) -> pd.DataFrame

Load a local file (CSV, TSV, TXT, Excel, Parquet, JSON) into a DataFrame.

Text files are read as text first, then columns that are plain numbers become numbers. Codes with leading zeros ("02134") stay text. Dates are left as text for tabsight's date reader, which reports what it assumed.

Profiling

profile

profile(
    data: Any,
    *,
    overrides: Mapping[str, Any] | None = None,
    schema: Any = None,
    hierarchies: bool | Mapping[str, list[str]] = True,
    dayfirst: bool | None = None,
    quiet: bool | None = None,
) -> Profile

Work out the role and unit of every column. The input is never changed.

Profile dataclass

show

show() -> str

A readable table of every guess and why it was made.

to_dict

to_dict() -> dict[str, Any]

Save the decisions (not the data) so the same reading applies to new data.

Field dataclass

__str__

__str__() -> str

One readable line, for example Sales: measure, currency, summed (name suggests money).

Checking and cleaning

check

check(
    data: Any,
    *,
    prof: Profile | None = None,
    as_of: str | Timestamp | None = None,
) -> CheckReport

Report data-quality issues. Nothing is changed.

CheckReport dataclass

score property

score: int

Data quality score out of 100 (100 = no issues found).

Issue dataclass

clean

clean(
    data: Any,
    mode: str = "safe",
    *,
    dedupe: bool = False,
    dayfirst: bool | None = None,
    decisions: Any = None,
) -> tuple[pd.DataFrame, CleanReport]

Return a cleaned copy and a report. The input is never changed.

CleanReport

What cleaning did and suggests. Methods return a new DataFrame each time.

apply

apply(target: str) -> pd.DataFrame

Apply one suggestion by id, or every suggestion for a column.

undo

undo(column: str) -> pd.DataFrame

Restore one column's original values.

to_dict

to_dict() -> dict[str, Any]

The applied decisions, to reuse with ts.clean(new_df, decisions=...).

Aggregating

aggregate

aggregate(
    data: Any,
    dims: str | Sequence[str] | None = None,
    measures: Any = None,
    *,
    freq: str | Mapping[str, str] | None = None,
    top: int | None = None,
    sort: str | Sequence[str] | None = None,
    missing: str | None = None,
    min_group_size: int | None = None,
    fiscal_start: int | None = None,
    week_start: str | None = None,
    params: Mapping[str, Any] | None = None,
    prof: Profile | None = None,
) -> Aggregation

Group data by dims and aggregate measures exactly.

  • measures: column names (aggregated by their profiled default), Measure objects or dicts like {"column": "Discount", "agg": "wavg", "weight": "Sales"}.
  • freq: date grain for time dimensions ("month"), or a dict per column.
  • top: keep the top N values of the first category dimension (never a date dimension); the rest become one exact "Other" row whose members are listed in result.other. Ranking uses the first requested output.
  • sort: "desc" / "asc" by the first measure, "none" for natural order, "by:<measure>", or an explicit list of values.
  • missing: missing time periods as "gap" (NaN), "zero" or "connect" (left out).
  • min_group_size: hide values of groups with fewer rows (privacy).
  • measures may also hold calculated fields from ts.calc(...); params supplies their $Name parameter values.

Aggregation dataclass

The result of :func:aggregate: tidy rows plus what was assumed.

values property

values: DataFrame

Only the dimension and output columns (no internal helper columns).

Measure dataclass

One measure: a column (or None for a row count) and how to aggregate it.

bucket

bucket(
    values: Series,
    grain: str,
    *,
    fiscal_start: int = 1,
    week_start: str = "monday",
) -> pd.Series

Start of the period each date falls in (2017-02-14 by month → 2017-02-01).

Fiscal years start in month fiscal_start; quarters follow the fiscal year.

table_calc

table_calc(
    result: Aggregation,
    calc: str,
    *,
    measure: str | None = None,
    along: str | None = None,
    fixed_total: float | None = None,
    name: str | None = None,
) -> Aggregation

Return a new Aggregation with one extra column holding the table calculation.

Charts

chart

chart(
    data: Any,
    kind: str = "bar",
    *,
    x: str | Sequence[str] | None = None,
    y: str | Sequence[str] | None = None,
    color: str | None = None,
    agg: str | None = None,
    sort: str | Sequence[str] | None = None,
    top: int | None = None,
    freq: str | None = None,
    title: str | None = None,
    subtitle: str | None = None,
    unit: str | None = None,
    currency: str | None = None,
    decimals: int | None = None,
    format: str | None = None,
    theme: str | Theme | None = None,
    locale: str | None = None,
    palette: str | None = None,
    colors: Mapping[str, str] | None = None,
    height: int | None = None,
    prof: Profile | None = None,
    geo: str | None = None,
    lat: str | None = None,
    lon: str | None = None,
    map: str | None = None,
    level: str | None = None,
    layer: str | None = None,
    geo_map: Mapping[str, str] | None = None,
    geo_names: str | None = None,
    projection: str | None = None,
    size: str | None = None,
    goal: float | str | None = None,
    bins: int | None = None,
    bin_width: float | None = None,
    source: str | None = None,
    target: str | None = None,
    facet: str | None = None,
    trend: bool | None = None,
    forecast: int | None = None,
    season: int | None = None,
    ref: str | float | Sequence[str | float] | None = None,
    band: Sequence[str | float] | None = None,
    outliers: bool | None = None,
    insights: bool | None = None,
    compare: str | None = None,
    as_of: str | None = None,
) -> Figure

Build a chart. x is what you group by, y the measure(s), color splits the series. Everything else has a sensible default and can be overridden.

kind="auto" draws the best chart ts.recommend finds for these fields.

Maps (kind="map", "choropleth" or "symbol_map"): geo is the column of places, matched automatically to countries, regions or cities. map picks the map ("world" or a country such as "Ireland"), level (country, region, city) and layer (for example "nation" for the UK) narrow it, geo_map fixes spellings, geo_names sets the language of place names, and lat/lon place points by coordinates.

Analytics: trend=True adds a least-squares trend line, forecast=6 estimates the next 6 periods (Holt-Winters, dashed, with a rough range; season= overrides the season length), ref="mean" ("median", "p90", a number, or a list) draws reference lines, band=("p25", "p75") shades a band, outliers=True circles unusual points, and insights=True adds plain-English facts under the chart. A KPI takes compare="previous_year" (or "previous_quarter", "previous_month") and as_of="2017-12-31"; "today" is otherwise the latest date in the data.

Figure dataclass

A chart ready to display. to_dict() is plain JSON for any web page that loads tabsight.js; to_html() writes a single offline file.

to_html

to_html(
    path: str | Path | None = None, *, cdn: bool = False
) -> str

The chart as one HTML document. Writes it to path if given.

Offline by default (ECharts is inlined, about 1.1 MB). cdn=True loads ECharts from jsDelivr instead, pinned with a Subresource Integrity hash.

to_svg

to_svg(
    path: str | Path | None = None, *, width: int = 800
) -> str

The chart as a still SVG picture (title, chart and notes), drawn without a browser.

Needs dukpy: pip install tabsight[static]. Writes it to path if given.

to_png

to_png(
    path: str | Path | None = None,
    *,
    width: int = 800,
    scale: float = 2.0,
) -> bytes

The chart as a PNG image (the SVG picture, rasterised), drawn without a browser.

Needs pip install tabsight[png] (dukpy and resvg-py). scale=2 gives sharp images on high-density screens. Writes it to path if given.

explain

explain() -> str

Every choice tabsight made for this chart, and why, in plain English.

lint

lint(
    *, ignore: Iterable[str] = (), strict: bool = False
) -> LintReport

Check this chart against the rules of good chart design. See tabsight.lint.

update

update(patch: dict[str, Any]) -> Figure

Return a copy with patch deep-merged into the ECharts option.

The escape hatch for anything tabsight doesn't support. Values must be plain JSON. formatter and keys starting with $ are refused: a string formatter would put unescaped data into tooltips, which is how XSS happens.

Dashboards

Dashboard

A dashboard: tiles laid out on a 12-column grid, filters, and cross-filtering.

dash = ts.Dashboard("Sales", data=orders, filters=["Region", "Order_Date"]) dash.kpi("Sales").kpi("Profit").add("bar", x="Category", y="Sales").to_html("sales.html")

Clicking a bar, slice, map region or table row filters every other tile; Ctrl or Shift + click adds values; clicking again removes them. The numbers are re-aggregated exactly in the browser from one shared, pre-aggregated table (no raw rows).

add

add(
    kind: str = "auto",
    *,
    w: int | None = None,
    action: str = "filter",
    **chart: Any,
) -> Dashboard

Add a tile: any ts.chart arguments, plus w (width, 1-12 columns) and action ("filter": clicks filter the other tiles; "highlight": they only highlight; "none").

kpi

kpi(y: str, *, w: int = 3, **chart: Any) -> Dashboard

Add a KPI card; compare="previous_year" adds the change on last year.

insights

insights(max_items: int = 6) -> Dashboard

Add a panel of plain-English insights about the dashboard's data.

to_dict

to_dict() -> dict[str, Any]

The dashboard as plain JSON, without any data.

bind

bind(data: Any, **datasets: Any) -> Dashboard

The same dashboard on new data. Every column it needs must exist ("did you mean" otherwise).

Tiles with their own data take it by name: bind(orders, targets=new_targets).

build

build() -> dict[str, Any]

The page payload: tiles, bindings and the shared table (plain JSON).

to_html

to_html(
    path: str | Path | None = None, *, cdn: bool = False
) -> str

One offline HTML file. cdn=True loads ECharts from jsDelivr (pinned by hash).

server

server(url: str) -> Any

Server mode: the data stays in Python and the page asks url for each filter change, so large data and every kind of tile stay exact. Returns a :class:tabsight.server.DashboardServer; ts.web has FastAPI and Flask helpers.

auto

auto(
    data: Any,
    title: str | None = None,
    *,
    max_tiles: int = 10,
    insights: bool = True,
    theme: str | None = None,
    locale: str | None = None,
    currency: str | None = None,
) -> Dashboard

A dashboard chosen from the data: KPIs, a trend, a map, bars, filters and insights.

ts.auto(df).to_html("dashboard.html")

dash.decisions says what was chosen and why; the result is an ordinary :class:Dashboard, so you can add tiles, change filters or save it.

Server mode and web apps

DashboardServer

Answers a server-mode dashboard page. Build it with dash.server(url).

payload

payload() -> dict[str, Any]

The page payload (no rows), for your own page that loads dashboard.js.

page

page(*, cdn: bool = False) -> str

The dashboard page. It may only talk to url (its Content Security Policy says so).

respond

respond(
    body: bytes | str | Mapping[str, Any],
) -> dict[str, Any]

Answer one request from the page (JSON in, JSON-ready dict out).

Raises :class:BadRequest (a ValueError) when the request is malformed; reply 400 then.

sanitize

sanitize(raw: Any) -> dict[str, dict[str, Any]]

Keep only filters on known columns with known values (as the page does).

serve_payload

serve_payload(
    obj: Any, request: Any = None
) -> dict[str, Any]

Plain JSON for your own page: a Figure's to_dict(), a Dashboard's payload, a server-mode answer (DashboardServer plus the request body), or, for a DataFrame, the payload of ts.auto(df). Draw it with tabsight.js / dashboard.js.

fastapi_router

fastapi_router(
    dash: Any,
    path: str = "/dashboard",
    *,
    cdn: bool = False,
) -> Any

A FastAPI APIRouter serving dash in server mode at path.

flask_blueprint

flask_blueprint(
    dash: Any,
    path: str = "/dashboard",
    *,
    name: str = "tabsight",
    cdn: bool = False,
) -> Any

A Flask Blueprint serving dash in server mode at path.

streamlit

streamlit(
    obj: Any,
    *,
    height: int | None = None,
    cdn: bool = False,
) -> Any

Show a Figure or Dashboard in a Streamlit app (in its own sandboxed frame).

dash_iframe

dash_iframe(
    obj: Any,
    *,
    height: int | None = None,
    cdn: bool = False,
    **props: Any,
) -> Any

A Plotly Dash html.Iframe showing a Figure or Dashboard (scripts only, no access to the host page).

Analytics and insights

insights

insights(
    data: Any,
    y: str | Sequence[str] | None = None,
    x: str | Sequence[str] | None = None,
    date: str | None = None,
    *,
    as_of: Any = None,
    max_items: int = 8,
    thresholds: Mapping[str, float] | None = None,
    locale: str | None = None,
    prof: Profile | None = None,
) -> Insights

Plain-English facts about data: peaks, trends, leaders, losses, outliers.

With no fields given, tabsight picks up to three measures (y), three category columns (x) and the first date column. Every sentence is a fact the numbers show, never a cause. thresholds changes when a rule speaks up (see THRESHOLDS).

Insights dataclass

Bases: Sequence[Insight]

The insights found, best first. Prints as a list; to_dict() for JSON.

Insight dataclass

One sentence and the numbers behind it.

compare

compare(
    data: Any,
    y: str,
    date: str,
    *,
    compare: str = "previous_year",
    as_of: Any = None,
    window: Any = None,
    agg: str | None = None,
    fiscal_start: int | None = None,
    locale: str | None = None,
    prof: Profile | None = None,
) -> Comparison

Compare y in the current window with the same window one period earlier.

compare: "previous_year" (year to date against the same days last year), "previous_quarter", "previous_month", or "previous_period" with window=(start, end) (that window against the one just before it, same length). window may also be given with the other choices, to compare a filtered range with the same range a year (quarter, month) earlier.

Comparison dataclass

One measure in two windows. change and pct are None when there is nothing to compare.

linear_trend

linear_trend(
    y: Sequence[float] | ndarray,
    x: Sequence[float] | ndarray | None = None,
) -> Trend

Fit a straight line by least squares. x defaults to 0, 1, 2, ... Missing values are skipped.

forecast

forecast(
    y: Sequence[float] | ndarray,
    horizon: int,
    season: int | None = None,
) -> Forecast

Forecast horizon steps ahead.

With a season (12 for months, 4 for quarters...) this is additive Holt-Winters and needs at least two full seasons of history; without one it is Holt's linear trend and needs at least 6 points. The smoothing weights are chosen on a fixed grid by the smallest one-step error, so the result is repeatable. The range is the typical one-step error times 1.96, widening as those errors add up over the horizon (the standard formula for additive Holt-Winters): a rough guide, not a guarantee.

outliers

outliers(
    values: Sequence[float] | ndarray | Series,
    method: str = "iqr",
    k: float | None = None,
) -> np.ndarray

A True/False mask of unusual values. Never removes anything.

"iqr": beyond k (1.5) box heights from the middle half (Tukey's fences). "zscore": more than k (3) standard deviations from the mean.

reference_value

reference_value(
    values: Sequence[float] | ndarray, ref: str | float
) -> float | None

"mean", "median", "p90" (a percentile) or a constant, for the values shown.

Choosing and checking charts

recommend

recommend(
    data: Any,
    x: str | Sequence[str] | None = None,
    y: str | Sequence[str] | None = None,
    color: str | None = None,
    *,
    prof: Profile | None = None,
) -> Recommendations

Rank the charts that fit these fields. x and color are what you group by, y the measure(s).

Recommendations dataclass

Ranked suggestions (best first) plus the kinds that were ruled out, with reasons.

best property

best: Recommendation

The highest-scoring chart that can be drawn today.

why_not

why_not(kind: str) -> str

Why kind is not suggested (or where it ranks, if it is).

Recommendation dataclass

LintReport dataclass

ok property

ok: bool

True when there are no warnings (info-level notes are fine).

LintIssue dataclass

runtime_js

runtime_js() -> str

The tabsight browser runtime, for web pages that draw Figure.to_dict() payloads.

Load ECharts first, then this script, then call tabsight.render(element, payload).

Notebooks and df.plot

plot

plot(
    data: DataFrame | Series,
    kind: str = "line",
    **kwargs: Any,
) -> Figure

Entry point pandas calls for df.plot(...) when the backend is "tabsight".

DataFrameAccessor

tabsight on a DataFrame: df.ts.chart(...), df.ts.bar(...), df.ts.profile()...

chart

chart(kind: str = 'auto', **kwargs: Any) -> Figure

ts.chart(df, kind, ...); the default kind is "auto".

SeriesAccessor

tabsight on one column: s.ts.hist(), s.ts.line() (over the index), s.ts.bar()...

Maps

geo

World maps: bundled shapes, place-name matching and map detection.

Everything here works offline from data bundled with tabsight (Natural Earth, public domain): about 240 countries, level-1 regions for every country (states, provinces, counties...) with useful groupings such as the UK's nations, and about 7,300 cities.

ts.geo.detect(df["County"])        # which map these values belong on
ts.geo.match(df["County"])         # every value matched, with what didn't match and why
ts.geo.layers("United Kingdom")    # ['admin1', 'nation', 'region']
ts.geo.coverage()                  # one row per country: regions, type, layers

GeoTarget dataclass

Which map a column belongs on.

GeoMatch dataclass

Every distinct value and what it matched, plus what didn't match and why.

detect

detect(
    values: Iterable[Any],
    *,
    country_column: Iterable[Any] | None = None,
    level: str | None = None,
    country: str | None = None,
) -> GeoTarget | None

Which map values belong on: the world, a country's regions, or cities.

Returns None when fewer than half of the distinct values are places. Pass a country_column (same rows) to settle names that exist in several countries, or level / country to narrow the search.

match

match(
    values: Iterable[Any],
    *,
    country_column: Iterable[Any] | None = None,
    level: str | None = None,
    country: str | None = None,
    layer: str | None = None,
    geo_map: Mapping[str, str] | None = None,
) -> GeoMatch

Match every distinct value to a country, region or city. Unmatched values are listed with a "did you mean" suggestion; geo_map fixes your own spellings.

layers

layers(country: str) -> list[str]

The region layers bundled for a country ("admin1" is Natural Earth's level 1).

country_code

country_code(name: str) -> str

ISO 3166-1 alpha-3 code for a country name or code: "Ireland" -> "IRL".

coverage

coverage() -> pd.DataFrame

One row per country: name, code, number of level-1 regions, what they are, layers.

version

version() -> dict[str, Any]

The bundled map data version (Natural Earth release and build settings).

Calculated fields

calc

calc(
    definition: str, *, name: str | None = None
) -> CalcField

Parse "Name = formula" (or a formula plus name=) into a CalcField.

CalcField dataclass

aggregates property

aggregates: tuple[Call, ...]

Each aggregate call, once, in order of appearance.

needs_server property

needs_server: bool

Row-level formulas with parameters cannot be recomputed in an offline HTML file.

evaluate

evaluate(
    data: DataFrame, params: Mapping[str, Any] | None = None
) -> pd.Series

Row-level formulas only: one value per row.

Formatting

format_number

format_number(
    value: Any,
    unit: str = "number",
    style: str = "full",
    *,
    locale: str | None = None,
    currency: str | None = None,
    decimals: int | None = None,
    percent_scale: str = "fraction",
    duration_unit: str = "seconds",
    negative: str = "minus",
    sign: bool = False,
    pattern: str | None = None,
) -> str

Format one value. Missing, NaN and infinite values give "".

format_date

format_date(
    value: Any,
    grain: str = "day",
    style: str = "full",
    *,
    locale: str | None = None,
    fiscal_start: int | None = None,
) -> str

2017, Q1 2017 / FY2018 Q1, Jan 2017 (axis: Jan '17), Jan 5, 2017.

parse_pattern

parse_pattern(pattern: str) -> Pattern

Parse a small Excel-style pattern: #,##0, 0.0%, "€"#,##0.00, 0.0" kg".

Colours and themes

ColorRegistry

Assigns palette colours to values, stably: values are sorted before assignment, so the same data always gets the same colours regardless of row order.

assign

assign(values: Iterable[Any]) -> None

Give colours to new values, in natural sort order.

validate_color

validate_color(value: Any) -> str

Return a normalised lower-case hex colour, or raise InvalidSpecError.

contrast_ratio

contrast_ratio(a: str, b: str) -> float

WCAG contrast ratio between two colours (1-21). Text needs 4.5 for AA.

custom_theme

custom_theme(base: str = 'light', **changes: Any) -> Theme

A theme based on a built-in one, for example brand colours. Contrast is checked.

Theme dataclass

Settings

set_defaults

set_defaults(**kwargs: Any) -> Settings

Change process-wide defaults, for example ts.set_defaults(locale="en_IE").

config

Scoped settings: with ts.config(locale="de_DE"): ....

Only code running in the same thread or async task sees the change. Calling it without with has no effect, so tabsight warns and points to set_defaults.

current

current() -> Settings

The settings in effect here (scoped settings win over process defaults).

Settings dataclass

All global options. Every field can also be passed to individual calls.

Chart specs

ChartSpec dataclass