如何使用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
相关产品推荐
相关产品推荐

