From 3ccbd52c87231518a17545a9dfc6ff94e1d1bcf6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:34:00 +0000 Subject: [PATCH 1/7] Iteration 174: Add pct_change for Series and DataFrame Implements pctChangeSeries() and pctChangeDataFrame() mirroring pandas.Series.pct_change() / pandas.DataFrame.pct_change(). - periods: configurable lag (positive = backward, negative = forward) - fillMethod: "pad" (default), "bfill", or null (no fill) - limit: cap consecutive fills - axis: column-wise (default) or row-wise for DataFrame Full test coverage: unit tests, edge cases, and fast-check property tests. Interactive playground page at playground/pct_change.html. Run: https://github.com/githubnext/tsessebe/actions/runs/24266545401 --- playground/index.html | 5 + playground/pct_change.html | 448 +++++++++++++++++++++++++++++++++ src/index.ts | 6 + src/stats/index.ts | 6 + src/stats/pct_change.ts | 231 +++++++++++++++++ tests/stats/pct_change.test.ts | 252 +++++++++++++++++++ 6 files changed, 948 insertions(+) create mode 100644 playground/pct_change.html create mode 100644 src/stats/pct_change.ts create mode 100644 tests/stats/pct_change.test.ts diff --git a/playground/index.html b/playground/index.html index 48bfbcb9..3addee3e 100644 --- a/playground/index.html +++ b/playground/index.html @@ -264,6 +264,11 @@

βœ… Complete +
+

πŸ“Š pct_change

+

Fractional change between elements. pctChangeSeries() and pctChangeDataFrame() with periods, fillMethod (pad/bfill), limit, and axis options.

+
βœ… Complete
+
diff --git a/playground/pct_change.html b/playground/pct_change.html new file mode 100644 index 00000000..3576797a --- /dev/null +++ b/playground/pct_change.html @@ -0,0 +1,448 @@ + + + + + + tsb β€” pct_change + + + +
+
+
Initializing playground…
+
+ ← Back to roadmap +

πŸ“Š pct_change β€” Interactive Playground

+

Compute the fractional change between each element and a prior element. + Mirrors pandas.Series.pct_change() / + pandas.DataFrame.pct_change().
+ Edit any code block below and press β–Ά Run + (or Ctrl+Enter) to execute it live in your browser. +

+ + +
+

1 Β· Basic pct_change on a Series

+

pctChangeSeries(series) returns the fractional (not percentage) change + from each previous element. The first element is always null.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

2 Β· Multi-period change

+

The periods option controls the lag. Use periods: 2 to + compare each value to the one two steps earlier β€” useful for month-over-month + comparisons in quarterly data.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

3 Β· Handling missing values

+

By default, pctChangeSeries forward-fills (fillMethod: "pad") + NaN/null values before computing the ratio β€” so gaps don't break the chain. + Set fillMethod: null to propagate NaN instead.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

4 Β· Limit consecutive fills

+

The limit option caps how many consecutive NaN values get forward-filled. + Useful when you want to tolerate short gaps but not bridge large ones.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

5 Β· DataFrame column-wise pct_change

+

pctChangeDataFrame(df) applies pctChangeSeries to every + column independently. Ideal for comparing multiple assets or metrics simultaneously.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

6 Β· Negative periods (look-forward change)

+

A negative periods value computes the forward change: how much will + this element change by the time we reach |periods| steps ahead. + Useful for computing returns on a "hold for N periods" strategy.

+
+
+ TypeScript +
+ + +
+
+ + +
Click β–Ά Run to execute
+
Ctrl+Enter to run Β· Tab to indent
+
+
+ + +
+

API Reference

+

All functions return a new Series/DataFrame of the same shape β€” inputs are never mutated.

+
// Series
+pctChangeSeries(series, {
+  periods?: number,           // default 1 (positive = look back, negative = look forward)
+  fillMethod?: "pad" | "bfill" | null,  // default "pad"
+  limit?: number | null,      // max consecutive fills; default unlimited
+}): Series
+
+// DataFrame
+pctChangeDataFrame(df, {
+  periods?: number,
+  fillMethod?: "pad" | "bfill" | null,
+  limit?: number | null,
+  axis?: 0 | 1 | "index" | "columns",  // default 0 (column-wise)
+}): DataFrame
+
+ + + + + diff --git a/src/index.ts b/src/index.ts index 1dd0aa57..d8f392de 100644 --- a/src/index.ts +++ b/src/index.ts @@ -107,3 +107,9 @@ export { export type { ClipOptions, RoundOptions, DataFrameElemOptions } from "./stats/index.ts"; export { valueCounts, dataFrameValueCounts } from "./stats/index.ts"; export type { ValueCountsOptions, DataFrameValueCountsOptions } from "./stats/index.ts"; +export { pctChangeSeries, pctChangeDataFrame } from "./stats/index.ts"; +export type { + PctChangeFillMethod, + PctChangeOptions, + DataFramePctChangeOptions, +} from "./stats/index.ts"; diff --git a/src/stats/index.ts b/src/stats/index.ts index b1de48eb..401233bc 100644 --- a/src/stats/index.ts +++ b/src/stats/index.ts @@ -39,3 +39,9 @@ export { nsmallestDataFrame, } from "./nlargest.ts"; export type { NKeep, NTopOptions, NTopDataFrameOptions } from "./nlargest.ts"; +export { pctChangeSeries, pctChangeDataFrame } from "./pct_change.ts"; +export type { + PctChangeFillMethod, + PctChangeOptions, + DataFramePctChangeOptions, +} from "./pct_change.ts"; diff --git a/src/stats/pct_change.ts b/src/stats/pct_change.ts new file mode 100644 index 00000000..c46c9e84 --- /dev/null +++ b/src/stats/pct_change.ts @@ -0,0 +1,231 @@ +/** + * pct_change β€” percentage change between current and prior element. + * + * Mirrors `pandas.Series.pct_change()` / `pandas.DataFrame.pct_change()`: + * - `pctChangeSeries(series, options)` β€” per-element % change + * - `pctChangeDataFrame(df, options)` β€” column-wise % change + * + * Formula (per element i, with shift=periods): + * `result[i] = (x[i] - x[i-periods]) / x[i-periods]` + * + * When `fillMethod` is set, NaN/null values in the source are filled *before* + * computing the ratio (matching pandas' default behaviour of `fill_method="pad"`). + * + * @module + */ + +import { DataFrame } from "../core/index.ts"; +import { Series } from "../core/index.ts"; +import type { Scalar } from "../types.ts"; + +// ─── public types ───────────────────────────────────────────────────────────── + +/** Fill method applied to NaN/null before computing pct_change. */ +export type PctChangeFillMethod = "pad" | "bfill"; + +/** Options for {@link pctChangeSeries} and {@link pctChangeDataFrame}. */ +export interface PctChangeOptions { + /** + * Number of periods (lags) to shift when computing the ratio. + * Positive values look backward; negative values look forward. + * Default `1`. + */ + readonly periods?: number; + /** + * How to fill NaN/null values *before* computing the ratio. + * - `"pad"` (default): forward-fill (last valid observation carries forward). + * - `"bfill"`: backward-fill (next valid observation fills backward). + * - `null`: no filling β€” NaN/null stays as-is. + */ + readonly fillMethod?: PctChangeFillMethod | null; + /** + * Maximum number of consecutive NaN/null values to fill when `fillMethod` + * is set. `undefined` / `null` means no limit. + */ + readonly limit?: number | null; +} + +/** Options for {@link pctChangeDataFrame} β€” adds an axis selector. */ +export interface DataFramePctChangeOptions extends PctChangeOptions { + /** + * - `0` or `"index"` (default): apply operation **column-wise** (down rows). + * - `1` or `"columns"`: apply operation **row-wise** (across columns). + */ + readonly axis?: 0 | 1 | "index" | "columns"; +} + +// ─── helpers ────────────────────────────────────────────────────────────────── + +/** True when `v` is a valid number (not null, undefined, or NaN). */ +function isNum(v: Scalar): v is number { + return typeof v === "number" && !Number.isNaN(v) && v !== null; +} + +/** + * Forward-fill an array of scalars in place, respecting an optional limit. + * Returns a NEW array. + */ +function padFill(vals: readonly Scalar[], limit: number | null | undefined): Scalar[] { + const out: Scalar[] = [...vals]; + let run = 0; + let lastValid: Scalar = null; + for (let i = 0; i < out.length; i++) { + const v = out[i] as Scalar; + if (v !== null && v !== undefined && !(typeof v === "number" && Number.isNaN(v))) { + lastValid = v; + run = 0; + } else if (lastValid !== null && (limit == null || run < limit)) { + out[i] = lastValid; + run++; + } + } + return out; +} + +/** + * Backward-fill an array of scalars, respecting an optional limit. + * Returns a NEW array. + */ +function bfillFill(vals: readonly Scalar[], limit: number | null | undefined): Scalar[] { + const tmp = padFill([...vals].reverse(), limit); + return tmp.reverse(); +} + +/** Fill NaN/null in `vals` using the requested method. */ +function applyFill( + vals: readonly Scalar[], + method: PctChangeFillMethod | null | undefined, + limit: number | null | undefined, +): Scalar[] { + if (!method) return [...vals]; + return method === "pad" ? padFill(vals, limit) : bfillFill(vals, limit); +} + +/** Compute pct_change on a flat array of scalars. */ +function computePct(vals: readonly Scalar[], periods: number): Scalar[] { + const n = vals.length; + const out: Scalar[] = new Array(n).fill(null); + const shift = periods; + if (shift >= 0) { + for (let i = shift; i < n; i++) { + const curr = vals[i] as Scalar; + const prev = vals[i - shift] as Scalar; + if (isNum(curr) && isNum(prev) && prev !== 0) { + out[i] = curr / prev - 1; + } else if (isNum(curr) && isNum(prev) && prev === 0) { + // 0 denominator β†’ Infinity (same as pandas) + out[i] = curr === 0 ? Number.NaN : curr > 0 ? Infinity : -Infinity; + } else { + out[i] = null; + } + } + } else { + // Negative periods: look forward + const absShift = -shift; + for (let i = 0; i < n - absShift; i++) { + const curr = vals[i] as Scalar; + const fwd = vals[i + absShift] as Scalar; + if (isNum(curr) && isNum(fwd) && curr !== 0) { + out[i] = fwd / curr - 1; + } else if (isNum(curr) && isNum(fwd) && curr === 0) { + out[i] = fwd === 0 ? Number.NaN : fwd > 0 ? Infinity : -Infinity; + } else { + out[i] = null; + } + } + } + return out; +} + +// ─── public API ─────────────────────────────────────────────────────────────── + +/** + * Compute the fractional change between a Series element and the element + * `periods` positions earlier (or later, for negative `periods`). + * + * Matches `pandas.Series.pct_change()`. + * + * @example + * ```ts + * const s = new Series({ data: [100, 110, 99, 121] }); + * pctChangeSeries(s); // [null, 0.1, -0.1, 0.2222…] + * ``` + */ +export function pctChangeSeries(series: Series, options: PctChangeOptions = {}): Series { + const periods = options.periods ?? 1; + const fillMethod = options.fillMethod !== undefined ? options.fillMethod : "pad"; + const limit = options.limit ?? null; + + const filled = applyFill(series.values, fillMethod, limit); + const result = computePct(filled, periods); + + return new Series({ + data: result, + index: series.index, + name: series.name ?? undefined, + }); +} + +/** + * Compute percentage change for every column (or row) of a DataFrame. + * + * Matches `pandas.DataFrame.pct_change()`. + * + * @example + * ```ts + * const df = new DataFrame(new Map([ + * ["a", new Series({ data: [100, 110, 121] })], + * ["b", new Series({ data: [200, 180, 198] })], + * ])); + * pctChangeDataFrame(df); // fractional change per column + * ``` + */ +export function pctChangeDataFrame( + df: DataFrame, + options: DataFramePctChangeOptions = {}, +): DataFrame { + const axis = options.axis ?? 0; + const colWise = axis === 0 || axis === "index"; + + if (colWise) { + const colMap = new Map>(); + for (const name of df.columns.values) { + colMap.set(name, pctChangeSeries(df.col(name), options)); + } + return new DataFrame(colMap, df.index); + } + + // Row-wise: each row across columns + const periods = options.periods ?? 1; + const fillMethod = options.fillMethod !== undefined ? options.fillMethod : "pad"; + const limit = options.limit ?? null; + const nRows = df.index.length; + const cols = df.columns.values; + const nCols = cols.length; + + const resultCols = new Map(); + for (const name of cols) { + resultCols.set(name, new Array(nRows).fill(null)); + } + + for (let r = 0; r < nRows; r++) { + const row: Scalar[] = []; + for (const name of cols) { + row.push(df.col(name).values[r] as Scalar); + } + const filled = applyFill(row, fillMethod, limit); + const pct = computePct(filled, periods); + for (let c = 0; c < nCols; c++) { + (resultCols.get(cols[c] as string) as Scalar[])[r] = pct[c] as Scalar; + } + } + + const colMap = new Map>(); + for (const name of cols) { + colMap.set( + name, + new Series({ data: resultCols.get(name) as Scalar[], index: df.index, name }), + ); + } + return new DataFrame(colMap, df.index); +} diff --git a/tests/stats/pct_change.test.ts b/tests/stats/pct_change.test.ts new file mode 100644 index 00000000..98966e8c --- /dev/null +++ b/tests/stats/pct_change.test.ts @@ -0,0 +1,252 @@ +/** + * Tests for src/stats/pct_change.ts β€” pctChangeSeries, pctChangeDataFrame + */ +import { describe, expect, it } from "bun:test"; +import fc from "fast-check"; +import { + DataFrame, + Series, + pctChangeDataFrame, + pctChangeSeries, +} from "../../src/index.ts"; +import type { Scalar } from "../../src/index.ts"; + +// ─── helpers ───────────────────────────────────────────────────────────────── + +function s(data: readonly Scalar[]): Series { + return new Series({ data: [...data] }); +} + +function nanEq(a: Scalar, b: Scalar): boolean { + if (typeof a === "number" && Number.isNaN(a) && typeof b === "number" && Number.isNaN(b)) { + return true; + } + return a === b; +} + +function arrEq(a: readonly Scalar[], b: readonly Scalar[]): boolean { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (!nanEq(a[i] as Scalar, b[i] as Scalar)) return false; + } + return true; +} + +function close(a: Scalar, b: Scalar, eps = 1e-9): boolean { + if (a === null && b === null) return true; + if (typeof a !== "number" || typeof b !== "number") return false; + if (Number.isNaN(a) && Number.isNaN(b)) return true; + return Math.abs(a - b) < eps; +} + +function arrClose(a: readonly Scalar[], b: readonly Scalar[], eps = 1e-9): boolean { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (!close(a[i] as Scalar, b[i] as Scalar, eps)) return false; + } + return true; +} + +// ─── pctChangeSeries ───────────────────────────────────────────────────────── + +describe("pctChangeSeries", () => { + it("basic increasing sequence", () => { + const result = pctChangeSeries(s([100, 110, 121, 133.1])); + expect(result.values[0]).toBeNull(); + expect(close(result.values[1] as Scalar, 0.1)).toBe(true); + expect(close(result.values[2] as Scalar, 0.1)).toBe(true); + expect(close(result.values[3] as Scalar, 0.1)).toBe(true); + }); + + it("decreasing sequence", () => { + const result = pctChangeSeries(s([200, 180, 162])); + expect(result.values[0]).toBeNull(); + expect(close(result.values[1] as Scalar, -0.1)).toBe(true); + expect(close(result.values[2] as Scalar, -0.1)).toBe(true); + }); + + it("periods=2", () => { + const result = pctChangeSeries(s([100, 105, 110, 121]), { periods: 2 }); + expect(result.values[0]).toBeNull(); + expect(result.values[1]).toBeNull(); + expect(close(result.values[2] as Scalar, 0.1)).toBe(true); + expect(close(result.values[3] as Scalar, (121 - 105) / 105)).toBe(true); + }); + + it("negative periods (look forward)", () => { + const result = pctChangeSeries(s([100, 110, 121]), { periods: -1 }); + expect(close(result.values[0] as Scalar, 0.1)).toBe(true); + expect(close(result.values[1] as Scalar, 0.1)).toBe(true); + expect(result.values[2]).toBeNull(); + }); + + it("NaN/null propagates when fillMethod=null", () => { + const result = pctChangeSeries(s([100, null, 110]), { fillMethod: null }); + expect(result.values[0]).toBeNull(); + expect(result.values[1]).toBeNull(); + expect(result.values[2]).toBeNull(); + }); + + it("fillMethod=pad fills NaN before computing", () => { + const result = pctChangeSeries(s([100, null, 110]), { fillMethod: "pad" }); + // after pad-fill: [100, 100, 110] + // pct: [null, 0, 0.1] + expect(result.values[0]).toBeNull(); + expect(close(result.values[1] as Scalar, 0)).toBe(true); + expect(close(result.values[2] as Scalar, 0.1)).toBe(true); + }); + + it("fillMethod=bfill fills NaN backward before computing", () => { + const result = pctChangeSeries(s([100, null, 110, 121]), { fillMethod: "bfill" }); + // after bfill: [100, 110, 110, 121] + // pct: [null, 0.1, 0, 0.1] + expect(result.values[0]).toBeNull(); + expect(close(result.values[1] as Scalar, 0.1)).toBe(true); + expect(close(result.values[2] as Scalar, 0)).toBe(true); + expect(close(result.values[3] as Scalar, 0.1)).toBe(true); + }); + + it("limit=1 caps forward-fill", () => { + const result = pctChangeSeries(s([100, null, null, 130]), { + fillMethod: "pad", + limit: 1, + }); + // after pad with limit=1: [100, 100, null, 130] + // pct: [null, 0, null, null] (null/100 β†’ null) + expect(result.values[0]).toBeNull(); + expect(close(result.values[1] as Scalar, 0)).toBe(true); + expect(result.values[2]).toBeNull(); + expect(result.values[3]).toBeNull(); + }); + + it("zero denominator returns Infinity", () => { + const result = pctChangeSeries(s([0, 10]), { fillMethod: null }); + expect(result.values[1]).toBe(Infinity); + }); + + it("zero/zero denominator returns NaN", () => { + const result = pctChangeSeries(s([0, 0]), { fillMethod: null }); + expect(Number.isNaN(result.values[1] as number)).toBe(true); + }); + + it("preserves Series name and index", () => { + const src = new Series({ data: [10, 20, 30], name: "price" }); + const result = pctChangeSeries(src); + expect(result.name).toBe("price"); + expect(result.index.length).toBe(3); + }); + + it("empty series returns empty", () => { + const result = pctChangeSeries(s([])); + expect(result.values.length).toBe(0); + }); + + it("single-element series returns [null]", () => { + const result = pctChangeSeries(s([42])); + expect(result.values[0]).toBeNull(); + }); +}); + +// ─── pctChangeDataFrame ─────────────────────────────────────────────────────── + +describe("pctChangeDataFrame", () => { + it("column-wise (default)", () => { + const df = new DataFrame( + new Map([ + ["a", new Series({ data: [100, 110, 121] })], + ["b", new Series({ data: [200, 180, 198] })], + ]), + ); + const result = pctChangeDataFrame(df); + const colA = result.col("a").values; + const colB = result.col("b").values; + expect(colA[0]).toBeNull(); + expect(close(colA[1] as Scalar, 0.1)).toBe(true); + expect(close(colA[2] as Scalar, 0.1)).toBe(true); + expect(colB[0]).toBeNull(); + expect(close(colB[1] as Scalar, -0.1)).toBe(true); + expect(close(colB[2] as Scalar, 0.1)).toBe(true); + }); + + it("row-wise (axis=1)", () => { + const df = new DataFrame( + new Map([ + ["a", new Series({ data: [100, 200] })], + ["b", new Series({ data: [110, 220] })], + ["c", new Series({ data: [121, 242] })], + ]), + ); + const result = pctChangeDataFrame(df, { axis: 1 }); + // row 0: [100, 110, 121] β†’ [null, 0.1, 0.1] + // row 1: [200, 220, 242] β†’ [null, 0.1, 0.1] + const row0a = result.col("a").values[0]; + const row0b = result.col("b").values[0]; + const row0c = result.col("c").values[0]; + expect(row0a).toBeNull(); + expect(close(row0b as Scalar, 0.1)).toBe(true); + expect(close(row0c as Scalar, 0.1)).toBe(true); + const row1a = result.col("a").values[1]; + const row1b = result.col("b").values[1]; + expect(row1a).toBeNull(); + expect(close(row1b as Scalar, 0.1)).toBe(true); + }); + + it("preserves column order", () => { + const df = new DataFrame( + new Map([ + ["x", new Series({ data: [1, 2] })], + ["y", new Series({ data: [3, 6] })], + ]), + ); + const result = pctChangeDataFrame(df); + expect(result.columns.values).toEqual(["x", "y"]); + }); +}); + +// ─── property-based tests ───────────────────────────────────────────────────── + +describe("pctChangeSeries β€” property tests", () => { + it("result length equals input length", () => { + fc.assert( + fc.property(fc.array(fc.float({ noNaN: true }), { minLength: 0, maxLength: 50 }), (arr) => { + const result = pctChangeSeries(s(arr)); + return result.values.length === arr.length; + }), + ); + }); + + it("first element is always null for periods=1", () => { + fc.assert( + fc.property( + fc.array(fc.float({ noNaN: true }), { minLength: 1, maxLength: 50 }), + (arr) => { + const result = pctChangeSeries(s(arr)); + return result.values[0] === null; + }, + ), + ); + }); + + it("pct_change(x, -p) equals pct_change_reversed pattern", () => { + // For a sequence of positive numbers with periods=1 and periods=-1: + // result[-1][i] represents the change looking forward, so result[-1][i] = (x[i+1]-x[i])/x[i] + // and result[+1][i+1] = (x[i+1]-x[i])/x[i], so they should agree on matching indices + fc.assert( + fc.property( + fc.array(fc.float({ noNaN: true, min: 1, max: 1000 }), { minLength: 3, maxLength: 20 }), + (arr) => { + const fwd = pctChangeSeries(s(arr), { periods: -1, fillMethod: null }); + const bwd = pctChangeSeries(s(arr), { periods: 1, fillMethod: null }); + // fwd[i] = (arr[i+1] - arr[i]) / arr[i] + // bwd[i+1] = (arr[i+1] - arr[i]) / arr[i] ← same ratio + for (let i = 0; i < arr.length - 1; i++) { + if (!close(fwd.values[i] as Scalar, bwd.values[i + 1] as Scalar, 1e-6)) { + return false; + } + } + return true; + }, + ), + ); + }); +}); From c3c63273e424daf7dccae90ce768818ddbab2b54 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 02:59:24 +0000 Subject: [PATCH 2/7] Merge conflict resolution: integrate main's new feature exports while preserving pct_change This commit resolves merge conflicts between the PR branch and origin/main: - src/stats/index.ts: pct_change exports preserved alongside new exports from main - src/index.ts: pct_change re-exports preserved alongside new feature re-exports - playground/index.html: pct_change card preserved alongside new feature cards from main Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- playground/index.html | 80 ++++++++++ src/index.ts | 358 ++++++++++++++++++++++++++++++++++++++++++ src/stats/index.ts | 245 +++++++++++++++++++++++++++++ 3 files changed, 683 insertions(+) diff --git a/playground/index.html b/playground/index.html index 3addee3e..dba1dfdb 100644 --- a/playground/index.html +++ b/playground/index.html @@ -269,6 +269,86 @@

βœ… Complete +
+

πŸ“₯ insertColumn / popColumn

+

Insert and remove DataFrame columns at precise positions. insertColumn(df, loc, col, values) inserts at integer position, popColumn(df, col) returns { series, df }. Also includes reorderColumns and moveColumn. Mirrors pandas.DataFrame.insert() and .pop().

+
βœ… Complete
+
+
+

βœ‚οΈ cut / qcut

+

Bin continuous numeric data into discrete intervals. cut() uses fixed-width or explicit bin edges; qcut() uses quantile-based bins of equal population. Both return codes, labels, and bin edges. Mirrors pandas.cut and pandas.qcut.

+
βœ… Complete
+
+
+

πŸ“Š Rolling Extended Stats

+

Higher-order rolling window statistics: rollingSem (standard error of mean), rollingSkew (Fisher-Pearson skewness), rollingKurt (excess kurtosis), and rollingQuantile (arbitrary percentile with 5 interpolation methods). Mirrors pandas.Series.rolling().sem/skew/kurt/quantile().

+
βœ… Complete
+
+
+

πŸ”§ Rolling Apply & Multi-Agg

+

Standalone custom rolling-window functions: rollingApply (custom fn per window), rollingAgg (multiple named aggregations β†’ DataFrame), dataFrameRollingApply, dataFrameRollingAgg. Supports minPeriods, center, and raw mode. Mirrors pandas.Rolling.apply() and Rolling.agg().

+
βœ… Complete
+
+
+

🎭 where / mask

+

Element-wise conditional selection: seriesWhere / seriesMask and dataFrameWhere / dataFrameMask. Accepts boolean arrays, label-aligned boolean Series/DataFrame, or callables. Mirrors pandas.Series.where, pandas.DataFrame.where, and their .mask() inverses.

+
βœ… Complete
+
+
+

πŸ” isna / notna

+

Module-level missing-value detection: isna, notna, isnull, notnull work on scalars, arrays, Series, and DataFrames. Plus standalone fillna, dropna, countna, and countValid. Mirrors pandas.isna, pandas.notna, pandas.isnull, pandas.notnull.

+
βœ… Complete
+
+
+

🏷️ attrs β€” User Metadata

+

Attach arbitrary key→value metadata to any Series or DataFrame via a WeakMap registry. Provides getAttrs, setAttrs, updateAttrs, copyAttrs, withAttrs, mergeAttrs, clearAttrs, getAttr, setAttr, deleteAttr, attrsCount, attrsKeys. Mirrors pandas.DataFrame.attrs / pandas.Series.attrs.

+
βœ… Complete
+
+
+

πŸ”€ string_ops β€” Standalone String Ops

+

Module-level string utilities: strNormalize (Unicode NFC/NFD/NFKC/NFKD), strGetDummies (one-hot DataFrame), strExtractAll (all regex matches), strRemovePrefix, strRemoveSuffix, strTranslate (char-level substitution), strCharWidth (CJK-aware display width), strByteLength. Works on Series, arrays, or scalars.

+
βœ… Complete
+
+
+

πŸ”€ string_ops_extended β€” Extended String Ops

+

Advanced string utilities: strSplitExpand (split β†’ DataFrame columns), strExtractGroups (regex capture groups β†’ DataFrame), strPartition / strRPartition (split into before/sep/after), strMultiReplace (batch replacements), strIndent / strDedent (line-level indentation). Works on Series, arrays, or scalars.

+
βœ… Complete
+
+
+

πŸ”— pipe_apply β€” Pipeline & Apply Utilities

+

Standalone equivalents of pandas' pipe() / apply() / applymap(): pipe (variadic type-safe pipeline), seriesApply (element-wise with label/pos context), seriesTransform, dataFrameApply (axis 0/1), dataFrameApplyMap (cell-wise), dataFrameTransform (column-wise), dataFrameTransformRows (row-wise).

+
βœ… Complete
+
+
+

πŸ”’ numeric_extended β€” Numeric Utilities

+

numpy/scipy-style numeric utilities: digitize (bin values), histogram (frequency counts with density option), linspace / arange (number sequences), percentileOfScore (percentile rank of a score), zscore (z-score standardisation), minMaxNormalize (scale to [0,1] or custom range), coefficientOfVariation (std/mean). Series-aware variants included.

+
βœ… Complete
+
+ +
+
+

🏷️ categorical_ops β€” Categorical Utilities

+

Standalone categorical helpers: catFromCodes (from integer codes), set operations (catUnionCategories, catIntersectCategories, catDiffCategories, catEqualCategories), catSortByFreq, catToOrdinal, catFreqTable, catCrossTab, catRecode.

+
βœ… Complete
+
+
+
+
+

πŸ”’ format_ops β€” Number Formatting

+

Number-formatting helpers for Series and DataFrame. Scalar formatters: formatFloat, formatPercent, formatScientific, formatEngineering, formatThousands, formatCurrency, formatCompact. Formatter factories: makeFloatFormatter, makePercentFormatter, makeCurrencyFormatter. Apply to collections: applySeriesFormatter, applyDataFrameFormatter. Render to string: seriesToString, dataFrameToString.

+
βœ… Complete
+
+
+ + +
+

Performance

+
+
+

⚑ Benchmarks

+

Side-by-side performance comparison of tsb (TypeScript/Bun) vs pandas (Python). Timing metrics for each function.

+
πŸ—οΈ In Progress
+
diff --git a/src/index.ts b/src/index.ts index d8f392de..3f970c69 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ * * @packageDocumentation */ +// merged: 2026-04-09T19:37Z (re-merge main into PR branch, barrel-export conflicts resolved by keeping PR superset) // Core exports will be added here as features are implemented. // Each module is imported and re-exported from its feature file in src/. @@ -45,12 +46,16 @@ export { DatetimeAccessor } from "./core/index.ts"; export type { DatetimeSeriesLike } from "./core/index.ts"; export { DataFrameGroupBy, SeriesGroupBy } from "./groupby/index.ts"; export type { AggFn, AggName, AggSpec } from "./groupby/index.ts"; +export { NamedAgg, namedAgg, isNamedAggSpec } from "./groupby/index.ts"; +export type { NamedAggSpec } from "./groupby/index.ts"; export { describe, quantile } from "./stats/index.ts"; export type { DescribeOptions } from "./stats/index.ts"; export { readCsv, toCsv } from "./io/index.ts"; export type { ReadCsvOptions, ToCsvOptions } from "./io/index.ts"; export { readJson, toJson } from "./io/index.ts"; export type { ReadJsonOptions, ToJsonOptions, JsonOrient } from "./io/index.ts"; +export { jsonNormalize } from "./io/index.ts"; +export type { JsonNormalizeOptions, JsonPath } from "./io/index.ts"; export { pearsonCorr, dataFrameCorr, dataFrameCov } from "./stats/index.ts"; export type { CorrMethod, CorrOptions, CovOptions } from "./stats/index.ts"; export { Rolling } from "./window/index.ts"; @@ -61,6 +66,13 @@ export type { ExpandingOptions, ExpandingSeriesLike } from "./window/index.ts"; export { DataFrameExpanding } from "./core/index.ts"; export { EWM } from "./window/index.ts"; export type { EwmOptions, EwmSeriesLike } from "./window/index.ts"; +export { + rollingApply, + rollingAgg, + dataFrameRollingApply, + dataFrameRollingAgg, +} from "./window/index.ts"; +export type { RollingApplyOptions, RollingAggOptions, AggFunctions } from "./window/index.ts"; export { DataFrameEwm } from "./core/index.ts"; export { CategoricalAccessor } from "./core/index.ts"; export type { CatSeriesLike } from "./core/index.ts"; @@ -74,6 +86,10 @@ export type { } from "./reshape/index.ts"; export { stack, unstack, STACK_DEFAULT_SEP } from "./reshape/index.ts"; export type { StackOptions, UnstackOptions } from "./reshape/index.ts"; +export { wideToLong } from "./reshape/index.ts"; +export type { WideToLongOptions } from "./reshape/index.ts"; +export { pivotTableFull } from "./reshape/index.ts"; +export type { PivotTableFullOptions } from "./reshape/index.ts"; export { MultiIndex } from "./core/index.ts"; export type { MultiIndexOptions } from "./core/index.ts"; export { rankSeries, rankDataFrame } from "./stats/index.ts"; @@ -113,3 +129,345 @@ export type { PctChangeOptions, DataFramePctChangeOptions, } from "./stats/index.ts"; +export { whereSeries, maskSeries, whereDataFrame, maskDataFrame } from "./stats/index.ts"; +export type { + WherePredicate, + SeriesCond, + DataFrameCond, + WhereMaskOptions, +} from "./stats/index.ts"; +export { + seriesEq, + seriesNe, + seriesLt, + seriesGt, + seriesLe, + seriesGe, + dataFrameEq, + dataFrameNe, + dataFrameLt, + dataFrameGt, + dataFrameLe, + dataFrameGe, +} from "./stats/index.ts"; +export type { CompareOp, SeriesOther, DataFrameOther } from "./stats/index.ts"; +export { shiftSeries, diffSeries, dataFrameShift, dataFrameDiff } from "./stats/index.ts"; +export type { ShiftDiffDataFrameOptions } from "./stats/index.ts"; +export { interpolateSeries, dataFrameInterpolate } from "./stats/index.ts"; +export type { + InterpolateMethod, + LimitDirection, + InterpolateOptions, + DataFrameInterpolateOptions, +} from "./stats/index.ts"; +export { fillnaSeries, fillnaDataFrame } from "./stats/index.ts"; +export type { + FillnaMethod, + FillnaSeriesOptions, + ColumnFillMap, + FillnaDataFrameOptions, +} from "./stats/index.ts"; +export { Interval, IntervalIndex } from "./core/index.ts"; +export type { IntervalClosed, IntervalIndexOptions } from "./core/index.ts"; +export { cut, qcut, cutIntervalIndex, qcutIntervalIndex } from "./stats/index.ts"; +export type { CutOptions, QCutOptions } from "./stats/index.ts"; +export { sampleSeries, sampleDataFrame } from "./stats/index.ts"; +export type { SampleSeriesOptions, SampleDataFrameOptions } from "./stats/index.ts"; +export { applySeries, applymap, dataFrameApply } from "./stats/index.ts"; +export type { DataFrameApplyOptions } from "./stats/index.ts"; +export { CategoricalIndex } from "./core/index.ts"; +export type { CategoricalIndexOptions } from "./core/index.ts"; +export { + pipeSeries, + dataFramePipe, + pipeTo, + dataFramePipeTo, + pipeChain, + dataFramePipeChain, +} from "./stats/index.ts"; + +export { Period, PeriodIndex } from "./core/index.ts"; +export type { PeriodFreq, PeriodIndexOptions } from "./core/index.ts"; +export { Timedelta, TimedeltaIndex } from "./core/index.ts"; +export type { TimedeltaComponents, TimedeltaIndexOptions } from "./core/index.ts"; +export { + Day, + Hour, + Minute, + Second, + Milli, + Week, + MonthEnd, + MonthBegin, + YearEnd, + YearBegin, + BusinessDay, +} from "./core/index.ts"; +export type { DateOffset, WeekOptions } from "./core/index.ts"; +export { DatetimeIndex, date_range, bdate_range, resolveFreq } from "./core/index.ts"; +export type { DateRangeFreq, DateRangeOptions, DatetimeIndexOptions } from "./core/index.ts"; +export { TZDatetimeIndex, tz_localize, tz_convert } from "./core/index.ts"; +export { + seriesFloor, + dataFrameFloor, + seriesCeil, + dataFrameCeil, + seriesTrunc, + dataFrameTrunc, + seriesSqrt, + dataFrameSqrt, + seriesExp, + dataFrameExp, + seriesLog, + dataFrameLog, + seriesLog2, + dataFrameLog2, + seriesLog10, + dataFrameLog10, + seriesSign, + dataFrameSign, +} from "./stats/index.ts"; +export { + seriesPow, + dataFramePow, + seriesMod, + dataFrameMod, + seriesFloorDiv, + dataFrameFloorDiv, +} from "./stats/index.ts"; +export { + seriesAdd, + seriesRadd, + seriesSub, + seriesRsub, + seriesMul, + seriesRmul, + seriesDiv, + seriesRdiv, + dataFrameAdd, + dataFrameRadd, + dataFrameSub, + dataFrameRsub, + dataFrameMul, + dataFrameRmul, + dataFrameDiv, + dataFrameRdiv, +} from "./stats/index.ts"; +export { getDummies, dataFrameGetDummies } from "./stats/index.ts"; +export type { GetDummiesOptions, DataFrameGetDummiesOptions } from "./stats/index.ts"; +export { factorize, seriesFactorize } from "./stats/index.ts"; +export type { FactorizeOptions, FactorizeResult } from "./stats/index.ts"; +export { crosstab, seriesCrosstab } from "./stats/index.ts"; +export type { AggFunc, Normalize, CrosstabOptions } from "./stats/index.ts"; +export { toNumeric, toNumericArray, toNumericScalar, toNumericSeries } from "./stats/index.ts"; +export type { ToNumericDowncast, ToNumericErrors, ToNumericOptions } from "./stats/index.ts"; +export { seriesMemoryUsage, dataFrameMemoryUsage } from "./stats/index.ts"; +export type { MemoryUsageOptions } from "./stats/index.ts"; +export { selectDtypes } from "./stats/index.ts"; +export type { DtypeSelector, SelectDtypesOptions } from "./stats/index.ts"; +export { clipSeriesWithBounds, clipDataFrameWithBounds } from "./stats/index.ts"; +export type { + BoundArg, + SeriesClipBoundsOptions, + DataFrameClipBoundsOptions, +} from "./stats/index.ts"; +export { Timestamp } from "./core/index.ts"; +export type { TimestampOptions, TimestampComponents, TimestampUnit } from "./core/index.ts"; +export { dataFrameAssign } from "./core/index.ts"; +export type { AssignColSpec, AssignSpec } from "./core/index.ts"; +export { inferDtype } from "./stats/index.ts"; +export type { InferredDtype, InferDtypeOptions } from "./stats/index.ts"; +export { isna, notna, isnull, notnull } from "./stats/index.ts"; +export { dropna, dropnaSeries, dropnaDataFrame } from "./stats/index.ts"; +export type { DropnaHow, DropnaDataFrameOptions } from "./stats/index.ts"; +export { combineFirstSeries, combineFirstDataFrame } from "./stats/index.ts"; +export { natCompare, natSorted, natSortKey, natArgSort } from "./core/index.ts"; +export type { NatSortOptions, NatSortedOptions } from "./core/index.ts"; +export { searchsorted, searchsortedMany, argsortScalars } from "./core/index.ts"; +export type { SearchSortedSide, SearchSortedOptions } from "./core/index.ts"; +export { valueCountsBinned } from "./stats/index.ts"; +export type { ValueCountsBinnedOptions } from "./stats/index.ts"; + +export { + duplicatedSeries, + duplicatedDataFrame, + dropDuplicatesSeries, + dropDuplicatesDataFrame, +} from "./stats/index.ts"; +export type { + KeepPolicy, + DuplicatedDataFrameOptions, + DuplicatedSeriesOptions, +} from "./stats/index.ts"; +export { reindexSeries, reindexDataFrame } from "./core/index.ts"; +export type { ReindexMethod, ReindexSeriesOptions, ReindexDataFrameOptions } from "./core/index.ts"; + +export { alignSeries, alignDataFrame } from "./core/index.ts"; +export type { AlignSeriesOptions, AlignDataFrameOptions } from "./core/index.ts"; + +export { explodeSeries, explodeDataFrame } from "./stats/index.ts"; +export type { ExplodeOptions, ExplodeDataFrameOptions } from "./stats/index.ts"; + +export { isin, dataFrameIsin } from "./stats/index.ts"; +export type { IsinValues, IsinDict, DataFrameIsinValues } from "./stats/index.ts"; + +export { + insertColumn, + popColumn, + reorderColumns, + moveColumn, + dataFrameFromPairs, +} from "./core/index.ts"; +export type { PopResult } from "./core/index.ts"; +export { toDictOriented, fromDictOriented } from "./core/index.ts"; +export type { + ToDictOrient, + FromDictOrient, + DictSplit, + DictTight, + SplitInput, +} from "./core/index.ts"; +export { rollingSem, rollingSkew, rollingKurt, rollingQuantile } from "./stats/index.ts"; +export type { WindowExtOptions, RollingQuantileOptions } from "./stats/index.ts"; +export { fillna, countna, countValid } from "./stats/index.ts"; +export type { IsnaInput, FillnaOptions, DropnaOptions } from "./stats/index.ts"; +export { + getAttrs, + setAttrs, + updateAttrs, + copyAttrs, + withAttrs, + clearAttrs, + hasAttrs, + getAttr, + setAttr, + deleteAttr, + attrsCount, + attrsKeys, + mergeAttrs, +} from "./core/index.ts"; +export type { Attrs } from "./core/index.ts"; +export { + pipe, + seriesApply, + seriesTransform, + dataFrameApplyMap, + dataFrameTransform, + dataFrameTransformRows, +} from "./core/index.ts"; +export { + isScalar, + isListLike, + isArrayLike, + isDictLike, + isIterator, + isNumber, + isBool, + isStringValue, + isFloat, + isInteger, + isBigInt, + isRegExp, + isReCompilable, + isMissing, + isHashable, + isDate, + isNumericDtype, + isIntegerDtype, + isSignedIntegerDtype, + isUnsignedIntegerDtype, + isFloatDtype, + isBoolDtype, + isStringDtype, + isDatetimeDtype, + isTimedeltaDtype, + isCategoricalDtype, + isObjectDtype, + isComplexDtype, + isExtensionArrayDtype, + isPeriodDtype, + isIntervalDtype, +} from "./core/index.ts"; +export { + strNormalize, + strGetDummies, + strExtractAll, + strRemovePrefix, + strRemoveSuffix, + strTranslate, + strCharWidth, + strByteLength, + strSplitExpand, + strExtractGroups, + strPartition, + strRPartition, + strMultiReplace, + strIndent, + strDedent, +} from "./stats/index.ts"; +export type { + NormalizeForm, + StrInput, + ExtractAllOptions, + SplitExpandOptions, + ExtractGroupsOptions, + PartitionResult, + ReplacePair, + IndentOptions, +} from "./stats/index.ts"; +export { + digitize, + histogram, + linspace, + arange, + percentileOfScore, + zscore, + minMaxNormalize, + coefficientOfVariation, + seriesDigitize, +} from "./stats/index.ts"; +export type { + HistogramOptions, + HistogramResult, + ZscoreOptions, + MinMaxOptions, + CvOptions, +} from "./stats/index.ts"; +export { + catFromCodes, + catUnionCategories, + catIntersectCategories, + catDiffCategories, + catEqualCategories, + catSortByFreq, + catToOrdinal, + catFreqTable, + catCrossTab, + catRecode, +} from "./stats/index.ts"; +export type { + CatFromCodesOptions, + CatSortByFreqOptions, + CatCrossTabOptions, +} from "./stats/index.ts"; +export { + formatFloat, + formatPercent, + formatScientific, + formatEngineering, + formatThousands, + formatCurrency, + formatCompact, + makeFloatFormatter, + makePercentFormatter, + makeCurrencyFormatter, + applySeriesFormatter, + applyDataFrameFormatter, + seriesToString, + dataFrameToString, +} from "./stats/index.ts"; +export type { + Formatter, + SeriesToStringOptions, + DataFrameToStringOptions, +} from "./stats/index.ts"; diff --git a/src/stats/index.ts b/src/stats/index.ts index 401233bc..6863a8c7 100644 --- a/src/stats/index.ts +++ b/src/stats/index.ts @@ -45,3 +45,248 @@ export type { PctChangeOptions, DataFramePctChangeOptions, } from "./pct_change.ts"; +export { whereSeries, maskSeries, whereDataFrame, maskDataFrame } from "./where_mask.ts"; +export type { + WherePredicate, + SeriesCond, + DataFrameCond, + WhereMaskOptions, +} from "./where_mask.ts"; +export { + seriesEq, + seriesNe, + seriesLt, + seriesGt, + seriesLe, + seriesGe, + dataFrameEq, + dataFrameNe, + dataFrameLt, + dataFrameGt, + dataFrameLe, + dataFrameGe, +} from "./compare.ts"; +export type { CompareOp, SeriesOther, DataFrameOther } from "./compare.ts"; +export { shiftSeries, diffSeries, dataFrameShift, dataFrameDiff } from "./shift_diff.ts"; +export type { ShiftDiffDataFrameOptions } from "./shift_diff.ts"; +export { interpolateSeries, dataFrameInterpolate } from "./interpolate.ts"; +export type { + InterpolateMethod, + LimitDirection, + InterpolateOptions, + DataFrameInterpolateOptions, +} from "./interpolate.ts"; +export { fillnaSeries, fillnaDataFrame } from "./fillna.ts"; +export type { + FillnaMethod, + FillnaSeriesOptions, + ColumnFillMap, + FillnaDataFrameOptions, +} from "./fillna.ts"; +export { cut, qcut, cutIntervalIndex, qcutIntervalIndex } from "./cut.ts"; +export type { CutOptions, QCutOptions } from "./cut.ts"; +export { sampleSeries, sampleDataFrame } from "./sample.ts"; +export type { SampleSeriesOptions, SampleDataFrameOptions } from "./sample.ts"; +export { applySeries, applymap, dataFrameApply } from "./apply.ts"; +export type { DataFrameApplyOptions } from "./apply.ts"; + +export { + pipeSeries, + dataFramePipe, + pipeTo, + dataFramePipeTo, + pipeChain, + dataFramePipeChain, +} from "./pipe.ts"; +export { + seriesFloor, + dataFrameFloor, + seriesCeil, + dataFrameCeil, + seriesTrunc, + dataFrameTrunc, + seriesSqrt, + dataFrameSqrt, + seriesExp, + dataFrameExp, + seriesLog, + dataFrameLog, + seriesLog2, + dataFrameLog2, + seriesLog10, + dataFrameLog10, + seriesSign, + dataFrameSign, +} from "./numeric_ops.ts"; + +export { + seriesPow, + dataFramePow, + seriesMod, + dataFrameMod, + seriesFloorDiv, + dataFrameFloorDiv, +} from "./pow_mod.ts"; + +export { + seriesAdd, + seriesRadd, + seriesSub, + seriesRsub, + seriesMul, + seriesRmul, + seriesDiv, + seriesRdiv, + dataFrameAdd, + dataFrameRadd, + dataFrameSub, + dataFrameRsub, + dataFrameMul, + dataFrameRmul, + dataFrameDiv, + dataFrameRdiv, +} from "./add_sub_mul_div.ts"; + +export { getDummies, dataFrameGetDummies } from "./get_dummies.ts"; +export type { GetDummiesOptions, DataFrameGetDummiesOptions } from "./get_dummies.ts"; + +export { factorize, seriesFactorize } from "./factorize.ts"; +export type { FactorizeOptions, FactorizeResult } from "./factorize.ts"; + +export { crosstab, seriesCrosstab } from "./crosstab.ts"; +export type { AggFunc, Normalize, CrosstabOptions } from "./crosstab.ts"; + +export { toNumeric, toNumericArray, toNumericScalar, toNumericSeries } from "./to_numeric.ts"; +export type { ToNumericDowncast, ToNumericErrors, ToNumericOptions } from "./to_numeric.ts"; + +export { seriesMemoryUsage, dataFrameMemoryUsage } from "./memory_usage.ts"; +export type { MemoryUsageOptions } from "./memory_usage.ts"; + +export { selectDtypes } from "./select_dtypes.ts"; +export type { DtypeSelector, SelectDtypesOptions } from "./select_dtypes.ts"; + +export { clipSeriesWithBounds, clipDataFrameWithBounds } from "./clip_with_bounds.ts"; +export type { + BoundArg, + SeriesClipBoundsOptions, + DataFrameClipBoundsOptions, +} from "./clip_with_bounds.ts"; + +export { inferDtype } from "./infer_dtype.ts"; +export type { InferredDtype, InferDtypeOptions } from "./infer_dtype.ts"; + +export { isna, notna, isnull, notnull } from "./notna.ts"; + +export { dropna, dropnaSeries, dropnaDataFrame } from "./dropna.ts"; +export type { DropnaHow, DropnaDataFrameOptions } from "./dropna.ts"; + +export { combineFirstSeries, combineFirstDataFrame } from "./combine_first.ts"; + +export { valueCountsBinned } from "./value_counts_full.ts"; +export type { ValueCountsBinnedOptions } from "./value_counts_full.ts"; + +export { + duplicatedSeries, + duplicatedDataFrame, + dropDuplicatesSeries, + dropDuplicatesDataFrame, +} from "./duplicated.ts"; +export type { + KeepPolicy, + DuplicatedDataFrameOptions, + DuplicatedSeriesOptions, +} from "./duplicated.ts"; + +export { explodeSeries, explodeDataFrame } from "./explode.ts"; +export type { ExplodeOptions, ExplodeDataFrameOptions } from "./explode.ts"; + +export { isin, dataFrameIsin } from "./isin.ts"; +export type { IsinValues, IsinDict, DataFrameIsinValues } from "./isin.ts"; + +export { rollingSem, rollingSkew, rollingKurt, rollingQuantile } from "./window_extended.ts"; +export type { WindowExtOptions, RollingQuantileOptions } from "./window_extended.ts"; +export { fillna, countna, countValid } from "./notna_isna.ts"; +export type { IsnaInput, FillnaOptions, DropnaOptions } from "./notna_isna.ts"; +export { + strNormalize, + strGetDummies, + strExtractAll, + strRemovePrefix, + strRemoveSuffix, + strTranslate, + strCharWidth, + strByteLength, +} from "./string_ops.ts"; +export type { NormalizeForm, StrInput, ExtractAllOptions } from "./string_ops.ts"; +export { + strSplitExpand, + strExtractGroups, + strPartition, + strRPartition, + strMultiReplace, + strIndent, + strDedent, +} from "./string_ops_extended.ts"; +export type { + SplitExpandOptions, + ExtractGroupsOptions, + PartitionResult, + ReplacePair, + IndentOptions, +} from "./string_ops_extended.ts"; +export { + digitize, + histogram, + linspace, + arange, + percentileOfScore, + zscore, + minMaxNormalize, + coefficientOfVariation, + seriesDigitize, +} from "./numeric_extended.ts"; +export type { + HistogramOptions, + HistogramResult, + ZscoreOptions, + MinMaxOptions, + CvOptions, +} from "./numeric_extended.ts"; +export { + catFromCodes, + catUnionCategories, + catIntersectCategories, + catDiffCategories, + catEqualCategories, + catSortByFreq, + catToOrdinal, + catFreqTable, + catCrossTab, + catRecode, +} from "./categorical_ops.ts"; +export type { + CatFromCodesOptions, + CatSortByFreqOptions, + CatCrossTabOptions, +} from "./categorical_ops.ts"; +export { + formatFloat, + formatPercent, + formatScientific, + formatEngineering, + formatThousands, + formatCurrency, + formatCompact, + makeFloatFormatter, + makePercentFormatter, + makeCurrencyFormatter, + applySeriesFormatter, + applyDataFrameFormatter, + seriesToString, + dataFrameToString, +} from "./format_ops.ts"; +export type { + Formatter, + SeriesToStringOptions, + DataFrameToStringOptions, +} from "./format_ops.ts"; From 67ab20228969ce3b2af4e33133553062ea7c7c55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:09:46 +0000 Subject: [PATCH 3/7] Move pct_change exports to end of barrel files to avoid merge conflicts with main The pct_change exports were inserted between sections that main also modified, causing merge conflicts. Moving them to the end of each barrel file ensures they appear as pure additions (appended after all of main's content), which git can merge automatically without conflicts. --- playground/index.html | 10 +++++----- src/index.ts | 12 ++++++------ src/stats/index.ts | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/playground/index.html b/playground/index.html index dba1dfdb..d00f3554 100644 --- a/playground/index.html +++ b/playground/index.html @@ -264,11 +264,6 @@

βœ… Complete -
-

πŸ“Š pct_change

-

Fractional change between elements. pctChangeSeries() and pctChangeDataFrame() with periods, fillMethod (pad/bfill), limit, and axis options.

-
βœ… Complete
-

πŸ“₯ insertColumn / popColumn

Insert and remove DataFrame columns at precise positions. insertColumn(df, loc, col, values) inserts at integer position, popColumn(df, col) returns { series, df }. Also includes reorderColumns and moveColumn. Mirrors pandas.DataFrame.insert() and .pop().

@@ -337,6 +332,11 @@

πŸ”’ format_ops β€” Number Formatting

Number-formatting helpers for Series and DataFrame. Scalar formatters: formatFloat, formatPercent, formatScientific, formatEngineering, formatThousands, formatCurrency, formatCompact. Formatter factories: makeFloatFormatter, makePercentFormatter, makeCurrencyFormatter. Apply to collections: applySeriesFormatter, applyDataFrameFormatter. Render to string: seriesToString, dataFrameToString.

βœ… Complete
+
+
+

πŸ“Š pct_change

+

Fractional change between elements. pctChangeSeries() and pctChangeDataFrame() with periods, fillMethod (pad/bfill), limit, and axis options.

+
βœ… Complete
diff --git a/src/index.ts b/src/index.ts index 3f970c69..684cbbf1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -123,12 +123,6 @@ export { export type { ClipOptions, RoundOptions, DataFrameElemOptions } from "./stats/index.ts"; export { valueCounts, dataFrameValueCounts } from "./stats/index.ts"; export type { ValueCountsOptions, DataFrameValueCountsOptions } from "./stats/index.ts"; -export { pctChangeSeries, pctChangeDataFrame } from "./stats/index.ts"; -export type { - PctChangeFillMethod, - PctChangeOptions, - DataFramePctChangeOptions, -} from "./stats/index.ts"; export { whereSeries, maskSeries, whereDataFrame, maskDataFrame } from "./stats/index.ts"; export type { WherePredicate, @@ -471,3 +465,9 @@ export type { SeriesToStringOptions, DataFrameToStringOptions, } from "./stats/index.ts"; +export { pctChangeSeries, pctChangeDataFrame } from "./stats/index.ts"; +export type { + PctChangeFillMethod, + PctChangeOptions, + DataFramePctChangeOptions, +} from "./stats/index.ts"; diff --git a/src/stats/index.ts b/src/stats/index.ts index 6863a8c7..95527e38 100644 --- a/src/stats/index.ts +++ b/src/stats/index.ts @@ -39,12 +39,6 @@ export { nsmallestDataFrame, } from "./nlargest.ts"; export type { NKeep, NTopOptions, NTopDataFrameOptions } from "./nlargest.ts"; -export { pctChangeSeries, pctChangeDataFrame } from "./pct_change.ts"; -export type { - PctChangeFillMethod, - PctChangeOptions, - DataFramePctChangeOptions, -} from "./pct_change.ts"; export { whereSeries, maskSeries, whereDataFrame, maskDataFrame } from "./where_mask.ts"; export type { WherePredicate, @@ -290,3 +284,9 @@ export type { SeriesToStringOptions, DataFrameToStringOptions, } from "./format_ops.ts"; +export { pctChangeSeries, pctChangeDataFrame } from "./pct_change.ts"; +export type { + PctChangeFillMethod, + PctChangeOptions, + DataFramePctChangeOptions, +} from "./pct_change.ts"; From 247af4ca6ba8d6180b0442d63ba09d41e76f4e22 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:48:14 +0000 Subject: [PATCH 4/7] Fix merge conflicts with main: resolve pct_change add/add conflicts - src/stats/pct_change.ts: adopt main's style (Number.POSITIVE_INFINITY, index.size) - tests/stats/pct_change.test.ts: adopt main's style (DataFrame.fromColumns, index.size) - playground/pct_change.html: use pandas 3.x Python examples - src/index.ts: add main's new module exports alongside pct_change - src/stats/index.ts: add main's new module exports alongside pct_change Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- playground/pct_change.html | 12 +++-- src/index.ts | 64 ++++++++++++++++++++++++++ src/stats/index.ts | 70 +++++++++++++++++++++++++++++ src/stats/pct_change.ts | 19 +++++--- tests/stats/pct_change.test.ts | 82 +++++++++++++++++----------------- 5 files changed, 196 insertions(+), 51 deletions(-) diff --git a/playground/pct_change.html b/playground/pct_change.html index 3576797a..ec1b4e3b 100644 --- a/playground/pct_change.html +++ b/playground/pct_change.html @@ -288,8 +288,10 @@

3 Β· Handling missing values

prices = pd.Series([100, None, None, 130]) -with_fill = prices.pct_change(fill_method="pad") -no_fill = prices.pct_change(fill_method=None) +# In pandas 3.x, fill_method was removed from pct_change. +# Use fillna + pct_change instead. +with_fill = prices.ffill().pct_change() +no_fill = prices.pct_change() print("pad-fill: ", with_fill.tolist()) print("no fill: ", no_fill.tolist()) @@ -327,7 +329,9 @@

4 Β· Limit consecutive fills

# 1-gap: filled; 2-gap: not filled (exceeds limit=1) data = pd.Series([100, None, 110, None, None, 130]) -limited = data.pct_change(fill_method="pad", limit=1) +# In pandas 3.x, fill_method/limit were removed from pct_change. +# Use fillna with limit before pct_change instead. +limited = data.ffill(limit=1).pct_change() print("input: ", data.tolist()) print("limited: ", limited.tolist()) @@ -408,7 +412,7 @@

6 Β· Negative periods (look-forward change)

# If you buy today, what return do you get in the next 2 days? prices = pd.Series([100, 105, 98, 110, 115]) -fwd2 = prices.pct_change(periods=-2, fill_method=None) +fwd2 = prices.pct_change(periods=-2) print("prices: ", prices.tolist()) print("2-day fwd rtn: ", fwd2.tolist()) diff --git a/src/index.ts b/src/index.ts index 684cbbf1..20cd1938 100644 --- a/src/index.ts +++ b/src/index.ts @@ -465,6 +465,70 @@ export type { SeriesToStringOptions, DataFrameToStringOptions, } from "./stats/index.ts"; +export { quantileSeries, quantileDataFrame } from "./stats/index.ts"; +export type { + QuantileInterpolation, + QuantileSeriesOptions, + QuantileDataFrameOptions, +} from "./stats/index.ts"; +export { replaceSeries, replaceDataFrame } from "./stats/index.ts"; +export type { + ReplaceMapping, + ReplaceSpec, + ReplaceOptions, + DataFrameReplaceOptions, +} from "./stats/index.ts"; +export { varSeries, semSeries, varDataFrame, semDataFrame } from "./stats/index.ts"; +export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./stats/index.ts"; +export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./stats/index.ts"; +export type { + SkewKurtSeriesOptions, + SkewKurtDataFrameOptions, +} from "./stats/index.ts"; +export { toDatetime } from "./stats/index.ts"; +export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./stats/index.ts"; + +// PR #120 unique modules β€” re-exported from sub-barrels +export { astypeSeries, astype, castScalar } from "./core/index.ts"; +export type { AstypeOptions, DataFrameAstypeOptions } from "./core/index.ts"; +// readExcel / xlsxSheetNames use node:zlib β€” import from "tsb/io/read_excel" directly +export { clipAdvancedSeries, clipAdvancedDataFrame } from "./stats/index.ts"; +export type { + SeriesBound, + DataFrameBound, + ClipAdvancedSeriesOptions, + ClipAdvancedDataFrameOptions, +} from "./stats/index.ts"; +export { idxminSeries, idxmaxSeries, idxminDataFrame, idxmaxDataFrame } from "./stats/index.ts"; +export type { IdxOptions, IdxDataFrameOptions } from "./stats/index.ts"; +export { modeSeries, modeDataFrame } from "./stats/index.ts"; +export type { ModeSeriesOptions, ModeDataFrameOptions } from "./stats/index.ts"; +export { + nancount, + nansum, + nanmean, + nanmedian, + nanvar, + nanstd, + nanmin, + nanmax, + nanprod, +} from "./stats/index.ts"; +export type { NanInput, NanAggOptions } from "./stats/index.ts"; +export { + nuniqueSeries, + nuniqueDataFrame, + anySeries, + allSeries, + anyDataFrame, + allDataFrame, +} from "./stats/index.ts"; +export type { + NuniqueSeriesOptions, + NuniqueDataFrameOptions, + AnyAllSeriesOptions, + AnyAllDataFrameOptions, +} from "./stats/index.ts"; export { pctChangeSeries, pctChangeDataFrame } from "./stats/index.ts"; export type { PctChangeFillMethod, diff --git a/src/stats/index.ts b/src/stats/index.ts index 95527e38..4d60904f 100644 --- a/src/stats/index.ts +++ b/src/stats/index.ts @@ -284,6 +284,76 @@ export type { SeriesToStringOptions, DataFrameToStringOptions, } from "./format_ops.ts"; + +export { quantileSeries, quantileDataFrame } from "./quantile.ts"; +export type { + QuantileInterpolation, + QuantileSeriesOptions, + QuantileDataFrameOptions, +} from "./quantile.ts"; + +export { replaceSeries, replaceDataFrame } from "./replace.ts"; +export type { + ReplaceMapping, + ReplaceSpec, + ReplaceOptions, + DataFrameReplaceOptions, +} from "./replace.ts"; + +export { varSeries, semSeries, varDataFrame, semDataFrame } from "./sem_var.ts"; +export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./sem_var.ts"; + +export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./skew_kurt.ts"; +export type { + SkewKurtSeriesOptions, + SkewKurtDataFrameOptions, +} from "./skew_kurt.ts"; + +export { toDatetime } from "./to_datetime.ts"; +export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./to_datetime.ts"; + +export { clipAdvancedSeries, clipAdvancedDataFrame } from "./clip_advanced.ts"; +export type { + SeriesBound, + DataFrameBound, + ClipAdvancedSeriesOptions, + ClipAdvancedDataFrameOptions, +} from "./clip_advanced.ts"; + +export { idxminSeries, idxmaxSeries, idxminDataFrame, idxmaxDataFrame } from "./idxmin_idxmax.ts"; +export type { IdxOptions, IdxDataFrameOptions } from "./idxmin_idxmax.ts"; + +export { modeSeries, modeDataFrame } from "./mode.ts"; +export type { ModeSeriesOptions, ModeDataFrameOptions } from "./mode.ts"; + +export { + nancount, + nansum, + nanmean, + nanmedian, + nanvar, + nanstd, + nanmin, + nanmax, + nanprod, +} from "./nancumops.ts"; +export type { NanInput, NanAggOptions } from "./nancumops.ts"; + +export { + nuniqueSeries, + nuniqueDataFrame, + anySeries, + allSeries, + anyDataFrame, + allDataFrame, +} from "./nunique.ts"; +export type { + NuniqueSeriesOptions, + NuniqueDataFrameOptions, + AnyAllSeriesOptions, + AnyAllDataFrameOptions, +} from "./nunique.ts"; + export { pctChangeSeries, pctChangeDataFrame } from "./pct_change.ts"; export type { PctChangeFillMethod, diff --git a/src/stats/pct_change.ts b/src/stats/pct_change.ts index c46c9e84..10e527ec 100644 --- a/src/stats/pct_change.ts +++ b/src/stats/pct_change.ts @@ -97,7 +97,9 @@ function applyFill( method: PctChangeFillMethod | null | undefined, limit: number | null | undefined, ): Scalar[] { - if (!method) return [...vals]; + if (!method) { + return [...vals]; + } return method === "pad" ? padFill(vals, limit) : bfillFill(vals, limit); } @@ -114,7 +116,8 @@ function computePct(vals: readonly Scalar[], periods: number): Scalar[] { out[i] = curr / prev - 1; } else if (isNum(curr) && isNum(prev) && prev === 0) { // 0 denominator β†’ Infinity (same as pandas) - out[i] = curr === 0 ? Number.NaN : curr > 0 ? Infinity : -Infinity; + out[i] = + curr === 0 ? Number.NaN : curr > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } else { out[i] = null; } @@ -128,7 +131,8 @@ function computePct(vals: readonly Scalar[], periods: number): Scalar[] { if (isNum(curr) && isNum(fwd) && curr !== 0) { out[i] = fwd / curr - 1; } else if (isNum(curr) && isNum(fwd) && curr === 0) { - out[i] = fwd === 0 ? Number.NaN : fwd > 0 ? Infinity : -Infinity; + out[i] = + fwd === 0 ? Number.NaN : fwd > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; } else { out[i] = null; } @@ -151,7 +155,10 @@ function computePct(vals: readonly Scalar[], periods: number): Scalar[] { * pctChangeSeries(s); // [null, 0.1, -0.1, 0.2222…] * ``` */ -export function pctChangeSeries(series: Series, options: PctChangeOptions = {}): Series { +export function pctChangeSeries( + series: Series, + options: PctChangeOptions = {}, +): Series { const periods = options.periods ?? 1; const fillMethod = options.fillMethod !== undefined ? options.fillMethod : "pad"; const limit = options.limit ?? null; @@ -162,7 +169,7 @@ export function pctChangeSeries(series: Series, options: PctChangeOption return new Series({ data: result, index: series.index, - name: series.name ?? undefined, + name: series.name, }); } @@ -199,7 +206,7 @@ export function pctChangeDataFrame( const periods = options.periods ?? 1; const fillMethod = options.fillMethod !== undefined ? options.fillMethod : "pad"; const limit = options.limit ?? null; - const nRows = df.index.length; + const nRows = df.index.size; const cols = df.columns.values; const nCols = cols.length; diff --git a/tests/stats/pct_change.test.ts b/tests/stats/pct_change.test.ts index 98966e8c..f7015e4a 100644 --- a/tests/stats/pct_change.test.ts +++ b/tests/stats/pct_change.test.ts @@ -3,12 +3,7 @@ */ import { describe, expect, it } from "bun:test"; import fc from "fast-check"; -import { - DataFrame, - Series, - pctChangeDataFrame, - pctChangeSeries, -} from "../../src/index.ts"; +import { DataFrame, Series, pctChangeDataFrame, pctChangeSeries } from "../../src/index.ts"; import type { Scalar } from "../../src/index.ts"; // ─── helpers ───────────────────────────────────────────────────────────────── @@ -25,24 +20,38 @@ function nanEq(a: Scalar, b: Scalar): boolean { } function arrEq(a: readonly Scalar[], b: readonly Scalar[]): boolean { - if (a.length !== b.length) return false; + if (a.length !== b.length) { + return false; + } for (let i = 0; i < a.length; i++) { - if (!nanEq(a[i] as Scalar, b[i] as Scalar)) return false; + if (!nanEq(a[i] as Scalar, b[i] as Scalar)) { + return false; + } } return true; } function close(a: Scalar, b: Scalar, eps = 1e-9): boolean { - if (a === null && b === null) return true; - if (typeof a !== "number" || typeof b !== "number") return false; - if (Number.isNaN(a) && Number.isNaN(b)) return true; + if (a === null && b === null) { + return true; + } + if (typeof a !== "number" || typeof b !== "number") { + return false; + } + if (Number.isNaN(a) && Number.isNaN(b)) { + return true; + } return Math.abs(a - b) < eps; } function arrClose(a: readonly Scalar[], b: readonly Scalar[], eps = 1e-9): boolean { - if (a.length !== b.length) return false; + if (a.length !== b.length) { + return false; + } for (let i = 0; i < a.length; i++) { - if (!close(a[i] as Scalar, b[i] as Scalar, eps)) return false; + if (!close(a[i] as Scalar, b[i] as Scalar, eps)) { + return false; + } } return true; } @@ -121,7 +130,7 @@ describe("pctChangeSeries", () => { it("zero denominator returns Infinity", () => { const result = pctChangeSeries(s([0, 10]), { fillMethod: null }); - expect(result.values[1]).toBe(Infinity); + expect(result.values[1]).toBe(Number.POSITIVE_INFINITY); }); it("zero/zero denominator returns NaN", () => { @@ -133,7 +142,7 @@ describe("pctChangeSeries", () => { const src = new Series({ data: [10, 20, 30], name: "price" }); const result = pctChangeSeries(src); expect(result.name).toBe("price"); - expect(result.index.length).toBe(3); + expect(result.index.size).toBe(3); }); it("empty series returns empty", () => { @@ -151,12 +160,10 @@ describe("pctChangeSeries", () => { describe("pctChangeDataFrame", () => { it("column-wise (default)", () => { - const df = new DataFrame( - new Map([ - ["a", new Series({ data: [100, 110, 121] })], - ["b", new Series({ data: [200, 180, 198] })], - ]), - ); + const df = DataFrame.fromColumns({ + a: [100, 110, 121], + b: [200, 180, 198], + }); const result = pctChangeDataFrame(df); const colA = result.col("a").values; const colB = result.col("b").values; @@ -169,13 +176,11 @@ describe("pctChangeDataFrame", () => { }); it("row-wise (axis=1)", () => { - const df = new DataFrame( - new Map([ - ["a", new Series({ data: [100, 200] })], - ["b", new Series({ data: [110, 220] })], - ["c", new Series({ data: [121, 242] })], - ]), - ); + const df = DataFrame.fromColumns({ + a: [100, 200], + b: [110, 220], + c: [121, 242], + }); const result = pctChangeDataFrame(df, { axis: 1 }); // row 0: [100, 110, 121] β†’ [null, 0.1, 0.1] // row 1: [200, 220, 242] β†’ [null, 0.1, 0.1] @@ -192,12 +197,10 @@ describe("pctChangeDataFrame", () => { }); it("preserves column order", () => { - const df = new DataFrame( - new Map([ - ["x", new Series({ data: [1, 2] })], - ["y", new Series({ data: [3, 6] })], - ]), - ); + const df = DataFrame.fromColumns({ + x: [1, 2], + y: [3, 6], + }); const result = pctChangeDataFrame(df); expect(result.columns.values).toEqual(["x", "y"]); }); @@ -217,13 +220,10 @@ describe("pctChangeSeries β€” property tests", () => { it("first element is always null for periods=1", () => { fc.assert( - fc.property( - fc.array(fc.float({ noNaN: true }), { minLength: 1, maxLength: 50 }), - (arr) => { - const result = pctChangeSeries(s(arr)); - return result.values[0] === null; - }, - ), + fc.property(fc.array(fc.float({ noNaN: true }), { minLength: 1, maxLength: 50 }), (arr) => { + const result = pctChangeSeries(s(arr)); + return result.values[0] === null; + }), ); }); From 827725b78d0622f6b6abcb4bde15c054309f03e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:28:13 +0000 Subject: [PATCH 5/7] Reorder barrel exports to match main's structure, avoiding merge conflicts Move quantile/replace/sem_var/skew_kurt/toDatetime exports to after pctChange in src/stats/index.ts and src/index.ts, matching main's export ordering so that merging the PR into main is conflict-free. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/index.ts | 44 ++++++++++++++++++------------------- src/stats/index.ts | 54 +++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/index.ts b/src/index.ts index 20cd1938..37c6e62e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -465,28 +465,6 @@ export type { SeriesToStringOptions, DataFrameToStringOptions, } from "./stats/index.ts"; -export { quantileSeries, quantileDataFrame } from "./stats/index.ts"; -export type { - QuantileInterpolation, - QuantileSeriesOptions, - QuantileDataFrameOptions, -} from "./stats/index.ts"; -export { replaceSeries, replaceDataFrame } from "./stats/index.ts"; -export type { - ReplaceMapping, - ReplaceSpec, - ReplaceOptions, - DataFrameReplaceOptions, -} from "./stats/index.ts"; -export { varSeries, semSeries, varDataFrame, semDataFrame } from "./stats/index.ts"; -export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./stats/index.ts"; -export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./stats/index.ts"; -export type { - SkewKurtSeriesOptions, - SkewKurtDataFrameOptions, -} from "./stats/index.ts"; -export { toDatetime } from "./stats/index.ts"; -export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./stats/index.ts"; // PR #120 unique modules β€” re-exported from sub-barrels export { astypeSeries, astype, castScalar } from "./core/index.ts"; @@ -535,3 +513,25 @@ export type { PctChangeOptions, DataFramePctChangeOptions, } from "./stats/index.ts"; +export { quantileSeries, quantileDataFrame } from "./stats/index.ts"; +export type { + QuantileInterpolation, + QuantileSeriesOptions, + QuantileDataFrameOptions, +} from "./stats/index.ts"; +export { replaceSeries, replaceDataFrame } from "./stats/index.ts"; +export type { + ReplaceMapping, + ReplaceSpec, + ReplaceOptions, + DataFrameReplaceOptions, +} from "./stats/index.ts"; +export { varSeries, semSeries, varDataFrame, semDataFrame } from "./stats/index.ts"; +export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./stats/index.ts"; +export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./stats/index.ts"; +export type { + SkewKurtSeriesOptions, + SkewKurtDataFrameOptions, +} from "./stats/index.ts"; +export { toDatetime } from "./stats/index.ts"; +export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./stats/index.ts"; diff --git a/src/stats/index.ts b/src/stats/index.ts index 4d60904f..63864005 100644 --- a/src/stats/index.ts +++ b/src/stats/index.ts @@ -285,33 +285,6 @@ export type { DataFrameToStringOptions, } from "./format_ops.ts"; -export { quantileSeries, quantileDataFrame } from "./quantile.ts"; -export type { - QuantileInterpolation, - QuantileSeriesOptions, - QuantileDataFrameOptions, -} from "./quantile.ts"; - -export { replaceSeries, replaceDataFrame } from "./replace.ts"; -export type { - ReplaceMapping, - ReplaceSpec, - ReplaceOptions, - DataFrameReplaceOptions, -} from "./replace.ts"; - -export { varSeries, semSeries, varDataFrame, semDataFrame } from "./sem_var.ts"; -export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./sem_var.ts"; - -export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./skew_kurt.ts"; -export type { - SkewKurtSeriesOptions, - SkewKurtDataFrameOptions, -} from "./skew_kurt.ts"; - -export { toDatetime } from "./to_datetime.ts"; -export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./to_datetime.ts"; - export { clipAdvancedSeries, clipAdvancedDataFrame } from "./clip_advanced.ts"; export type { SeriesBound, @@ -360,3 +333,30 @@ export type { PctChangeOptions, DataFramePctChangeOptions, } from "./pct_change.ts"; + +export { quantileSeries, quantileDataFrame } from "./quantile.ts"; +export type { + QuantileInterpolation, + QuantileSeriesOptions, + QuantileDataFrameOptions, +} from "./quantile.ts"; + +export { replaceSeries, replaceDataFrame } from "./replace.ts"; +export type { + ReplaceMapping, + ReplaceSpec, + ReplaceOptions, + DataFrameReplaceOptions, +} from "./replace.ts"; + +export { varSeries, semSeries, varDataFrame, semDataFrame } from "./sem_var.ts"; +export type { VarSemSeriesOptions, VarSemDataFrameOptions } from "./sem_var.ts"; + +export { skewSeries, kurtSeries, skewDataFrame, kurtDataFrame } from "./skew_kurt.ts"; +export type { + SkewKurtSeriesOptions, + SkewKurtDataFrameOptions, +} from "./skew_kurt.ts"; + +export { toDatetime } from "./to_datetime.ts"; +export type { DatetimeUnit, DatetimeErrors, ToDatetimeOptions } from "./to_datetime.ts"; From 19ada577c267ded1912c2e2de3375165d6bffe4a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:28:14 +0000 Subject: [PATCH 6/7] Remove pct_change card from playground/index.html to match main The card conflicted with main's version. Remove it to allow clean merge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- playground/index.html | 5 ----- 1 file changed, 5 deletions(-) diff --git a/playground/index.html b/playground/index.html index d00f3554..bf6823ba 100644 --- a/playground/index.html +++ b/playground/index.html @@ -332,11 +332,6 @@

πŸ”’ format_ops β€” Number Formatting

Number-formatting helpers for Series and DataFrame. Scalar formatters: formatFloat, formatPercent, formatScientific, formatEngineering, formatThousands, formatCurrency, formatCompact. Formatter factories: makeFloatFormatter, makePercentFormatter, makeCurrencyFormatter. Apply to collections: applySeriesFormatter, applyDataFrameFormatter. Render to string: seriesToString, dataFrameToString.

βœ… Complete
- -
-

πŸ“Š pct_change

-

Fractional change between elements. pctChangeSeries() and pctChangeDataFrame() with periods, fillMethod (pad/bfill), limit, and axis options.

-
βœ… Complete
From 6d082c7c061f051d2be72f21e9948029eb82e61c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 19:28:16 +0000 Subject: [PATCH 7/7] Add missing blank line in src/index.ts to match main's formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>