{"spec_id":"histogram-overlapping","library":"echarts","language":"javascript","code":"// anyplot.ai\n// histogram-overlapping: Overlapping Histograms\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-08-18\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic LCG) ------------------------------------\nfunction lcg(seed) {\n  let state = seed;\n  return function next() {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nfunction normalSamples(rand, mean, stdDev, count) {\n  const samples = [];\n  for (let i = 0; i < count; i += 2) {\n    const u1 = Math.max(rand(), 1e-9);\n    const u2 = rand();\n    const mag = Math.sqrt(-2.0 * Math.log(u1));\n    samples.push(mean + stdDev * mag * Math.cos(2 * Math.PI * u2));\n    samples.push(mean + stdDev * mag * Math.sin(2 * Math.PI * u2));\n  }\n  return samples.slice(0, count);\n}\n\nconst randControl = lcg(42);\nconst randTreatment = lcg(1337);\n\nconst clampScore = (value) => Math.min(100, Math.max(0, value));\n\nconst controlScores = normalSamples(randControl, 68, 11, 220).map(clampScore);\nconst treatmentScores = normalSamples(randTreatment, 76, 10, 220).map(clampScore);\n\nconst meanOf = (samples) => samples.reduce((sum, v) => sum + v, 0) / samples.length;\nconst controlMean = meanOf(controlScores);\nconst treatmentMean = meanOf(treatmentScores);\n\n// --- Shared bins across both groups ------------------------------------------\nconst allScores = controlScores.concat(treatmentScores);\nconst dataMin = Math.min(...allScores);\nconst dataMax = Math.max(...allScores);\nconst binCount = 20;\nconst binWidth = (dataMax - dataMin) / binCount;\n\nfunction toHistogram(samples) {\n  const counts = new Array(binCount).fill(0);\n  for (const value of samples) {\n    let index = Math.floor((value - dataMin) / binWidth);\n    if (index >= binCount) index = binCount - 1;\n    if (index < 0) index = 0;\n    counts[index] += 1;\n  }\n  return counts;\n}\n\nconst binLabels = [];\nfor (let i = 0; i < binCount; i += 1) {\n  binLabels.push(Math.round(dataMin + i * binWidth));\n}\n\nconst controlCounts = toHistogram(controlScores);\nconst treatmentCounts = toHistogram(treatmentScores);\n\nconst binIndexFor = (value) => {\n  const idx = Math.round((value - dataMin) / binWidth);\n  return Math.min(binCount - 1, Math.max(0, idx));\n};\nconst controlMeanIndex = binIndexFor(controlMean);\nconst treatmentMeanIndex = binIndexFor(treatmentMean);\n\n// --- Init ---------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"histogram-overlapping · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  legend: {\n    top: 50,\n    textStyle: { color: t.inkSoft, fontSize: 16 },\n  },\n  grid: { left: 90, right: 60, top: 110, bottom: 80 },\n  xAxis: {\n    type: \"category\",\n    data: binLabels,\n    name: \"Exam Score\",\n    nameLocation: \"middle\",\n    nameGap: 42,\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: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Frequency\",\n    nameLocation: \"middle\",\n    nameGap: 60,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Control group\",\n      type: \"bar\",\n      data: controlCounts,\n      barGap: \"-100%\",\n      barCategoryGap: \"0%\",\n      itemStyle: {\n        color: t.palette[0],\n        opacity: 0.55,\n        borderColor: t.pageBg,\n        borderWidth: 1,\n      },\n      emphasis: { disabled: true },\n      z: 2,\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.palette[0], type: \"dashed\", width: 2 },\n        label: { show: true, formatter: \"Control mean\", color: t.inkSoft, fontSize: 12 },\n        data: [{ xAxis: controlMeanIndex }],\n      },\n    },\n    {\n      name: \"Treatment group\",\n      type: \"bar\",\n      data: treatmentCounts,\n      barGap: \"-100%\",\n      barCategoryGap: \"0%\",\n      itemStyle: {\n        color: t.palette[1],\n        opacity: 0.55,\n        borderColor: t.pageBg,\n        borderWidth: 1,\n      },\n      emphasis: { disabled: true },\n      z: 3,\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.palette[1], type: \"dashed\", width: 2 },\n        label: { show: true, formatter: \"Treatment mean\", color: t.inkSoft, fontSize: 12 },\n        data: [{ xAxis: treatmentMeanIndex }],\n      },\n    },\n  ],\n});\n"}