You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中实现类似Python的Stem Plot(茎图)?

在JavaScript中实现Python风格的茎图

什么是茎图

茎图会在图表覆盖的每个x位置绘制一条从基线到y值的垂直线,并在垂直线顶端放置一个标记点。

你可以通过以下两种方式在JavaScript中实现这类图表:

方法一:原生Canvas实现

无需依赖任何库,直接用HTML5 Canvas API绘制,灵活性拉满:

<canvas id="stemPlot" width="600" height="400"></canvas>

<script>
// 准备示例数据
const data = [
  { x: 0, y: 25 },
  { x: 1, y: 40 },
  { x: 2, y: 15 },
  { x: 3, y: 35 },
  { x: 4, y: 20 },
  { x: 5, y: 45 }
];
const baselineY = 350; // 基线的y坐标(Canvas坐标系y轴向下为正)
const padding = 50;
const canvas = document.getElementById('stemPlot');
const ctx = canvas.getContext('2d');

// 计算缩放比例,适配Canvas尺寸
const maxX = Math.max(...data.map(d => d.x));
const maxY = Math.max(...data.map(d => d.y));
const xScale = (canvas.width - 2 * padding) / maxX;
const yScale = (baselineY - padding) / maxY;

// 绘制底部基线
ctx.beginPath();
ctx.moveTo(padding, baselineY);
ctx.lineTo(canvas.width - padding, baselineY);
ctx.strokeStyle = '#333';
ctx.stroke();

// 遍历数据绘制茎线和标记点
data.forEach(point => {
  const xPos = padding + point.x * xScale;
  const yPos = baselineY - point.y * yScale;

  // 绘制茎(垂直线)
  ctx.beginPath();
  ctx.moveTo(xPos, baselineY);
  ctx.lineTo(xPos, yPos);
  ctx.strokeStyle = '#666';
  ctx.lineWidth = 1;
  ctx.stroke();

  // 绘制顶端标记点
  ctx.beginPath();
  ctx.arc(xPos, yPos, 4, 0, Math.PI * 2);
  ctx.fillStyle = '#ff4444';
  ctx.fill();
  ctx.strokeStyle = '#000';
  ctx.stroke();
});
</script>

你可以根据需求调整基线位置、标记样式、颜色、尺寸等参数。

方法二:基于Chart.js扩展实现

如果习惯用可视化库,可以通过Chart.js的自定义插件改造现有图表类型:

<canvas id="stemPlotChartjs" width="600" height="400"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script>
const ctx = document.getElementById('stemPlotChartjs').getContext('2d');

new Chart(ctx, {
  type: 'line',
  data: {
    labels: [0, 1, 2, 3, 4, 5],
    datasets: [{
      label: '茎图数据',
      data: [25, 40, 15, 35, 20, 45],
      pointBackgroundColor: '#ff4444',
      pointRadius: 4,
      showLine: false, // 隐藏数据点之间的连线
      borderWidth: 0
    }]
  },
  options: {
    scales: {
      y: {
        beginAtZero: true,
        grid: { display: false }
      },
      x: {
        grid: { display: false }
      }
    },
    plugins: {
      // 自定义插件绘制茎线
      afterDatasetsDraw: (chart) => {
        const ctx = chart.ctx;
        const yAxis = chart.scales.y;
        const xAxis = chart.scales.x;
        const baselineY = yAxis.getPixelForValue(0); // 以y=0为基线

        chart.data.datasets.forEach((dataset) => {
          dataset.data.forEach((value, index) => {
            const xPos = xAxis.getPixelForTick(index);
            const yPos = yAxis.getPixelForValue(value);

            // 绘制茎线
            ctx.beginPath();
            ctx.moveTo(xPos, baselineY);
            ctx.lineTo(xPos, yPos);
            ctx.strokeStyle = '#666';
            ctx.lineWidth = 1;
            ctx.stroke();
          });
        });
      }
    }
  }
});
</script>

该方案利用Chart.js的插件机制,在绘制完数据点后额外添加从基线到每个点的垂直线,快速实现茎图效果。


内容的提问来源于stack exchange,提问作者hermi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:15:07