Skip to content

Getting started ​

Installation ​

The library is published on JSR as @paluigi/jsindexnum:

bash
# Deno
deno add jsr:@paluigi/jsindexnum

# Bun (fetches @jsr scopes from JSR automatically)
bunx jsr add @paluigi/jsindexnum

# npm — one-time registry mapping, then install
echo '@jsr:registry=https://npm.jsr.io' >> .npmrc
npm install @jsr/palugi__jsindexnum

npm consumers import from the @jsr scope:

ts
import { fisher } from "@jsr/palugi__jsindexnum";

The pipeline ​

Like PyIndexNum, calculations run on a canonical panel schema produced by aggregateTime: one row per (product, period) with aggregated_price and (optionally) aggregated_quantity.

ts
import {
  standardizeColumns,
  aggregateTime,
  removeUnbalanced,
  geksFisher,
  fisher,
} from "@jsr/palugi__jsindexnum";

// 1. Standardize raw columns (rename, parse dates, drop zero quantities)
const std = standardizeColumns(
  {
    date: ["2023-01-05", "2023-01-20", "2023-02-10"],
    product_id: ["A", "A", "A"],
    price: [9.99, 10.5, 10.2],
    quantity: [100, 80, 120],
  },
  { quantityCol: "quantity" },
);

// 2. Aggregate to monthly periods
const panel = aggregateTime(std, { aggType: "weighted_arithmetic", freq: "1mo" });

// 3. (optionally) balance the panel
const balanced = removeUnbalanced(panel);

// 4. Compute indices
const series = geksFisher(balanced);
// [{ period: "2023-01-01", indexValue: 1 }, { period: "2023-02-01", indexValue: ... }]

const twoPeriods = /* ... a panel with exactly two periods ... */;
const f = fisher(twoPeriods); // a number, 1.02 = +2 %

Data model ​

Panel — the canonical panel — stores data columnar:

columnstoragemeaning
productIdsstring[] + Uint32Array codesdictionary-encoded ids
periodsInt32ArrayUTC epoch days
pricesFloat64Arrayaggregated prices
quantitiesFloat64Array | nullaggregated quantities

Build one directly from columns or rows:

ts
const panel = Panel.fromColumns({
  productId: ["A", "B"],
  period: ["2023-01-01", "2023-01-01"],
  price: [100, 200],
  quantity: [10, 20],
});

Balancing and imputation ​

ts
import {
  removeUnbalanced,
  carryForwardImputation,
  carryBackwardImputation,
} from "jsindexnum";

removeUnbalanced(panel); // keep products present in every period
carryForwardImputation(panel); // full grid, last observation carried forward
carryBackwardImputation(panel); // full grid, next observation carried backward

Multilateral methods accept balanced: false to compute each period comparison on the products matched between the periods involved, and an onWarning callback (the counterpart of Python's UserWarnings):

ts
const { onWarning, messages } = collectWarnings();
const series = geksFisher(panel, { balanced: false, onWarning });
console.log(messages); // e.g. "Unbalanced panel: 3 of 105 period pairs..."

Export-quality numbers ​

ts
import { roundSignificant, formatSignificant } from "jsindexnum";

0.1 + 0.2; // 0.30000000000000004
roundSignificant(0.1 + 0.2); // 0.3
formatSignificant(1.2007597555242124); // "1.2007598"

Every display/export helper in the library and on this site routes numbers through this 8-significant-digit convention — see numeric alignment.