{"spec_id":"pdp-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// pdp-basic: Partial Dependence Plot\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\n// The harness only exposes pageBg/elevatedBg/ink/inkSoft/grid/palette/amber/seq/div —\n// the \"muted\" semantic anchor (confidence-band fill) isn't a token field, so it's\n// derived here the same way the Python reference snippet derives INK_MUTED.\nconst MUTED = window.ANYPLOT_THEME === \"dark\" ? \"#A8A79F\" : \"#6B6A63\";\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Gradient-boosting model predicting crop yield; partial dependence of the\n// \"rainfall\" feature, averaged over all other features, centered at zero.\nlet seed = 42;\nfunction lcg() {\n  seed = (seed * 1664525 + 1013904223) >>> 0;\n  return seed / 4294967296;\n}\nfunction gaussian() {\n  const u1 = Math.max(lcg(), 1e-9);\n  const u2 = lcg();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n}\n\nconst GRID_POINTS = 60;\nconst R_MIN = 200;\nconst R_MAX = 1400;\n\nfunction rawResponse(r) {\n  const logistic = 3.0 / (1 + Math.exp(-(r - 550) / 140));\n  const waterlogging = r > 900 ? 0.0000009 * Math.pow(r - 900, 2) : 0;\n  return logistic - waterlogging;\n}\n\nconst featureValues = [];\nconst rawCurve = [];\nfor (let i = 0; i < GRID_POINTS; i++) {\n  const r = R_MIN + (i * (R_MAX - R_MIN)) / (GRID_POINTS - 1);\n  featureValues.push(r);\n  rawCurve.push(rawResponse(r));\n}\nconst meanResponse = rawCurve.reduce((a, b) => a + b, 0) / rawCurve.length;\nconst partialDependence = rawCurve.map((v) => v - meanResponse);\n\n// Pointwise confidence half-width — widest at the tails, where training\n// samples (see rug plot below) are sparse and the estimate is less certain.\nconst DATA_CENTER = 650;\nconst halfWidth = featureValues.map(\n  (r) => 0.15 + 0.9 * Math.pow(Math.abs(r - DATA_CENTER) / DATA_CENTER, 1.4)\n);\nconst ciLower = partialDependence.map((v, i) => v - halfWidth[i]);\nconst ciBandHeight = halfWidth.map((h) => 2 * h);\n\n// ECharts pairs series data against an implicit index on a continuous\n// \"value\" xAxis unless each point is given explicitly as [x, y].\nconst partialDependenceXY = featureValues.map((r, i) => [r, partialDependence[i]]);\nconst ciLowerXY = featureValues.map((r, i) => [r, ciLower[i]]);\nconst ciBandHeightXY = featureValues.map((r, i) => [r, ciBandHeight[i]]);\n\n// Training-data rug: rainfall samples clustered around the data center.\nconst RUG_SAMPLES = 70;\nconst rugValues = [];\nfor (let i = 0; i < RUG_SAMPLES; i++) {\n  const r = Math.min(R_MAX, Math.max(R_MIN, DATA_CENTER + gaussian() * 180));\n  rugValues.push([r, 0.5]);\n}\n\nconst yMin = Math.min(...ciLower) - 0.25;\nconst yMax = Math.max(...partialDependence.map((v, i) => v + halfWidth[i])) + 0.25;\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Title (scales down for long titles per style guide) --------------------\nconst titleText = \"Crop Yield vs. Rainfall · pdp-basic · javascript · echarts · anyplot.ai\";\nconst baseTitleFontSize = 22;\nconst titleFontSize =\n  titleText.length > 67 ? Math.round((baseTitleFontSize * 67) / titleText.length) : baseTitleFontSize;\n\n// --- Option -------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: \"transparent\",\n  title: {\n    text: titleText,\n    left: \"center\",\n    top: 20,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 500 },\n  },\n  legend: {\n    data: [\"Partial dependence\", \"Confidence interval\", \"Training data (rug)\"],\n    top: 70,\n    textStyle: { color: t.ink, fontSize: 16 },\n  },\n  grid: [\n    { left: 110, right: 60, top: 110, height: 560 },\n    { left: 110, right: 60, top: 700, height: 70 },\n  ],\n  xAxis: [\n    {\n      gridIndex: 0,\n      type: \"value\",\n      min: R_MIN,\n      max: R_MAX,\n      axisLabel: { show: false },\n      axisLine: { show: false },\n      axisTick: { show: false },\n      splitLine: { show: false },\n    },\n    {\n      gridIndex: 1,\n      type: \"value\",\n      min: R_MIN,\n      max: R_MAX,\n      name: \"Rainfall (mm)\",\n      nameLocation: \"middle\",\n      nameGap: 40,\n      nameTextStyle: { color: t.ink, fontSize: 18 },\n      axisLabel: { color: t.inkSoft, fontSize: 14 },\n      axisLine: { lineStyle: { color: t.inkSoft } },\n      axisTick: { lineStyle: { color: t.inkSoft } },\n      splitLine: { show: false },\n    },\n  ],\n  yAxis: [\n    {\n      gridIndex: 0,\n      type: \"value\",\n      min: Math.floor(yMin * 10) / 10,\n      max: Math.ceil(yMax * 10) / 10,\n      name: \"Δ Predicted Yield (t/ha)\",\n      nameLocation: \"middle\",\n      nameGap: 65,\n      nameTextStyle: { color: t.ink, fontSize: 18 },\n      axisLabel: {\n        color: t.inkSoft,\n        fontSize: 14,\n        formatter: (v) => (v > 0 ? \"+\" : \"\") + v.toFixed(1),\n      },\n      axisLine: { lineStyle: { color: t.inkSoft } },\n      splitLine: { lineStyle: { color: t.grid } },\n    },\n    {\n      gridIndex: 1,\n      type: \"value\",\n      min: 0,\n      max: 1,\n      show: false,\n      splitLine: { show: false },\n    },\n  ],\n  series: [\n    {\n      name: \"CI lower (hidden)\",\n      type: \"line\",\n      xAxisIndex: 0,\n      yAxisIndex: 0,\n      data: ciLowerXY,\n      stack: \"confidence\",\n      symbol: \"none\",\n      lineStyle: { opacity: 0 },\n      areaStyle: { opacity: 0 },\n      silent: true,\n      legendHoverLink: false,\n      z: 1,\n    },\n    {\n      name: \"Confidence interval\",\n      type: \"line\",\n      xAxisIndex: 0,\n      yAxisIndex: 0,\n      data: ciBandHeightXY,\n      stack: \"confidence\",\n      symbol: \"none\",\n      lineStyle: { opacity: 0 },\n      areaStyle: { color: MUTED, opacity: 0.25 },\n      itemStyle: { color: MUTED },\n      silent: true,\n      z: 1,\n    },\n    {\n      name: \"Partial dependence\",\n      type: \"line\",\n      xAxisIndex: 0,\n      yAxisIndex: 0,\n      data: partialDependenceXY,\n      symbol: \"none\",\n      lineStyle: { width: 3.5, color: t.palette[0] },\n      itemStyle: { color: t.palette[0] },\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        lineStyle: { color: t.inkSoft, type: \"dashed\", width: 1.5 },\n        label: { show: false },\n        data: [{ yAxis: 0 }],\n      },\n      z: 3,\n    },\n    {\n      name: \"Training data (rug)\",\n      type: \"scatter\",\n      xAxisIndex: 1,\n      yAxisIndex: 1,\n      data: rugValues,\n      symbol: \"rect\",\n      symbolSize: [2, 24],\n      itemStyle: { color: t.inkSoft, opacity: 0.5 },\n    },\n  ],\n});\n"}