如何将 Observable Notebook 示例翻译成 javascript?
How to translate an Observable Notebook example into javascript?
我正在尝试将 this observable notebook example 翻译或转换为纯 javascript 和 d3,以便我在网站中使用它。
我试着跟随 取得了类似的成就。
但它对我不起作用,我收到一条错误消息“Unexpected value NaN parsing cy attribute”。
这是我到目前为止所做的:
<!DOCTYPE HTML>
<meta charset="UTF-8">
<html>
<body>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.2/d3.min.js"></script>
<script src="https://unpkg.com/simple-statistics@7.7.0/dist/simple-statistics.min.js"></script>
</head>
<svg width="300" height="300" />
<script>
const width = 600;
const height = 600;
const margin = ({top: 20, right: 40, bottom: 30, left: 40});
const data = [608.781, 569.67, 689.556, 747.541, 618.134, 612.182, 680.203, 607.766, 726.232, 605.38, 518.655, 589.226, 740.447, 588.375, 666.83, 531.384, 710.272, 633.417, 751.669, 619.06];
const erfinv = () => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
return x => {
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
}
const qnorm = p => Math.SQRT2 * erfinv(2 * p - 1);
const qy = Float64Array.from(data).sort(d3.ascending);
const n = qy.length;
const a = n <= 10 ? 5 / 8 : 0.5;
const z = i => qnorm((i + a) / (n + 1 - 2 * a));
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom + 6})`)
.call(d3.axisBottom(x.copy().interpolate(d3.interpolateRound)).ticks(null, "+f"))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y1", -height))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -3)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.text("z"));
const x = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left, width - margin.right]);
const regression = x.domain().map(ss.linearRegressionLine(ss.linearRegression(Array.from(qy, (d, i) => ([z(i), d])))));
const y = d3.scaleLinear()
.domain(regression).nice()
.range([height - margin.bottom, margin.top]);
const yAxis = g => g
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y.copy().interpolate(d3.interpolateRound)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("x1", width))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.title));
const chart = () => {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("max-width", `${width}px`);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("line")
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.3)
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("y1", y(regression[0]))
.attr("y2", y(regression[1]));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(d3.range(n))
.join("circle")
.attr("cx", i => x(z(i)))
.attr("cy", i => y(qy[i]))
.attr("r", 3);
return svg.node();
}
chart();
</script>
</body>
</html>
非常感谢任何帮助,谢谢。
这是一个工作示例:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.2/d3.min.js"></script>
<script src="https://unpkg.com/simple-statistics@7.7.0/dist/simple-statistics.min.js"></script>
</head>
<body>
<svg></svg>
<script>
const width = 600;
const height = 600;
const margin = ({ top: 20, right: 40, bottom: 30, left: 40 });
const data = [
608.781, 569.67, 689.556, 747.541,
618.134, 612.182, 680.203, 607.766,
726.232, 605.38, 518.655, 589.226,
740.447, 588.375, 666.83, 531.384,
710.272, 633.417, 751.669, 619.06
];
const erfinv = x => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
const qnorm = p => Math.SQRT2 * erfinv(2 * p - 1);
const qy = Float64Array.from(data).sort(d3.ascending);
const n = qy.length;
const a = n <= 10 ? 5 / 8 : 0.5;
const z = i => qnorm((i + a) / (n + 1 - 2 * a));
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom + 6})`)
.call(d3.axisBottom(x.copy().interpolate(d3.interpolateRound)).ticks(null, "+f"))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y1", -height))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -3)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.text("z"));
const x = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left, width - margin.right]);
const regression = x.domain()
.map(ss.linearRegressionLine(ss.linearRegression(Array.from(qy, (d, i) => ([z(i), d])))));
const y = d3.scaleLinear()
.domain(regression).nice()
.range([height - margin.bottom, margin.top]);
const yAxis = g => g
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y.copy().interpolate(d3.interpolateRound)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("x1", width))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Stength"));
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("line")
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.3)
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("y1", y(regression[0]))
.attr("y2", y(regression[1]));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(d3.range(n))
.join("circle")
.attr("cx", i => x(z(i)))
.attr("cy", i => y(qy[i]))
.attr("r", 3);
</script>
</body>
</html>
有几点需要注意。首先,erfinv
函数。你有:
const erfinv = () => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
return x => {
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
}
这是一个正在返回另一个函数的函数。所以当你像 erfinv(2 * p - 1)
这样调用它时,它返回一个函数,而不是一个数字。你可以这样写:
const erfinv = x => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
其次,您的 HTML 代码已经在 DOM 中包含了 <svg>
元素。因此,我们可以使用 d3.select('svg')
select 而不是使用 d3.create('svg')
。此外,return svg.node();
仅在 Observable 上需要。
我正在尝试将 this observable notebook example 翻译或转换为纯 javascript 和 d3,以便我在网站中使用它。
我试着跟随
这是我到目前为止所做的:
<!DOCTYPE HTML>
<meta charset="UTF-8">
<html>
<body>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.2/d3.min.js"></script>
<script src="https://unpkg.com/simple-statistics@7.7.0/dist/simple-statistics.min.js"></script>
</head>
<svg width="300" height="300" />
<script>
const width = 600;
const height = 600;
const margin = ({top: 20, right: 40, bottom: 30, left: 40});
const data = [608.781, 569.67, 689.556, 747.541, 618.134, 612.182, 680.203, 607.766, 726.232, 605.38, 518.655, 589.226, 740.447, 588.375, 666.83, 531.384, 710.272, 633.417, 751.669, 619.06];
const erfinv = () => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
return x => {
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
}
const qnorm = p => Math.SQRT2 * erfinv(2 * p - 1);
const qy = Float64Array.from(data).sort(d3.ascending);
const n = qy.length;
const a = n <= 10 ? 5 / 8 : 0.5;
const z = i => qnorm((i + a) / (n + 1 - 2 * a));
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom + 6})`)
.call(d3.axisBottom(x.copy().interpolate(d3.interpolateRound)).ticks(null, "+f"))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y1", -height))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -3)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.text("z"));
const x = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left, width - margin.right]);
const regression = x.domain().map(ss.linearRegressionLine(ss.linearRegression(Array.from(qy, (d, i) => ([z(i), d])))));
const y = d3.scaleLinear()
.domain(regression).nice()
.range([height - margin.bottom, margin.top]);
const yAxis = g => g
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y.copy().interpolate(d3.interpolateRound)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("x1", width))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.title));
const chart = () => {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("max-width", `${width}px`);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("line")
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.3)
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("y1", y(regression[0]))
.attr("y2", y(regression[1]));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(d3.range(n))
.join("circle")
.attr("cx", i => x(z(i)))
.attr("cy", i => y(qy[i]))
.attr("r", 3);
return svg.node();
}
chart();
</script>
</body>
</html>
非常感谢任何帮助,谢谢。
这是一个工作示例:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.2/d3.min.js"></script>
<script src="https://unpkg.com/simple-statistics@7.7.0/dist/simple-statistics.min.js"></script>
</head>
<body>
<svg></svg>
<script>
const width = 600;
const height = 600;
const margin = ({ top: 20, right: 40, bottom: 30, left: 40 });
const data = [
608.781, 569.67, 689.556, 747.541,
618.134, 612.182, 680.203, 607.766,
726.232, 605.38, 518.655, 589.226,
740.447, 588.375, 666.83, 531.384,
710.272, 633.417, 751.669, 619.06
];
const erfinv = x => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
const qnorm = p => Math.SQRT2 * erfinv(2 * p - 1);
const qy = Float64Array.from(data).sort(d3.ascending);
const n = qy.length;
const a = n <= 10 ? 5 / 8 : 0.5;
const z = i => qnorm((i + a) / (n + 1 - 2 * a));
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom + 6})`)
.call(d3.axisBottom(x.copy().interpolate(d3.interpolateRound)).ticks(null, "+f"))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("y1", -height))
.call(g => g.append("text")
.attr("x", width - margin.right)
.attr("y", -3)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.text("z"));
const x = d3.scaleLinear()
.domain([-3, 3])
.range([margin.left, width - margin.right]);
const regression = x.domain()
.map(ss.linearRegressionLine(ss.linearRegression(Array.from(qy, (d, i) => ([z(i), d])))));
const y = d3.scaleLinear()
.domain(regression).nice()
.range([height - margin.bottom, margin.top]);
const yAxis = g => g
.attr("transform", `translate(${margin.left - 6},0)`)
.call(d3.axisLeft(y.copy().interpolate(d3.interpolateRound)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("stroke-opacity", 0.1)
.attr("x1", width))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Stength"));
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("line")
.attr("stroke", "currentColor")
.attr("stroke-opacity", 0.3)
.attr("x1", x.range()[0])
.attr("x2", x.range()[1])
.attr("y1", y(regression[0]))
.attr("y2", y(regression[1]));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(d3.range(n))
.join("circle")
.attr("cx", i => x(z(i)))
.attr("cy", i => y(qy[i]))
.attr("r", 3);
</script>
</body>
</html>
有几点需要注意。首先,erfinv
函数。你有:
const erfinv = () => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
return x => {
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
}
这是一个正在返回另一个函数的函数。所以当你像 erfinv(2 * p - 1)
这样调用它时,它返回一个函数,而不是一个数字。你可以这样写:
const erfinv = x => {
const a = 8 * (Math.PI - 3) / (3 * Math.PI * (4 - Math.PI));
const b = Math.log(1 - x * x);
const c = b / 2 + (2 / (Math.PI * a));
return Math.sign(x) * Math.sqrt(Math.sqrt((c * c) - b / a) - c);
};
其次,您的 HTML 代码已经在 DOM 中包含了 <svg>
元素。因此,我们可以使用 d3.select('svg')
select 而不是使用 d3.create('svg')
。此外,return svg.node();
仅在 Observable 上需要。