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
¶
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
¶
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.
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),Measureobjects 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 inresult.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).measuresmay also hold calculated fields fromts.calc(...);paramssupplies their$Nameparameter 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.
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.
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
¶
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
¶
Recommendation
dataclass
¶
LintReport
dataclass
¶
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.
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.