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

如何使用Observable Plot(D3)消除K线图中的空白间隙?

解决Observable Plot K线图周末空白间隙问题

问题:使用Observable Plot开发K线图,数据集不含周末休市时段的数据,但生成的图表仍出现空白间隙。已确认数据解析正确,尝试调整x轴的tick、domain等参数后问题仍未解决。

问题原因

Observable Plot默认将日期类型的x轴设置为连续时间轴,会自动计算并填充时间序列中的间隔(包括周末这类无数据的时段),因此出现空白间隙。

解决方案

将x轴类型改为序数轴(ordinal),让图表严格按照数据点的顺序排列,不再自动填充时间间隙。只需在x轴配置中添加type: "ordinal"即可。

修改后的代码示例

核心修改点

在每个图表配置的x对象中添加type: "ordinal":

x: {
  type: "ordinal", // 关键:切换为序数轴消除空白
  domain: [ticker[0].date, ticker[ticker.length - 1].date],
  reverse: true,
  tickFormat: d => Plot.formatIsoDate(d),
  tickRotate: -15,
  tickPadding: 5,
}

完整修改代码

const ticker = parseDates(tickerData);
const radioBtns = document.getElementsByName("chartType");
let chartType;

for(var i = 0; i < radioBtns.length; i++) {
  radioBtns[i].onclick = updateChart;
}

updateChart();

function updateChart() {
  // 重置按钮状态图标
  candlestickLabel.childNodes[0].src = "assets/candlestick-unselected.png";
  lineLabel.childNodes[0].src = "assets/line-unselected.png";
  areaLabel.childNodes[0].src = "assets/area-unselected.png";

  for (let i = 0; i < radioBtns.length; i++) {
    if (radioBtns[i].checked) {
      chartType = radioBtns[i];
      
      const labels = document.getElementsByTagName("label");
      for (let j = 0; j < labels.length; j++) {
        if (labels[j].htmlFor == radioBtns[i].id) {
          labels[j].childNodes[0].src = "assets/" + chartType.value + "-selected.png";
        }
      }
    }
  }

  let plot = null;

  if (chartType.value == "candlestick") {
    plot = Plot.plot({
      inset: 20,
      width: 1000,
      height: 400,
      grid: true,
      x: {
        type: "ordinal", // 切换为序数轴
        domain: [ticker[0].date, ticker[ticker.length - 1].date],
        reverse: true,
        tickFormat: d => Plot.formatIsoDate(d),
        tickRotate: -15,
        tickPadding: 5,
      },
      y: {
        label: "↑ Stock price ($)"
      },
      color: {
        domain: [-1, 0, 1],
        range: ["#f23645", "currentColor", "#138808"]
      },
      marks: [
        Plot.ruleX(ticker.filter(d => d.low), {
          x: "date",
          y1: "low",
          y2: "high",
          stroke: d => Math.sign(d.close - d.open),
        }),
        Plot.ruleX(ticker, {
          x: "date",
          y1: "open",
          y2: "close",
          stroke: d => Math.sign(d.close - d.open),
          strokeWidth: 4,
          strokeLinecap: "butt"
        })
      ]
    });
  } else if (chartType.value == "line") {
    plot = Plot.plot({
      inset: 6,
      width: 1000,
      height: 400,
      grid: true,
      x: {
        type: "ordinal", // 切换为序数轴
        domain: [new Date(ticker[0].date), new Date(ticker[ticker.length - 1].date)],
        reverse: true
      },
      y: {
        label: "↑ Stock Price ($)"
      },
      marks: [
        Plot.lineY(ticker, {
          x: "date",
          y: "close",
          stroke: "#6495ed"
        })
      ]
    });
  } else {
    plot = Plot.plot({
      inset: 6,
      width: 1000,
      height: 400,
      grid: true,
      x: {
        type: "ordinal", // 切换为序数轴
        domain: [new Date(ticker[0].date), new Date(ticker[ticker.length - 1].date)],
        reverse: true
      },
      y: {
        label: "↑ Stock Price ($)"
      },
      marks: [
        Plot.lineY(ticker, {
          x: "date",
          y: "close",
          stroke: "#6495ed"
        }),
        Plot.areaY(ticker, {
          x: "date",
          y1: Math.min(...ticker.map((t) => t.close)) - 10,
          y2: "close",
          fill: "#6495ed",
          fillOpacity: 0.3
        })
      ]
    });
  }

  const div = chartDiv;
  div.innerHTML = "";
  div.append(plot);
}

// 工具函数
const weeks = (start, stop, stride) => d3.utcMonday.every(stride).range(start, +stop + 1);
const weekdays = (start, stop) => d3.utcDays(start, +stop + 1).filter(d => d.getUTCDay() !== 0 && d.getUTCDay() !== 6);

function parseDates(data) {
  return data.map(d => ({
    ...d,
    date: new Date(d.date)
  }));
}

数据集格式

const tickerData = [{
          date: "2024-09-10",
          open: 204.2000,
          high: 205.8300,
          low: 202.8700,
          close: 205.3200,
          volume: 3070644
        },{
          date: "2024-09-09",
          open: 201.9400,
          high: 205.0500,
          low: 201.4300,
          close: 203.5300,
          volume: 3705004
        },{
          date: "2024-09-06",
          open: 202.3800,
          high: 204.1000,
          low: 199.3350,
          close: 200.7400,
          volume: 3304491
        }
}]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 14:24:51