Stack(堆栈图)

208 阅读2分钟

简介

Stack布局是一种数据可视化布局,‌专门用于处理需要将多个数据系列叠加在一起以展示它们之间的关系和总和的情况。‌这种布局特别适用于处理多维数据,‌例如,‌当需要将不同类别的数据(‌如不同产品的销售额)‌叠加在一起以显示它们的总销售额和相对贡献时,‌Stack布局非常有用。‌

数据

const data = [{
		month: new Date(2015, 0, 1),
		apples: 3840,
		bananas: 1920,
		cherries: 960,
		dates: 400
	},
	{
		month: new Date(2015, 1, 1),
		apples: 1600,
		bananas: 1440,
		cherries: 960,
		dates: 400
	},
	{
		month: new Date(2015, 2, 1),
		apples: 640,
		bananas: 960,
		cherries: 640,
		dates: 400
	},
	{
		month: new Date(2015, 3, 1),
		apples: 320,
		bananas: 480,
		cherries: 640,
		dates: 400
	}
];

//d3.timeFormat
const dateString = new Date();
const today = d3.timeFormat("%Y-%m-%d %H:%M:%S")(dateString);
console.log(today);

主程序

  • d3.stack - 创建一个新的堆叠生成器。
  • stack - 为给定数据生成堆叠数据。
  • stack.keys - 设置键访问器。
  • stack.value - 设置值访问器。
  • stack.order - 设置排序访问器。
    • d3.stackOrderAscending - 将最小值放在底部。
    • d3.stackOrderDescending - 将最大值放在底部。
    • d3.stackOrderInsideOut - 将最大值放在中部。
    • d3.stackOrderNone - 使用给定的系列顺序。
    • d3.stackOrderReverse - 系列给定的系列相反的顺序。
  • stack.offset - 设置偏移访问器。
    • d3.stackOffsetExpand - 标准化为0=1之间。
    • d3.stackOffsetNone - 应用零基准。
    • d3.stackOffsetSilhouette - 将流图居中在0附近。
    • d3.stackOffsetWiggle - 流图最小摆动。
const stack = d3.stack()
	.keys(["apples", "bananas", "cherries", "dates"])
	.order(d3.stackOrderNone) // 设置排序访问器 使用给定的系列顺序
	.offset(d3.stackOffsetNone); // 设置偏移访问器 应用零基准

const dataset = stack(data);
console.log(dataset);

let dimensions = {
	// 图表尺寸
	width: 600,
	height: 600,
	margin: {
		top: 30,
		right: 10,
		bottom: 50,
		left: 50
	}
};
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right;
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom;

const svg = d3
	.select("body")
	.append("svg")
	.attr("width", dimensions.width)
	.attr("height", dimensions.height);

const bounds = svg
	.append("g")
	.style(
		"transform",
		`translate(${dimensions.margin.left}px, ${dimensions.margin.top}px)`
	); // 画布边界

//需要三个比例尺:x轴比例尺,y轴比例尺和颜色比例尺 ///////////////////////
const xScale = d3.scaleBand()
	.domain(data.map((d) => d.month))
	.range([0, dimensions.boundedWidth])
	.padding(0.1);

const yScale = d3.scaleLinear()
	.domain([0, d3.max(dataset, (d) => d3.max(d, (d) => d[1]))])
	.range([dimensions.boundedHeight, 0]);

const color = d3.scaleOrdinal()
	.domain(["apples", "bananas", "cherries", "dates"])
	.range(["#c51b7d", "#e9a3c9", "#fde0ef", "#f1e4f1"]);

// 绘制数据 ///////////////////////////////////////////////
// 堆叠柱状图 
bounds
	.selectAll("g")
	.data(dataset) // 绑定数据绘制四个分组(柱状图盒子)
	.join("g")
	.selectAll("rect")
	.data((d) => d) // 绑定每一组的数据,绘制四个矩形
	.join("rect")
	.attr("x", (d) => {
		return xScale(d.data.month);
	})
	.attr("y", (d) => yScale(d[1]))
	.attr("width", xScale.bandwidth())
	.attr("height", (d) => yScale(d[0]) - yScale(d[1]))
	.attr("fill", (d, i, nodes) => {
		// console.log(nodes[i].parentNode.__data__);
		return color(nodes[i].parentNode.__data__.key);
	});

// 多列柱状图 
// bounds
// 	.selectAll("g")
// 	.data(dataset)
// 	.join("g")
// 	.selectAll("rect")
// 	.data((d) => d)
// 	.join("rect")
// 	.attr("x", (d, i, nodes) => {
// 		return (
// 			xScale(data[i].month) +
// 			(xScale.bandwidth() / 4) * nodes[i].parentNode.__data__.index
// 		);
// 	})// 改x位置
// 	.attr("y", (d) => yScale(d[1] - d[0]))
// 	.attr("width", xScale.bandwidth() / 4)// 改宽度
// 	.attr("height", (d) => yScale(d[0]) - yScale(d[1]))
// 	.attr("fill", (d, i, nodes) => {
// 		return color(nodes[i].parentNode.__data__.key);
// 	});

//坐标轴 ///////////////////////////////////////////////////////////
const xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%b"));
const yAxis = d3.axisLeft(yScale);
bounds
	.append("g")
	.attr("transform", `translate(0, ${dimensions.boundedHeight})`)
	.call(xAxis);
bounds.append("g").call(yAxis);