Skip to main content

:::note Generated reference Generated from honeyframeapi 0.1.8 — do not edit by hand. :::

honeyframeapi.statistics

Statistics worksheets — run the EDA cards from code.

A Honeyframe dataset's Statistics tab hosts persisted worksheets of analysis cards. This module is the same surface for a notebook or a script: one method per card type, computed against the dataset's real data by the platform (Postgres warehouse datasets run on the app session, uploaded-file datasets on DuckDB over their Parquet — identical SQL either way).

ds = client.get_project(1).get_dataset("fact_orders")

ds.statistics.univariate("age") # summary + histogram
ds.statistics.correlation(["age", "order_value", "cost"])
ds.statistics.ttest_2samp("order_value", "store_key") # Welch
ds.statistics.pca(["age", "order_value", "cost"], n_components=2)

Every card is also reachable generically, which is what you want when the card type is itself data (a sweep over a config table):

for spec in my_cards:
ds.statistics.compute(spec["type"], **spec["params"])

A worksheet is the persisted version — the same rows the Statistics tab shows, so a worksheet built here is editable in the UI and vice versa:

ws = ds.statistics.create_worksheet("Length-of-stay EDA")
ws.add_card("univariate", column="order_value")
ws.add_card("anova_1way", column="order_value", group="store_key")
for entry in ws.compute_all():
print(entry["card"]["type"], entry["result"])

The 17 card types, exactly as the platform's CARD_TYPES defines them, are in CARD_TYPES. An unknown type raises ValueError here rather than costing a 400 round-trip; the per-card methods below document each one's parameters and result keys.

Two honest limits worth knowing before you build on this:

  • Worksheets only exist on a migrated tenant. The platform degrades a missing honeyframe.dataset_worksheets table to an empty list, so DatasetStatistics.list_worksheets returning [] can mean "none yet" or "this install has not run the worksheet migration". Ad-hoc DatasetStatistics.compute needs no table and works either way.
  • Warehouse + uploaded-file datasets only in v1. A lakehouse or external connector dataset raises HoneyframeValidationError carrying the platform's reason — it never silently returns a partial answer.

Backed by /api/statistics. Docs: https://docs.honeyframe.io/

class DatasetStatistics​

DatasetStatistics(dataset)

The statistics surface of one dataset: ad-hoc cards + saved worksheets.

Reached as dataset.statistics; holds no state of its own, so it is free to construct.

DatasetStatistics.list_worksheets()​

list_worksheets() -> list

Saved worksheets for this dataset, oldest first.

An empty list can also mean the install has not run the worksheet migration — the platform degrades a missing table to [] so the Statistics tab still reads correctly.

DatasetStatistics.create_worksheet()​

create_worksheet(name: str = 'Worksheet 1') -> StatisticsWorksheet

Create an empty worksheet on this dataset and return its handle.

DatasetStatistics.get_worksheet()​

get_worksheet(worksheet_id: int) -> StatisticsWorksheet

Return a worksheet handle. Cards are fetched on first use (there is no per-worksheet GET — the handle reads through the list endpoint).

DatasetStatistics.compute()​

compute(card_type: str, **params: Any) -> dict

Compute any card type against this dataset, without saving it.

card_type must be one of CARD_TYPES — a typo raises ValueError here. params are the card's own parameters; the typed methods below are thin wrappers over this call and document them. Unrecognised parameters are forwarded as-is, so a parameter the platform gains later needs no SDK release.

DatasetStatistics.compute_config()​

compute_config(card: dict) -> dict

Compute a {type, params} (or {id, type, params}) card config — the stored shape, so a worksheet card round-trips through this.

DatasetStatistics.univariate()​

univariate(column: str) -> dict

One column's summary — any type.

Returns {type, column, numeric, stats} with stats = {count, non_null, nulls, distinct} plus {min, max, avg, stddev} when the column is numeric. Numeric columns also carry histogram (20 equal-width bins: {bucket, low, high, count}); non-numeric ones carry topk ({value, count}, top 20). Values the caller is not cleared to see are masked by the platform, never dropped.

DatasetStatistics.correlation()​

correlation(columns: list) -> dict

Pairwise Pearson correlation across 2–12 numeric columns.

Returns {type, columns, matrix} — matrix is the full symmetric square (diagonal 1.0); a pair with no overlapping non-null rows is None, not 0.

DatasetStatistics.scatter_fit()​

scatter_fit(x: str, y: str) -> dict

Two numeric columns with a least-squares line.

Returns {type, x, y, points, sampled, fit}; fit = {slope, intercept, r, n} is computed from SQL regr_* aggregates over every row, while points is capped at the first 2,000 rows the engine returns — so sampled=True means the plotted points are partial (and unordered, not a random draw), while the fit is not.

DatasetStatistics.quantile_table()​

quantile_table(columns: list) -> dict

Quantiles per numeric column (up to 12 columns).

Returns {type, columns, quantiles, values} — quantiles is the probability grid the platform uses (0, .05, .25, .5, .75, .95, .99, 1) and values[i][j] is columns[i] at quantiles[j].

DatasetStatistics.cdf_plot()​

cdf_plot(column: str) -> dict

Empirical cumulative distribution of one numeric column.

Returns {type, column, points, method} — points is [[value, probability], …] on a 100-step percentile grid.

DatasetStatistics.fit_distribution()​

fit_distribution(column: str) -> dict

Normal + lognormal fit overlaid on the histogram.

Returns {type, column, n, total, bins, bin_width, normal, lognormal, ks_note}. Each bin carries the observed count and the fitted normal (and lognormal) expected count. lognormal is None when the column is not strictly positive — the platform omits the fit rather than inventing one. Compare fits by ks_distance (lower is better).

DatasetStatistics.shapiro_wilk()​

shapiro_wilk(column: str) -> dict

Shapiro-Wilk normality test (Royston 1992 / AS R94).

Returns {type, column, available, …}. When available is True: {w, p, n, sampled, note, method} — over 5,000 non-null values the test runs on a random 5,000-value subsample (sampled=True, with note saying so) because the approximation is only valid there. When False, reason says why — an honest unavailable result, never a fabricated statistic. Check available before reading p.

DatasetStatistics.chi2_independence_test()​

chi2_independence_test(column_a: str, column_b: str) -> dict

Chi-square test of independence between two categorical columns.

Returns {type, column_a, column_b, row_levels, col_levels, table, chi2, p, dof, …}. Low-cardinality only (≤50 levels per dimension); a wider pair is a validation error naming the cap. Level labels are masked per the caller's identity; the statistic is computed on the raw levels.

DatasetStatistics.ttest_2samp()​

ttest_2samp(column: str, group: str) -> dict

Welch two-sample t-test of column across exactly 2 groups.

Returns {type, column, group, groups, t, df, p} — groups is [{name, n, mean, var}, …]. A grouping column with any count other than 2 distinct values is a validation error listing what was found.

DatasetStatistics.mann_whitney_u()​

mann_whitney_u(column: str, group: str) -> dict

Mann-Whitney U (Wilcoxon rank-sum) — the non-parametric counterpart of ttest_2samp, also exactly 2 groups.

Returns {type, column, group, groups, u, p, n1, n2, sampled, small_sample, …}. small_sample=True flags that the normal approximation is rough at this n — the platform surfaces it rather than hiding it; sampled=True means a per-group cap (10,000 values) was hit.

DatasetStatistics.paired_ttest()​

paired_ttest(column_x: str, column_y: str) -> dict

Paired (dependent-samples) t-test over two numeric columns measured on the same rows.

Returns {type, column_x, column_y, available, …} — when available is True: {t, p, n, mean_diff, sampled, …}. Rows where either column is NULL are dropped as incomplete pairs; fewer than 2 complete pairs comes back available=False with a reason.

DatasetStatistics.anova_1way()​

anova_1way(column: str, group: str) -> dict

One-way ANOVA — does column's mean differ across the groups?

Returns {type, column, group, groups, groups_cap, f, p, df_between, df_within, …}; groups is [{name, n, mean, var}, …]. Needs 2–50 groups (groups_cap); a higher-cardinality grouping column is a validation error, not a 50-row truncation.

DatasetStatistics.levene_test()​

levene_test(column: str, group: str) -> dict

Levene's test for equal variance, Brown-Forsythe (median-centered).

Returns {type, column, group, groups, groups_cap, sampled, center, w, p, df_between, df_within, k, n_total}. Run it before leaning on a test that assumes equal variances — anova_1way's F-test.

DatasetStatistics.ks_test_2samp()​

ks_test_2samp(column: str, group: str) -> dict

Two-sample Kolmogorov-Smirnov test — distribution shape across exactly 2 groups (where the t-test compares only means).

Returns {type, column, group, groups, sampled, d, p, n1, n2, …}. sampled=True means a pull/per-group cap was reached, so d is over a sample.

DatasetStatistics.acf_plot()​

acf_plot(date_column: str, value_column: str, *, agg: str = 'sum', max_lag: Optional[int] = None) -> dict

Autocorrelation of a daily-resampled series.

agg ∈ AGGREGATIONS is how rows collapse per calendar day. sum/count zero-fill missing days (a day with no rows genuinely measures 0); avg drops them (a missing day has no average, and zero-filling would fabricate a downward trend) — gap_filled reports how many were filled. max_lag is clamped server-side to 1–30.

Returns {type, date_column, value_column, agg, n, gap_filled, conf, acf, range} — conf is the ±95% white-noise band, so a lag matters when abs(acf[lag]) > conf.

DatasetStatistics.mann_kendall_test()​

mann_kendall_test(date_column: str, value_column: str, *, agg: str = 'sum') -> dict

Mann-Kendall monotonic-trend test on a daily-resampled series.

Same agg resampling (and gap-filling rule) as acf_plot. Returns {type, date_column, value_column, agg, gap_filled, range, trend, s, z, p, tau, …}. The p-value is the method's own normal approximation — labelled as such by the platform, not presented as exact.

DatasetStatistics.pca()​

pca(columns: list, *, n_components: Optional[int] = None, standardize: bool = True, color_by: Optional[str] = None) -> dict

Principal Component Analysis over 2–12 numeric columns.

n_components defaults to min(5, len(columns)) server-side and may not exceed the column count. standardize=True (the default) runs the decomposition on the correlation matrix — leave it on when the columns are in different units, or the largest-scale column dominates PC1. color_by tints the projection by another column's value.

Returns {type, columns, color_by, n_total, dropped_nulls, sampled, constant_columns, points, point_colors, points_sampled, n_components, n_columns, standardize, total_variance, explained_variance, explained_variance_ratio, cumulative_variance_ratio, loadings, center, scale}. Rows with a NULL in any selected column are excluded — dropped_nulls says how many. points is the PC1×PC2 projection and is empty at n_components=1 (there is no PC2; a flat line would read as real structure). constant_columns names zero-variance columns, which carry no information into the fit.

class StatisticsWorksheet​

StatisticsWorksheet(statistics: DatasetStatistics, worksheet_id: int, *, name: str = '', cards: Optional[list] = None, _data: Optional[dict] = None)

A saved worksheet: an ordered list of {id, type, params} cards.

The same rows the dataset's Statistics tab reads, so a worksheet authored here opens in the UI (and edits made there show up on reload). Results are never stored — compute_all recomputes against live data.

StatisticsWorksheet.reload()​

reload() -> StatisticsWorksheet

Re-read this worksheet through the dataset's list endpoint (there is no per-worksheet GET). Raises ValueError when the id is not on this dataset — including after someone else deleted it.

StatisticsWorksheet.cards​

property

cards: list

The stored card configs, in worksheet order (fetched on first use).

StatisticsWorksheet.get_card()​

get_card(card_id: str) -> dict

One stored card config by its id.

StatisticsWorksheet.rename()​

rename(name: str) -> StatisticsWorksheet

Rename the worksheet (truncated to 200 chars server-side).

StatisticsWorksheet.set_cards()​

set_cards(cards: list) -> StatisticsWorksheet

Replace the whole card list. Every card is validated here first, so a bad type cannot overwrite a good worksheet with a rejected write.

Accepts configs from stat_card (or any {id, type, params} dict); a card without an id gets a fresh uuid4 so the UI and remove_card can address it.

StatisticsWorksheet.add_card()​

add_card(card_type: str, **params: Any) -> dict

Append one card and persist the worksheet. Returns the new card config (its id is what compute_card takes).

StatisticsWorksheet.update_card()​

update_card(card_id: str, **params: Any) -> dict

Replace a stored card's parameters (the type is unchanged) and persist. Returns the updated config.

StatisticsWorksheet.remove_card()​

remove_card(card_id: str) -> StatisticsWorksheet

Remove one card by id and persist. Raises when no card has that id — a silent no-op would read as a successful delete.

StatisticsWorksheet.delete()​

delete() -> None

Delete the worksheet and its card configs.

StatisticsWorksheet.compute_card()​

compute_card(card_id: str) -> dict

Compute one stored card against live data.

StatisticsWorksheet.compute_all()​

compute_all() -> list

Compute every card, in worksheet order.

Returns [{"card": <config>, "result": <payload>}, …]. A card the platform rejects (too-high cardinality, a dropped column) raises — the error names the card's own reason, and swallowing it would hand back a worksheet that looks complete. Use compute_card per card when you want to continue past one.

Functions​

validate_card_type()​

validate_card_type(card_type: Any) -> str

Return card_type if the platform has such a card, else raise.

Raises ValueError naming the nearest match and the full list — a typo is a local failure, not a 400 from the server.

validate_card()​

validate_card(card_type: Any, params: Optional[dict] = None) -> str

Validate a card type + its parameters client-side. Returns the type.

Checks the type against CARD_TYPES, that every required parameter is present and non-empty, the structural columns minimums, and agg against AGGREGATIONS. Everything else is left to the server, which owns the column/type/cardinality rules and reports them with real numbers.

stat_card()​

stat_card(card_type: str, **params: Any) -> dict

Build one validated worksheet-card config: {id, type, params}.

The id is a fresh uuid4 — the same shape the Statistics tab generates — and is what StatisticsWorksheet.remove_card and StatisticsWorksheet.compute_card address a card by. Use this when writing a whole worksheet at once with StatisticsWorksheet.set_cards.

Constants​

NameValue
AGGREGATIONS('sum', 'avg', 'count')

Verified against honeyframeapi 0.1.8.