{"spec_id":"ecdf-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// ecdf-basic: Basic ECDF Plot\n// Library: echarts 5.5.1 | JavaScript 22.23.0\n// Quality: 90/100 | Created: 2026-06-25\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// Seeded LCG for reproducible in-browser data generation (no Math.random())\nfunction lcg(seed) {\n  let s = seed >>> 0;\n  return () => {\n    s = (s * 1664525 + 1013904223) >>> 0;\n    return s / 0x100000000;\n  };\n}\n\n// Box-Muller transform for normal distribution samples\nfunction normalSamples(n, mean, std, rng) {\n  const out = [];\n  for (let i = 0; i < n; i++) {\n    const u1 = Math.max(rng(), 1e-10);\n    const u2 = rng();\n    const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n    out.push(mean + std * z);\n  }\n  return out;\n}\n\n// Data: exam scores for two student groups in a university statistics course\nconst rng = lcg(42);\nconst tutoringScores  = normalSamples(200, 75, 12, rng);  // weekly tutoring sessions\nconst selfStudyScores = normalSamples(200, 63, 15, rng);  // independent study only\n\n// Build ECDF: sort and pair each value with its cumulative proportion\nconst tSorted = [...tutoringScores].sort((a, b) => a - b);\nconst sSorted = [...selfStudyScores].sort((a, b) => a - b);\n\nconst toECDF = (s) => {\n  const n = s.length, pad = (s[n - 1] - s[0]) * 0.03;\n  return [[+(s[0] - pad).toFixed(2), 0], ...s.map((x, i) => [+x.toFixed(2), +((i + 1) / n).toFixed(4)])];\n};\n\nconst tPts    = toECDF(tSorted);\nconst sPts    = toECDF(sSorted);\nconst tMedian = +tSorted[Math.floor(tSorted.length / 2)].toFixed(1);\nconst sMedian = +sSorted[Math.floor(sSorted.length / 2)].toFixed(1);\n\n// Chart\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"ecdf-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 20,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" },\n  },\n  legend: {\n    data: [\"Tutoring (n=200)\", \"Self-Study (n=200)\"],\n    right: 80,\n    top: 62,\n    textStyle: { color: t.inkSoft, fontSize: 16 },\n    itemWidth: 28,\n    itemHeight: 3,\n    backgroundColor: t.elevatedBg,\n    borderRadius: 4,\n    padding: [8, 14],\n  },\n  tooltip: {\n    trigger: \"axis\",\n    formatter: (params) => {\n      const score = params[0].value[0].toFixed(1);\n      return `Score: ${score}<br/>` +\n        params.map(p => `${p.seriesName}: ${(p.value[1] * 100).toFixed(1)}%`).join(\"<br/>\");\n    },\n  },\n  grid: { left: 100, right: 80, top: 110, bottom: 85 },\n  xAxis: {\n    type: \"value\",\n    name: \"Exam Score\",\n    nameLocation: \"center\",\n    nameGap: 48,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n    min: 15,\n    max: 120,\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Cumulative Proportion\",\n    nameLocation: \"center\",\n    nameGap: 60,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: (v) => v.toFixed(1) },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n    min: 0,\n    max: 1,\n  },\n  series: [\n    {\n      name: \"Tutoring (n=200)\",\n      type: \"line\",\n      step: \"end\",\n      data: tPts,\n      lineStyle: { color: t.palette[0], width: 3 },\n      itemStyle: { color: t.palette[0] },\n      areaStyle: { color: t.palette[0], opacity: 0.12 },\n      showSymbol: false,\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.palette[0], type: \"dashed\", opacity: 0.55, width: 1.5 },\n        label: { formatter: `Median ${tMedian}`, color: t.inkSoft, fontSize: 12, position: \"end\" },\n        data: [{ xAxis: tMedian }],\n      },\n    },\n    {\n      name: \"Self-Study (n=200)\",\n      type: \"line\",\n      step: \"end\",\n      data: sPts,\n      lineStyle: { color: t.palette[1], width: 3, type: \"dashed\" },\n      itemStyle: { color: t.palette[1] },\n      areaStyle: { color: t.palette[1], opacity: 0.10 },\n      showSymbol: false,\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.palette[1], type: \"dashed\", opacity: 0.55, width: 1.5 },\n        label: { formatter: `Median ${sMedian}`, color: t.inkSoft, fontSize: 12, position: \"end\" },\n        data: [{ xAxis: sMedian }],\n      },\n    },\n  ],\n});\n"}