{
  "schemaVersion": 1,
  "slug": "chi-square-calculator",
  "category": "statistics",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "515b463af087e8d1819300db74b010f7a374ec3c55eaf1ea13197256360bff7c",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/chi-square-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "χ² = Σ (O_i − E_i)² ÷ E_i,  df = k − 1",
    "vars": [
      [
        "χ²",
        "Chi-square statistic"
      ],
      [
        "O_i",
        "Observed count"
      ],
      [
        "E_i",
        "Expected count"
      ],
      [
        "n",
        "Number of categories"
      ],
      [
        "df",
        "Degrees of freedom"
      ]
    ],
    "note": "Expected counts must be positive and sum to the observed total. For a goodness-of-fit test with fixed expected proportions, df = k − 1. Small expected counts may make the approximation unreliable."
  },
  "externalEvidence": {
    "reviewedAt": "2026-10-12",
    "sources": [
      {
        "name": "NIST — Chi-square goodness-of-fit test",
        "url": "https://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm"
      }
    ],
    "referenceRole": "definition",
    "implementationHash": "515b463af087e8d1819300db74b010f7a374ec3c55eaf1ea13197256360bff7c",
    "basis": "χ² = Σ (O_i − E_i)² ÷ E_i,  df = k − 1",
    "limitations": "The references explain definitions and methods; any extra preset must be checked against the assumptions on this page."
  },
  "contract": {
    "compute": "const parse = (value) => { const raw=String(value ?? '').trim(); if(!raw) return null; const parts=raw.split(/[,\\s]+/); if(parts.some((part)=>part==='' || !Number.isFinite(Number(part)))) return null; return parts.map(Number); };\nconst obs=parse(observed), exp=parse(expected);\nconst fail=()=>({chiSquare:null,degreesOfFreedom:null,totalObserved:null,totalExpected:null,largestContribution:null,__error:'inputError'});\nif(!obs || !exp || obs.length<2 || obs.length!==exp.length || obs.some((n)=>n<0) || exp.some((n)=>n<=0)) return fail();\nconst totalObserved=obs.reduce((a,b)=>a+b,0), totalExpected=exp.reduce((a,b)=>a+b,0);\nif(Math.abs(totalObserved-totalExpected)>Math.max(1,totalObserved,totalExpected)*1e-9) return fail();\nlet chi=0, largest=0;\nfor(let k=0;k<obs.length;k++){const c=(obs[k]-exp[k])**2/exp[k];chi+=c;if(c>largest)largest=c;}\nreturn {chiSquare:chi,degreesOfFreedom:obs.length-1,totalObserved,totalExpected,largestContribution:largest};",
    "inputs": [
      {
        "name": "observed",
        "type": "text",
        "default": "30, 20, 25, 25",
        "step": 1
      },
      {
        "name": "expected",
        "type": "text",
        "default": "25, 25, 25, 25",
        "step": 1
      }
    ],
    "outputs": [
      {
        "name": "chiSquare",
        "precision": 4,
        "unit": ""
      },
      {
        "name": "degreesOfFreedom",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "totalObserved",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "totalExpected",
        "precision": 0,
        "unit": ""
      },
      {
        "name": "largestContribution",
        "precision": 4,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
