{"spec_id":"pdp-basic","library":"muix","language":"javascript","code":"// anyplot.ai\n// pdp-basic: Partial Dependence Plot\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 97/100 | Created: 2026-09-05\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst muted = window.ANYPLOT_THEME === \"dark\" ? \"#A8A79F\" : \"#6B6A63\";\n\n// --- Data (in-memory, deterministic) ---------------------------------------\n// Tiny fixed-seed LCG — the browser has no seeded RNG.\nfunction makeLcg(seed: number) {\n  let state = seed >>> 0;\n  return function next() {\n    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\n\nconst rng = makeLcg(20260905);\nconst GRID_POINTS = 61;\nconst SPEND_MIN = 5;\nconst SPEND_MAX = 65;\n\n// Simulated PartialDependenceDisplay output for a GradientBoostingRegressor\n// predicting weekly units sold from weekly marketing spend, averaging over\n// every other feature in the model.\nconst spend = Array.from(\n  { length: GRID_POINTS },\n  (_, i) => SPEND_MIN + (i * (SPEND_MAX - SPEND_MIN)) / (GRID_POINTS - 1),\n);\n\nconst rawPrediction = spend.map((x) => {\n  const saturating = 620 / (1 + Math.exp(-(x - 32) / 7));\n  const modelWiggle = (rng() - 0.5) * 16;\n  return saturating + modelWiggle;\n});\n\n// Center at zero so the curve reads as \"effect relative to the average\n// prediction\" rather than an absolute (and arbitrary-looking) sales count.\nconst meanPrediction =\n  rawPrediction.reduce((sum, v) => sum + v, 0) / rawPrediction.length;\nconst partialDependence = rawPrediction.map((v) => v - meanPrediction);\n\n// Confidence band widens toward both ends of the spend range, where training\n// samples are sparser and the model's average prediction is less certain.\nconst ciHalfWidth = spend.map((x) => 9 + 0.5 * Math.abs(x - 32));\nconst ciLowerBound = partialDependence.map((v, i) => v - ciHalfWidth[i]);\nconst ciBandWidth = ciHalfWidth.map((halfWidth) => 2 * halfWidth);\n\n// A handful of individual conditional expectation (ICE) curves — the\n// per-instance predictions the PDP curve is the average of. Each sample\n// varies the saturation midpoint/amplitude and carries its own model noise,\n// then is shifted by the same meanPrediction constant as the PDP so it reads\n// in the same \"effect relative to average\" units.\nconst ICE_SAMPLE_COUNT = 8;\nconst iceCurves = Array.from({ length: ICE_SAMPLE_COUNT }, () => {\n  const midpointShift = (rng() - 0.5) * 16;\n  const amplitudeScale = 0.82 + rng() * 0.36;\n  return spend.map((x) => {\n    const saturating =\n      (620 * amplitudeScale) /\n      (1 + Math.exp(-(x - (32 + midpointShift)) / 7));\n    const modelWiggle = (rng() - 0.5) * 12;\n    return saturating + modelWiggle - meanPrediction;\n  });\n});\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const W = window.ANYPLOT_SIZE.width;\n  const H = window.ANYPLOT_SIZE.height;\n  const CHART_TOP = 64;\n\n  const title = \"pdp-basic · javascript · muix · anyplot.ai\";\n  const titleSize =\n    title.length > 67 ? Math.round((22 * 67) / title.length) : 22;\n\n  // MUI X's built-in y-axis title sits at a fixed, small offset from the\n  // axis line — too small to clear wide 4-digit tick numbers, so it renders\n  // the axis label as its own rotated element in a reserved strip instead.\n  const Y_LABEL_W = 44;\n  const yAxisLabel = \"Partial dependence (Δ units sold/week)\";\n\n  return (\n    <Box sx={{ position: \"relative\", width: W, height: H, bgcolor: t.pageBg }}>\n      <Box sx={{ position: \"absolute\", top: 20, left: 56, right: 56 }}>\n        <Typography sx={{ color: t.ink, fontSize: titleSize, fontWeight: 500 }}>\n          {title}\n        </Typography>\n      </Box>\n      <Box\n        sx={{\n          position: \"absolute\",\n          top: CHART_TOP,\n          left: 0,\n          width: Y_LABEL_W,\n          bottom: 0,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <Typography\n          sx={{\n            color: t.ink,\n            fontSize: 16,\n            whiteSpace: \"nowrap\",\n            transform: \"rotate(-90deg)\",\n          }}\n        >\n          {yAxisLabel}\n        </Typography>\n      </Box>\n      <Box\n        sx={{\n          position: \"absolute\",\n          top: CHART_TOP,\n          left: Y_LABEL_W,\n          right: 0,\n          bottom: 0,\n        }}\n      >\n        <LineChart\n          width={W - Y_LABEL_W}\n          height={H - CHART_TOP}\n          skipAnimation\n          series={[\n            ...iceCurves.map((curve, i) => ({\n              id: `ice-${i}`,\n              data: curve,\n              color: t.palette[0],\n              curve: \"monotoneX\" as const,\n              area: false,\n              showMark: false,\n              valueFormatter: () => null,\n            })),\n            {\n              id: \"pdp\",\n              data: partialDependence,\n              label: \"Partial dependence\",\n              color: t.palette[0],\n              curve: \"monotoneX\",\n              area: false,\n              showMark: ({ index }: { index: number }) => index % 6 === 0,\n              valueFormatter: (value: number | null) =>\n                value == null\n                  ? null\n                  : `${value >= 0 ? \"+\" : \"\"}${value.toFixed(0)} units/week`,\n            },\n            {\n              id: \"ci-lower\",\n              data: ciLowerBound,\n              color: muted,\n              curve: \"monotoneX\",\n              area: true,\n              stack: \"ci\",\n              showMark: false,\n              valueFormatter: () => null,\n            },\n            {\n              id: \"ci-band\",\n              data: ciBandWidth,\n              label: \"95% confidence interval\",\n              color: muted,\n              curve: \"monotoneX\",\n              area: true,\n              stack: \"ci\",\n              showMark: false,\n              valueFormatter: (value: number | null) =>\n                value == null ? null : `±${(value / 2).toFixed(0)} units/week`,\n            },\n          ]}\n          xAxis={[\n            {\n              data: spend,\n              scaleType: \"linear\",\n              label: \"Weekly marketing spend ($1,000s)\",\n              labelStyle: { fontSize: 16 },\n              tickLabelStyle: { fontSize: 14 },\n              valueFormatter: (value: number) => `$${value.toFixed(0)}k`,\n            },\n          ]}\n          yAxis={[\n            {\n              tickLabelStyle: { fontSize: 14 },\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{ legend: { labelStyle: { fontSize: 14 } } }}\n          sx={{\n            \"& .MuiLineElement-series-pdp\": { strokeWidth: 3.5 },\n            \"& .MuiLineElement-series-ci-band\": { strokeWidth: 0 },\n            \"& .MuiLineElement-series-ci-lower\": { strokeWidth: 0 },\n            \"& .MuiAreaElement-series-ci-lower\": { fill: \"none\" },\n            \"& .MuiAreaElement-series-ci-band\": { fillOpacity: 0.22 },\n            ...Object.fromEntries(\n              iceCurves.map((_, i) => [\n                `& .MuiLineElement-series-ice-${i}`,\n                { strokeWidth: 1.1, strokeOpacity: 0.22 },\n              ]),\n            ),\n          }}\n        >\n          <ChartsReferenceLine\n            y={0}\n            label=\"average prediction\"\n            labelAlign=\"end\"\n            labelStyle={{ fontSize: 13, fill: muted }}\n            lineStyle={{ stroke: muted, strokeDasharray: \"6 4\" }}\n          />\n        </LineChart>\n      </Box>\n    </Box>\n  );\n}\n"}