Public
Edited
May 23, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

let g = svg
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);

g.selectAll("path")
.data(fromflatData)
.join("path")
.attr("fill", (d) => colorScale(d.id))
.attr("id", (d) => d.id)
.attr("d", (d) => {
// console.log(d);
return areaGenerator(d.areaPoints);
});
return svg.node();
}
Insert cell
fromflatData = FlatToFinal(flat_data)
Insert cell
import {a} from "3d00c9ea9e212875"
Insert cell
colorScale = d3.scaleOrdinal().domain(series.series.map(d => d.id)).range(['#ffa822', '#134e6f', '#ff6150', '#1ac0c6', '#dee0e6'])
Insert cell
series = {
const { align, spacing, xPadding } = form;
const { series, xScale, heightScale } = computeSeries({
data,
width,
height,
align,
spacing,
xPadding
});
return { series, xScale, heightScale };
}
Insert cell
Insert cell
Insert cell
Insert cell
xScale = d3
.scalePoint()
.domain(data[0].data.map((d) => d.x))
.range([0, width])
Insert cell
yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d3.max(d.data, (d) => d.y))])
.range([height, 0])
Insert cell
Insert cell
Insert cell
Insert cell
import {stacked} from "0a03a39eac099c0a"
Insert cell
Insert cell
Insert cell
function FlatToFinal(alluvial) {

let xKey = "year";
let yKey = "id";
let defaultValue = 5;
let overlap = true;
const slices = new Map();

let uniqueXkeys = [...new Set(alluvial.map((d) => d[xKey]))];
let uniqueYkeys = [...new Set(alluvial.map((d) => d[yKey]))];
let dataArray = Array.from(d3.group(alluvial, (d) => d[yKey]));

uniqueXkeys.forEach((key) => {
const filteredData = alluvial.filter((d) => d.year === key);
slices.set(key, {
id: key,
total: d3.sum(filteredData, (d) => +d.value),
values: uniqueYkeys.map((y) => {
const temp = filteredData.find((f) => f.id === y);
return temp ? { id: temp.id, value: +temp.value } : { id: y, value: 0 };
})
});
});

// step 2
const { align, spacing, xPadding } = form;
const xScale = d3.scalePoint().domain(uniqueXkeys).range([0, width]);

const heightScale = d3
.scaleLinear()
.domain([0, 105])
.range([0, height - uniqueYkeys.length * spacing]);

slices.forEach((slice, key) => {
slice.x = xScale(key);
const sliceTotalHeight =
heightScale(slice.total) + slice.values.length * spacing;

let offset = 0;
if (align === "middle") {
offset = (height - sliceTotalHeight) / 2;
} else if (align === "end") {
offset = height - sliceTotalHeight;
}

const heightSums = new Map();

slice.values
.sort((a, b) => b.value - a.value)
.forEach((value, currentRanking, all) => {
const previousValues = all.filter((v, ranking) => {
if (overlap) {
return ranking < currentRanking;
} else {
return v.value > value.value;
}
});
const beforeValue = previousValues.reduce((t, v) => t + v.value, 0);

const sliceValueForEachY = slice.values.find((v) => v[yKey]);

value.height = heightScale(value.value);
value.beforeHeight =
heightScale(beforeValue) +
offset +
spacing * (previousValues.length + 0.5);
});
});

const areaPointPadding = xScale.step() * Math.min(xPadding * 0.5, 0.5);
const filledData = dataArray.map(([key, dataArrayValue]) => {
const filteredDataByY = alluvial.filter((d) => d[yKey] === key);
const computedSerie = {
[yKey]: key,
data: uniqueXkeys.map((x) => {
const temp = filteredDataByY.find((f) => f[xKey] === x);
return temp
? { [xKey]: temp[xKey], value: +temp.value }
: { [xKey]: x, value: 0 };
}),
points: [],
areaPoints: []
};

return computedSerie;
});

let finalData = filledData.map((f) => {
f.data.forEach((d, i) => {
const slice = slices.get(d[xKey]);
const position = slice.values.find(d => d[yKey] == f[yKey])
const x = slice.x;
const { beforeHeight, height } = position;
const y = beforeHeight + height / 2;
const y0 = beforeHeight;
const y1 = beforeHeight + height;

if (i > 0) {
f.areaPoints.push({ x: x - areaPointPadding, y0, y1 });
}
f.areaPoints.push({ x, y0, y1 });
if (i < f.data.length - 1) {
f.areaPoints.push({ x: x + areaPointPadding, y0, y1 });
}
});
});
return filledData;

}
Insert cell
// fromflatData = {
// let xKey = "year";
// let yKey = "id";
// let defaultValue = 5;
// let overlap = true;
// const slices = new Map();

// let uniqueXkeys = [...new Set(alluvial.map((d) => d[xKey]))];
// let uniqueYkeys = [...new Set(alluvial.map((d) => d[yKey]))];
// let dataArray = Array.from(d3.group(alluvial, (d) => d[yKey]));

// uniqueXkeys.forEach((key) => {
// const filteredData = alluvial.filter((d) => d.year === key);
// slices.set(key, {
// id: key,
// total: d3.sum(filteredData, (d) => +d.value),
// values: uniqueYkeys.map((y) => {
// const temp = filteredData.find((f) => f.id === y);
// return temp ? { id: temp.id, value: +temp.value } : { id: y, value: 0 };
// })
// });
// });

// // step 2
// const { align, spacing, xPadding } = form;
// const xScale = d3.scalePoint().domain(uniqueXkeys).range([0, width]);

// const heightScale = d3
// .scaleLinear()
// .domain([0, 105])
// .range([0, height - uniqueYkeys.length * spacing]);

// slices.forEach((slice, key) => {
// slice.x = xScale(key);
// const sliceTotalHeight =
// heightScale(slice.total) + slice.values.length * spacing;

// let offset = 0;
// if (align === "middle") {
// offset = (height - sliceTotalHeight) / 2;
// } else if (align === "end") {
// offset = height - sliceTotalHeight;
// }

// const heightSums = new Map();

// slice.values
// .sort((a, b) => b.value - a.value)
// .forEach((value, currentRanking, all) => {
// const previousValues = all.filter((v, ranking) => {
// if (overlap) {
// return ranking < currentRanking;
// } else {
// return v.value > value.value;
// }
// });
// const beforeValue = previousValues.reduce((t, v) => t + v.value, 0);

// const sliceValueForEachY = slice.values.find((v) => v[yKey]);

// value.height = heightScale(value.value);
// value.beforeHeight =
// heightScale(beforeValue) +
// offset +
// spacing * (previousValues.length + 0.5);
// });
// });

// const areaPointPadding = xScale.step() * Math.min(xPadding * 0.5, 0.5);
// const filledData = dataArray.map(([key, dataArrayValue]) => {
// const filteredDataByY = alluvial.filter((d) => d[yKey] === key);
// const computedSerie = {
// [yKey]: key,
// data: uniqueXkeys.map((x) => {
// const temp = filteredDataByY.find((f) => f[xKey] === x);
// return temp
// ? { [xKey]: temp[xKey], value: +temp.value }
// : { [xKey]: x, value: 0 };
// }),
// points: [],
// areaPoints: []
// };

// return computedSerie;
// });

// let finalData = filledData.map((f) => {
// f.data.forEach((d, i) => {
// const slice = slices.get(d[xKey]);
// const position = slice.values.find(d => d[yKey] == f[yKey])
// const x = slice.x;
// const { beforeHeight, height } = position;
// const y = beforeHeight + height / 2;
// const y0 = beforeHeight;
// const y1 = beforeHeight + height;

// if (i > 0) {
// f.areaPoints.push({ x: x - areaPointPadding, y0, y1 });
// }
// f.areaPoints.push({ x, y0, y1 });
// if (i < f.data.length - 1) {
// f.areaPoints.push({ x: x + areaPointPadding, y0, y1 });
// }
// });
// });
// return filledData;
// }
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
firstStep[0][5]
Insert cell
{
let a = firstStep[0][5].values.sort((a, b) => b.value - a.value);

const previousValues = a.filter((d, restOfrank) =>restOfrank < 5);
const beforeValue = previousValues.reduce((t, v) => t + v.value, 0);
return [previousValues,beforeValue];
}
Insert cell
console.log(
secondStep.map((d) => {
let values = d.values.filter((v) => v.serieId == "JavaScript0");
return {
...d,
values
};
})
)
Insert cell
secondStep = {
const [slices, maxSum, maxValues] = firstStep;

const { align, spacing, xPadding } = form;
const xScale = d3
.scalePoint()
.domain(slices.map((slice) => slice.id))
.range([0, width]);

const heightScale = d3
.scaleLinear()
.domain([0, maxSum])
.range([0, height - maxValues * spacing]);
slices.forEach((slice) => {
slice.x = xScale(slice.id);
const sliceHeight =
heightScale(slice.total) + slice.values.length * spacing;

let offset = 0;
if (align === "middle") {
offset = (height - sliceHeight) / 2;
} else if (align === "end") {
offset = height - sliceHeight;
}

const heightSums = new Map();
slice.values
.sort((a, b) => b.value - a.value)
.forEach((value, _, all) => {
// console.log(value, all);
const previousValues = all.filter((v) => v.value > value.value);
const equalValues = previousValues.filter(
(v) => v.value === value.value
);
const equalValuesHeightSum = equalValues.reduce(
(t, v) => t + v.height,
0
);
const beforeValue =
previousValues.reduce((t, v) => t + v.value, 0) -
equalValuesHeightSum;

value.height = heightScale(value.value);
value.beforeHeight =
heightScale(beforeValue) +
offset +
spacing * (previousValues.length - equalValues.length + 0.5);
});
});
// console.log(slices);
return slices;
}
Insert cell
computeSeries = ({
data,
width,
height,
align,
spacing,
xPadding,
xName = "x"
}) => {
const xAccessor = (d, xName) => d[xName];
const slices = [];

let maxSum;
let maxValues;

data.forEach((serie) => {
serie.data.forEach((datum) => {
let slice = slices.find((s) => s.id === datum.x);
if (!slice) {
slice = {
id: xAccessor(datum, xName),
total: 0,
values: [],
x: 0
};
slices.push(slice);
}

const total = slice.total + datum.y;
slice.total = total;

slice.values.push({
serieId: serie.id,
value: datum.y,
position: 0,
height: 0,
beforeHeight: 0
});

if (maxSum === undefined || total > maxSum) {
maxSum = total;
}
if (maxValues === undefined || slice.values.length > maxValues) {
maxValues = slice.values.length;
}
});
});

const xScale = d3
.scalePoint()
.domain(slices.map((slice) => slice.id))
.range([0, width]);

const heightScale = d3
.scaleLinear()
.domain([0, maxSum])
.range([0, height - maxValues * spacing]);

slices.forEach((slice) => {
slice.x = xScale(slice.id);
const sliceHeight =
heightScale(slice.total) + slice.values.length * spacing;

let offset = 0;
if (align === "middle") {
offset = (height - sliceHeight) / 2;
} else if (align === "end") {
offset = height - sliceHeight;
}
slice.values
.sort((a, b) => b.value - a.value)
.forEach((value, _, all) => {
// console.log(value, all);
const previousValues = all.filter((v) => v.value < value.value);
const equalValues = previousValues.filter(
(v) => v.value === value.value
);
const equalValuesHeightSum = equalValues.reduce(
(t, v) => t + v.height,
0
);
const beforeValue =
previousValues.reduce((t, v) => t + v.value, 0) -
equalValuesHeightSum;

value.height = heightScale(value.value);
value.beforeHeight =
heightScale(beforeValue) +
offset +
spacing * (previousValues.length - equalValues.length + 0.5);
});
// slice.values
// .map((value, index) => ({ ...value, originalIndex: index }))
// .sort((a, b) => {
// if (b.value === a.value) {
// return a.originalIndex - b.originalIndex;
// }
// return b.value - a.value;
// })
// .forEach((value, position, all) => {
// // 不应该是previous value,而是 previous value里
// const previousValues = all.filter((_i, pos) => pos < position);
// const beforeValue = previousValues.reduce((t, v) => t + v.value, 0);

// value.position = position;
// value.height = heightScale(value.value);
// value.beforeHeight =
// heightScale(beforeValue) +
// offset +
// spacing * (previousValues.length + 0.5);
// });
});

const areaPointPadding = xScale.step() * Math.min(xPadding * 0.5, 0.5);

const series = data.map((serie) => {
const computedSerie = {
id: serie.id,
data: serie,
points: [],
areaPoints: []
};

serie.data.forEach((datum, i) => {
const slice = slices.find((s) => s.id === datum.x);
const position = slice.values.find((v) => v.serieId === serie.id);

const x = slice.x;
const { beforeHeight, height } = position;
const y = beforeHeight + height / 2;
const y0 = beforeHeight;
const y1 = beforeHeight + height;

computedSerie.points.push({
x,
y,
height,

data: { ...datum }
});
if (i > 0) {
computedSerie.areaPoints.push({ x: x - areaPointPadding, y0, y1 });
}
computedSerie.areaPoints.push({ x, y0, y1 });
if (i < serie.data.length - 1) {
computedSerie.areaPoints.push({ x: x + areaPointPadding, y0, y1 });
}
});

return computedSerie;
});

return {
series,
xScale,
heightScale
};
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more