{"spec_id":"bar-error","library":"echarts","language":"javascript","code":"// anyplot.ai\n// bar-error: Bar Chart with Error Bars\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Mean crop yield by fertilizer treatment, kg per plot (n = 30 plots each),\n// with error bars showing ±1 standard deviation.\nconst categories = [\"Control\", \"Nitrogen\", \"Phosphorus\", \"Potassium\", \"N+P+K\", \"Compost\"];\nconst means = [18.4, 24.7, 21.3, 22.9, 29.6, 26.1];\nconst sds = [2.1, 2.8, 2.4, 2.5, 3.1, 2.7];\nconst rows = categories.map((category, idx) => ({\n  idx,\n  category,\n  low: means[idx] - sds[idx],\n  high: means[idx] + sds[idx],\n}));\nconst maxWithError = Math.max(...means.map((m, i) => m + sds[i]));\n\n// --- Custom-series renderer for vertical error-bar whiskers -----------------\nfunction whiskerRenderItem(params, api) {\n  const categoryIndex = api.value(0);\n  const low = api.coord([categoryIndex, api.value(1)]);\n  const high = api.coord([categoryIndex, api.value(2)]);\n  const capHalf = 16;\n  const style = { stroke: t.ink, lineWidth: 2.5 };\n  return {\n    type: \"group\",\n    children: [\n      { type: \"line\", shape: { x1: low[0] - capHalf, y1: low[1], x2: low[0] + capHalf, y2: low[1] }, style },\n      { type: \"line\", shape: { x1: high[0] - capHalf, y1: high[1], x2: high[0] + capHalf, y2: high[1] }, style },\n      { type: \"line\", shape: { x1: low[0], y1: low[1], x2: high[0], y2: high[1] }, style },\n    ],\n  };\n}\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"bar-error · javascript · echarts · anyplot.ai\",\n    subtext: \"Error bars represent ±1 standard deviation (n = 30 plots per treatment)\",\n    left: \"center\",\n    top: 30,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n    subtextStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  grid: { left: 90, right: 60, top: 160, bottom: 90, containLabel: true },\n  xAxis: {\n    type: \"category\",\n    data: categories,\n    name: \"Treatment\",\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: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    min: 0,\n    max: Math.ceil(maxWithError * 1.15),\n    name: \"Yield (kg per plot)\",\n    nameLocation: \"center\",\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      type: \"bar\",\n      name: \"Mean yield\",\n      barWidth: \"48%\",\n      itemStyle: { color: t.palette[0] },\n      data: means,\n      z: 2,\n      markLine: {\n        symbol: \"none\",\n        silent: true,\n        label: {\n          formatter: \"Average: {c} kg\",\n          color: t.inkSoft,\n          fontSize: 13,\n          position: \"insideEndTop\",\n        },\n        lineStyle: { color: t.inkSoft, type: \"dashed\", width: 1.5 },\n        data: [{ type: \"average\", name: \"Average yield\" }],\n      },\n    },\n    {\n      type: \"custom\",\n      name: \"__error_bars\",\n      silent: true,\n      renderItem: whiskerRenderItem,\n      encode: { x: 0, y: [1, 2] },\n      data: rows.map((r) => [r.idx, r.low, r.high]),\n      z: 3,\n    },\n  ],\n});\n"}