Tidy-TS Logo

Tidy-TS

Data analytics framework

Type-safe data analytics and statistics in TypeScript. Research shows that static typing can prevent 15–38% of production bugs1,2,3. Designed for modern data science workflows.

Type-Safe Data Operations
Perform DataFrame operations with full TypeScript support

Create, transform, filter, select, and sort data with chaining and automatic column typing for compile-time safety.

Learn More
Statistical Analysis
A comprehensive toolkit for statistical analysis

80+ functions across descriptive statistics, hypothesis testing, and probability distributions. All tests are rigorously validated against results from R.

Learn More

See Tidy-TS in Action

A complete data analysis workflow, broken into focused examples

Create DataFrames
Learn More
Create DataFrames from arrays of objects with automatic type inference
import { createDataFrame } from "@tidy-ts/dataframe";

// πŸš€ Load character data from the galaxy
const characters = createDataFrame([
  { name: "Luke", species: "Human", mass_kg: 77, height_cm: 172 },
  { name: "Leia", species: "Human", mass_kg: 49, height_cm: 150 },
  { name: "C-3PO", species: "Droid", mass_kg: 75, height_cm: 167 },
  { name: "R2-D2", species: "Droid", mass_kg: 32, height_cm: 96 },
]);

characters.print();

// Output:
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ name  β”‚ speciesβ”‚ mass_kg β”‚ height_cm β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Luke  β”‚ Human  β”‚ 77      β”‚ 172       β”‚
// β”‚ Leia  β”‚ Human  β”‚ 49      β”‚ 150       β”‚
// β”‚ C-3PO β”‚ Droid  β”‚ 75      β”‚ 167       β”‚
// β”‚ R2-D2 β”‚ Droid  β”‚ 32      β”‚ 96        β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
import { createDataFrame } from "@tidy-ts/dataframe";

// πŸš€ Load character data from the galaxy
const characters = createDataFrame([
  { name: "Luke", species: "Human", mass_kg: 77, height_cm: 172 },
  { name: "Leia", species: "Human", mass_kg: 49, height_cm: 150 },
  { name: "C-3PO", species: "Droid", mass_kg: 75, height_cm: 167 },
  { name: "R2-D2", species: "Droid", mass_kg: 32, height_cm: 96 },
]);

characters.print();

// Output:
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ name  β”‚ speciesβ”‚ mass_kg β”‚ height_cm β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Luke  β”‚ Human  β”‚ 77      β”‚ 172       β”‚
// β”‚ Leia  β”‚ Human  β”‚ 49      β”‚ 150       β”‚
// β”‚ C-3PO β”‚ Droid  β”‚ 75      β”‚ 167       β”‚
// β”‚ R2-D2 β”‚ Droid  β”‚ 32      β”‚ 96        β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Transform Data
Learn More
Add calculated columns using `mutate()` with access to row values, index, and full DataFrame context
// πŸ”§ Transform data with calculated columns
const analysis = characters
  .mutate({
    mass_lbs: (row) => row.mass_kg * 2.20462,  // Convert to pounds
    height_in: (row) => row.height_cm / 2.54,  // Convert to inches
    bmi: (row) => row.mass_kg / ((row.height_cm / 100) ** 2),  // Body Mass Index
  })
  .select("name", "mass_lbs", "height_in", "bmi");

analysis.print("Character Analysis with Calculations");

// Output:
// Character Analysis with Calculations
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ name  β”‚ mass_lbs β”‚ height_in β”‚ bmi     β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Luke  β”‚ 169.76   β”‚ 67.72     β”‚ 26.03   β”‚
// β”‚ Leia  β”‚ 108.03   β”‚ 59.06     β”‚ 21.78   β”‚
// β”‚ C-3PO β”‚ 165.35   β”‚ 65.75     β”‚ 26.89   β”‚
// β”‚ R2-D2 β”‚ 70.55    β”‚ 37.80     β”‚ 34.72   β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
// πŸ”§ Transform data with calculated columns
const analysis = characters
  .mutate({
    mass_lbs: (row) => row.mass_kg * 2.20462,  // Convert to pounds
    height_in: (row) => row.height_cm / 2.54,  // Convert to inches
    bmi: (row) => row.mass_kg / ((row.height_cm / 100) ** 2),  // Body Mass Index
  })
  .select("name", "mass_lbs", "height_in", "bmi");

analysis.print("Character Analysis with Calculations");

// Output:
// Character Analysis with Calculations
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ name  β”‚ mass_lbs β”‚ height_in β”‚ bmi     β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Luke  β”‚ 169.76   β”‚ 67.72     β”‚ 26.03   β”‚
// β”‚ Leia  β”‚ 108.03   β”‚ 59.06     β”‚ 21.78   β”‚
// β”‚ C-3PO β”‚ 165.35   β”‚ 65.75     β”‚ 26.89   β”‚
// β”‚ R2-D2 β”‚ 70.55    β”‚ 37.80     β”‚ 34.72   β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Group and Summarize
Learn More
Group data by categories and calculate summary statistics with groupBy() and summarize()
import { stats as s } from "@tidy-ts/dataframe";

// πŸ“Š Group by species and calculate statistics
const summary = analysis
  .groupBy("species")
  .summarize({
    avg_mass_lbs: (group) => s.mean(group.mass_lbs),
    avg_height_in: (group) => s.mean(group.height_in),
    count: (group) => group.nrows(),
  })
  .arrange("avg_mass_lbs", "desc");

summary.print("Species Comparison Report");

// Output:
// Species Comparison Report
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ speciesβ”‚ avg_mass_lbs  β”‚ avg_height_in β”‚ count β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Human  β”‚ 138.90        β”‚ 63.39         β”‚ 2     β”‚
// β”‚ Droid  β”‚ 117.95        β”‚ 51.78         β”‚ 2     β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜
import { stats as s } from "@tidy-ts/dataframe";

// πŸ“Š Group by species and calculate statistics
const summary = analysis
  .groupBy("species")
  .summarize({
    avg_mass_lbs: (group) => s.mean(group.mass_lbs),
    avg_height_in: (group) => s.mean(group.height_in),
    count: (group) => group.nrows(),
  })
  .arrange("avg_mass_lbs", "desc");

summary.print("Species Comparison Report");

// Output:
// Species Comparison Report
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ speciesβ”‚ avg_mass_lbs  β”‚ avg_height_in β”‚ count β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ Human  β”‚ 138.90        β”‚ 63.39         β”‚ 2     β”‚
// β”‚ Droid  β”‚ 117.95        β”‚ 51.78         β”‚ 2     β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜
Statistical Tests
Learn More
Perform hypothesis testing and correlation analysis using the `stats` module
// Test 1: Are droid proportions (suspiciously?) similar to human proportions?
const humans = analysis.filter((r) => r.species === "Human");
const droids = analysis.filter((r) => r.species === "Droid");

const bmiTest = s.compare.twoGroups.centralTendency.toEachOther({
  x: humans.bmi,
  y: droids.bmi,
  parametric: "auto", // Auto-detects appropriate test
});

console.log(`Droid conspiracy? Test: ${bmiTest.testName}, p-value: ${s.round(bmiTest.pValue, 3)}`);

// Output:
// Droid conspiracy? Test: Independent T-Test, p-value: 0.261

// Test 2: Are height and mass correlated among all characters?
const heightMassTest = s.compare.twoGroups.association.toEachOther({
  x: analysis.height_cm,
  y: analysis.mass_kg,
  method: "auto", // Selects best choice between Pearson, Spearman, or Kendall
});

console.log(`Height and mass correlation?
Test: ${heightMassTest.testName}
${heightMassTest.effectSize.name}: ${s.round(heightMassTest.effectSize.value, 3)}
p-value: ${s.round(heightMassTest.pValue, 3)}`);


// Output:
// Height and mass correlation? 
// Test: Kendall's rank correlation tau
// Kendall's Tau: 1
// p-value: 0.083
// Test 1: Are droid proportions (suspiciously?) similar to human proportions?
const humans = analysis.filter((r) => r.species === "Human");
const droids = analysis.filter((r) => r.species === "Droid");

const bmiTest = s.compare.twoGroups.centralTendency.toEachOther({
  x: humans.bmi,
  y: droids.bmi,
  parametric: "auto", // Auto-detects appropriate test
});

console.log(`Droid conspiracy? Test: ${bmiTest.testName}, p-value: ${s.round(bmiTest.pValue, 3)}`);

// Output:
// Droid conspiracy? Test: Independent T-Test, p-value: 0.261

// Test 2: Are height and mass correlated among all characters?
const heightMassTest = s.compare.twoGroups.association.toEachOther({
  x: analysis.height_cm,
  y: analysis.mass_kg,
  method: "auto", // Selects best choice between Pearson, Spearman, or Kendall
});

console.log(`Height and mass correlation?
Test: ${heightMassTest.testName}
${heightMassTest.effectSize.name}: ${s.round(heightMassTest.effectSize.value, 3)}
p-value: ${s.round(heightMassTest.pValue, 3)}`);


// Output:
// Height and mass correlation? 
// Test: Kendall's rank correlation tau
// Kendall's Tau: 1
// p-value: 0.083

Proven Bug Prevention

Empirical research shows that static typing significantly reduces production bugs

38%
Production Bugs Preventable

Research-Backed Type Safety

Evidence suggests that static typing prevents 15-38% of bugs that would otherwise reach production. These are conservative estimates focusing on publicly visible, type-related defects.

Note: These figures represent lower bounds, excluding pre-commit logic issues and data validation bugs.

Ready to get started?

Start building data analytics workflows with type safety.