Public
Edited
May 8, 2023
Insert cell
Insert cell
main = html`<div style="height: 100%; min-height:300px;width:100%;" />`
Insert cell
dimensions = ["province"]
Insert cell
metrics = ['thisyear', 'lastyear', 'ytoy']
Insert cell
data = [
{
province: "广东省",
thisyear: 13429.1234,
// lastyear: 12329.1234,
// yy: 1100,
// ytoy: 91.8087720604662
},
{
province: "湖南省",
thisyear: 15248,
lastyear: 10109,
yy: 1000,
ytoy: 66.2972193074502
},
{
province: "海南省",
thisyear: 11919,
lastyear: 11981,
yy: 1001,
ytoy: 100.520177867271
},
{
province: "湖北省",
thisyear: 15462,
lastyear: 19547,
yy: 1011,
ytoy: 126.419609364895
},
{
province: "福建省",
thisyear: 14791,
lastyear: 12190,
yy: 1001,
ytoy: 82.4149820836996
},
{
province: "云南省",
thisyear: 13973,
lastyear: 19525,
yy: 1111,
ytoy: 139.733772275102
},
{ province: "浙江省", thisyear: 0, lastyear: 18871, yy: 1000, ytoy: 0 },
{
province: "江苏省",
thisyear: 11773,
lastyear: 12773,
yy: -1000,
ytoy: -100
},
{ province: "上海市", thisyear: 16773, lastyear: 0, yy: -16773, ytoy: "--" }
]
Insert cell
finalData = {
const lastMetric = _.last(metrics);
const metricsSet = new Set(metrics);
const fillNa = _.map(data, (d) =>
_.mapValues(d, (v, k) => {
if (!metricsSet.has(k)) return v;
return Number.isNaN(+v) ? 0 : +v;
})
);
const dropIfCompleteNa = fillNa.filter(
(d) => !_.every(metrics, (m) => d[m] === 0)
);
const sorted = _.orderBy(
dropIfCompleteNa,
[lastMetric, metrics[0]],
["desc", "desc"]
);
return sorted;
}
Insert cell
translationDict = ({
ytoy: "比基期(%)"
})
Insert cell
metricsFormatDict = ({
[metrics[0]]: (x) => (x ? _.round(x, 2) : x)
})
Insert cell
echartsLikeTooltipFormatter = (params) => {
let arr = _(params)
.filter((p) => p.value !== undefined && isFinite(p.value))
.take(15)
.map((p, pi) => {
let seriesName = p.seriesName;
let seriesFormatted = seriesName;
const metricIdx = p.seriesIndex;
const metric = metrics[metricIdx];
const metricFormatterFn = _.includes(translationDict[metric], "%")
? d3.format(".2~%")
: metricsFormatDict[metric] || ((v) => _.round(v * 100, 2));

//let val = p.value
let val = (
_.isFunction(metricFormatterFn) ? metricFormatterFn : _.identity
)(p.value);
//let valHtml = `<span style='float:right;margin-left:20px;font-size:14px;color:#666;font-weight:900'>${val}</span>`
let valHtml = `<span style='float:right;margin-left:10px;'>${val}</span>`;
return `<span style='display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:${p.color}' ></span>${seriesFormatted} : ${valHtml}`;
})
.value();

let xVal = params[0].name;

return `${xVal}<br />${arr.join("<br />")}`;
}
Insert cell
option = {
let lastMetric = _.last(metrics);
// let dataWithout = _.filter(data, (d) => !isNaN(d[lastMetric])) //排除非数字,NaN:not a number
// let sorted = _.orderBy(dataWithout, lastMetric,'desc')
let sorted = finalData;

return {
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
crossStyle: {
color: "#999"
}
},
formatter: echartsLikeTooltipFormatter
},
legend: { type: "scroll" },
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true
},
xAxis: [
{
type: "category",
data: (sorted || []).map((d) =>
dimensions.map((dim) => _.trim(d[dim])).join("-")
),
axisLabel: { rotate: 30 }
}
],
yAxis: [
{
type: "value",
boundaryGap: [0, 0.01]
},
{
//name: '(%)',
type: "value",
boundaryGap: [0, 0.01],
splitLine: { show: false },
scale: true
}
],
color: [
"#5470c6",
"#91cc75",
"#fac858",
"#ee6666",
"#73c0de",
"#3ba272",
"#fc8452",
"#9a60b4",
"#ea7ccc"
],
series: [
..._.dropRight(metrics, 1).map((metric) => {
return {
id: metric,
name: translationDict[metric] || metric,
type: "bar",
barMaxWidth: "30%",
data: (sorted || []).map((d) => d[metric])
};
}),
{
id: lastMetric,
name: translationDict[lastMetric] || lastMetric,
type: "line",
symbol: "circle",
yAxisIndex: 1,
data: (sorted || []).map((d) => d[lastMetric])
}
]
};
}
Insert cell
chartInst = echarts.init(main)
Insert cell
chartInst.setOption(option, { notMerge: true, lazyUpdate: true })
Insert cell
onEvents = ({})
Insert cell
function bindEvents(instance, events) {
function _bindEvent(eventName, func) {
if (eventName && func) {
instance.on(eventName, param => {
func(param, instance)
})
}
}

for (const eventName in events) {
_bindEvent(eventName, events[eventName])
}
}
Insert cell
bindEvents(chartInst, onEvents || {}); //调用bindEvents
Insert cell
(width, chartInst.resize()) // 自适应宽度
Insert cell
echarts = window.echarts || require("https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js")
Insert cell
d3 = window.d3 || require("d3@5")
Insert cell
_ = window._ || require("lodash")
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