为本人作业内容。题目如下:

解题思路:notepad++编写html文件,使用开源可视化工具D3绘制三种鸢尾花的五种表示图:平行坐标图、散点图、气泡图、特征雷达图、小提琴图。
1. notepad++如何编写html文件?
html文件基本代码框架如下:
javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试页面</title>
</head>
<body>
<h1>Hello 网页测试成功</h1>
</body>
</html>
这是一个测试程序。打开notepad++,新建文件,复制该代码,保存时文件名添加后缀".html",类型选择全部类型(".")。在文件夹中双击打开保存的html文件,可以看到浏览器出现下图,测试成功。

2. 能否成功读取CSV文件?
这是一段测试能否读取CSV文件的代码。CSV表无表头,共150行5列,第一列是sepal.length,第二列是sepal.width,第三列是petal.length,第四列是petal.width,第五列是variety。
java
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSV 读取测试(无表头)</title>
<style>
body { font-family: Arial; margin: 30px; }
#result { margin-top: 20px; padding: 10px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h2>CSV 读取测试(150行 无表头)</h2>
<input type="file" id="csvFile" accept=".csv" />
<div id="result"></div>
<script>
document.getElementById('csvFile').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
const csvData = event.target.result;
// 按行拆分,过滤空行
const lines = csvData.split(/\r\n|\n/).filter(line => line.trim() !== '');
const resultDiv = document.getElementById('result');
resultDiv.innerHTML = `
<h3>✅ 读取成功!</h3>
<p>📊 检测到行数:${lines.length}</p>
<p>📝 每行列数:5列</p>
<hr>
<h4>前 10 行数据预览:</h4>
`;
// 显示前10行
for (let i = 0; i < Math.min(10, lines.length); i++) {
resultDiv.innerHTML += `<pre>第 ${i+1} 行:${lines[i]}</pre>`;
}
};
reader.readAsText(file, 'utf-8');
});
</script>
</body>
</html>
实现效果如何?双击保存的html文件,出现如下选择文件页面:

选择csv文件,可以成功读取,且正确显示前10行:

说明编写的html文件可以在浏览器上正常读取csv文件。
如果无法读取,在浏览器页面按F12键,打开控制器页面观察为何报错,一般会出现:

这是因为浏览器对本地 file:// 协议的安全限制,但一般不影响CSV读取,可以不用管。
如果出现问题,可以尝试:
1.将html文件和csv文件保存到桌面
2.打开终端,进入桌面:cd Desktop
3.运行:python -m http.server 8000
4.浏览器访问:http://localhost:8000/你保存的.html
3. 数据可视化代码结构
在html结构中,首先标明title,引入D3网址,设置需要的格式(直接告诉AI你要图例标题之类的它会帮你生成),添加文件选择按钮,输入大标题和子标题,明确三种配色(三种花)和四个特征值(题目中的四个特征),依次攥写平行坐标图、散点图、气泡图、特征雷达图、小提琴图的函数内容。读取CSV文件、解析CSV文件,最后调用绘图函数。
完整代码如下:
java
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>鸢尾花数据集可视化作业</title>
<!-- 引入 D3 -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", Arial, sans-serif;
}
body {
background: #f7f8fa;
padding: 30px;
}
.container {
max-width: 1100px;
margin: 0 auto;
}
.main-title {
text-align: center;
font-size: 26px;
color: #2d3748;
margin-bottom: 10px;
}
.sub-title {
text-align: center;
color: #718096;
margin-bottom: 30px;
font-size: 14px;
}
.legend {
display: flex;
justify-content: center;
gap: 25px;
margin-bottom: 40px;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #4a5568;
}
.legend-color {
width: 18px;
height: 18px;
border-radius: 4px;
}
.card {
background: white;
border-radius: 12px;
padding: 25px;
margin-bottom: 30px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
}
.card-title {
font-size: 18px;
color: #2d3748;
margin-bottom: 20px;
font-weight: 600;
}
</style>
</head>
<body>
<!-- 文件选择按钮 -->
<input type="file" id="csvFile" accept=".csv">
<div class="container">
<h1 class="main-title">鸢尾花数据集 5 种可视化展示</h1>
<p class="sub-title">Iris Dataset Visualization</p>
<div class="legend">
<div class="legend-item"><div class="legend-color" style="background:#9400d3"></div>Setosa</div>
<div class="legend-item"><div class="legend-color" style="background:#40e0d0"></div>Versicolor</div>
<div class="legend-item"><div class="legend-color" style="background:#ffff00"></div>Virginica</div>
</div>
<div class="card">
<div class="card-title">1. 平行坐标图</div>
<div id="parallel"></div>
</div>
<div class="card">
<div class="card-title">2. 散点图(花瓣长度 vs 花瓣宽度)</div>
<div id="scatter"></div>
</div>
<div class="card">
<div class="card-title">3. 气泡图(花萼尺寸 + 花瓣大小)</div>
<div id="bubble"></div>
</div>
<div class="card">
<div class="card-title">4. 特征雷达图(三类平均值)</div>
<div id="radar"></div>
</div>
<div class="card">
<div class="card-title">5. 花瓣长度小提琴图</div>
<div id="violin"></div>
</div>
</div>
<script>
// 配色
const color = d3.scaleOrdinal()
.domain(["Setosa", "Versicolor", "Virginica"])
.range(["#9400d3", "#40e0d0", "#ffff00"]);
const features = ["sepalLength", "sepalWidth", "petalLength", "petalWidth"];
// ====================== 1. 平行坐标图 ======================
function drawParallel(data) {
const w = 900, h = 420;
const svg = d3.select("#parallel").append("svg").attr("width", w).attr("height", h);
const x = d3.scalePoint().domain(features).range([50, w - 50]);
const y = {};
features.forEach(f => {
y[f] = d3.scaleLinear().domain(d3.extent(data, d => d[f])).range([h - 40, 20]);
});
features.forEach(f => {
svg.append("g").attr("transform", `translate(${x(f)},0)`).call(d3.axisLeft(y[f]));
svg.append("text").attr("x", x(f)).attr("y", 15).style("text-anchor", "middle").text(f);
});
svg.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", d => d3.line()(features.map(f => [x(f), y[f](d[f])])))
.style("fill", "none")
.style("stroke", d => color(d.variety))
.style("opacity", 0.5);
};
// ====================== 2. 散点图 ======================
function drawScatter(data) {
const w = 700, h = 400;
const svg = d3.select("#scatter").append("svg").attr("width", w).attr("height", h);
const x = d3.scaleLinear().domain(d3.extent(data, d => d.petalLength)).range([50, w - 30]);
const y = d3.scaleLinear().domain(d3.extent(data, d => d.petalWidth)).range([h - 40, 20]);
svg.append("g").attr("transform", `translate(0,${h-40})`).call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(50,0)`).call(d3.axisLeft(y));
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", d => x(d.petalLength))
.attr("cy", d => y(d.petalWidth))
.attr("r", 4.5)
.style("fill", d => color(d.variety))
.style("opacity", 0.8);
};
// ====================== 3. 气泡图 ======================
function drawBubble(data) {
const w = 700, h = 400;
const svg = d3.select("#bubble").append("svg").attr("width", w).attr("height", h);
const x = d3.scaleLinear().domain(d3.extent(data, d => d.sepalLength)).range([50, w - 30]);
const y = d3.scaleLinear().domain(d3.extent(data, d => d.sepalWidth)).range([h - 40, 20]);
const r = d3.scaleSqrt().domain(d3.extent(data, d => d.petalLength)).range([3, 13]);
svg.append("g").attr("transform", `translate(0,${h-40})`).call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(50,0)`).call(d3.axisLeft(y));
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", d => x(d.sepalLength))
.attr("cy", d => y(d.sepalWidth))
.attr("r", d => r(d.petalLength))
.style("fill", d => color(d.variety))
.style("opacity", 0.7)
.style("stroke", "#fff");
};
// ====================== 4. 雷达图 ======================
function drawRadar(data) {
const w = 450, h = 450, cx = w / 2, cy = h / 2, rad = 150;
const svg = d3.select("#radar").append("svg").attr("width", w).attr("height", h).append("g").attr("transform", `translate(${cx},${cy})`);
const groups = d3.groups(data, d => d.variety);
const avg = groups.map(([k, vs]) => {
let o = { variety: k };
features.forEach(f => o[f] = d3.mean(vs, x => x[f]));
return o;
});
const angle = d3.scalePoint().domain(features).range([0, Math.PI * 2]);
features.forEach(f => {
const a = angle(f);
svg.append("line").attr("x1", 0).attr("y1", 0).attr("x2", Math.cos(a) * rad).attr("y2", Math.sin(a) * rad).style("stroke", "#ddd");
svg.append("text").attr("x", Math.cos(a) * (rad + 20)).attr("y", Math.sin(a) * (rad + 20)).style("text-anchor", "middle").text(f);
});
avg.forEach(g => {
const pts = features.map(f => {
const a = angle(f);
const r = (g[f] / 7.5) * rad;
return [Math.cos(a) * r, Math.sin(a) * r];
});
svg.append("polygon")
.attr("points", pts.join(","))
.style("fill", color(g.variety))
.style("fill-opacity", 0.3)
.style("stroke", color(g.variety))
.style("stroke-width", 2);
});
};
// ====================== 5. 小提琴图 ======================
function drawViolin(data) {
const w = 650, h = 400;
const svg = d3.select("#violin").append("svg").attr("width", w).attr("height", h);
const cats = ["Setosa", "Versicolor", "Virginica"];
const x = d3.scaleBand().domain(cats).range([60, w - 60]).padding(0.3);
const y = d3.scaleLinear().domain(d3.extent(data, d => d.petalLength)).range([h - 40, 20]);
svg.append("g").attr("transform", `translate(0,${h-40})`).call(d3.axisBottom(x));
svg.append("g").attr("transform", `translate(60,0)`).call(d3.axisLeft(y));
const kde = (arr, v) => d3.mean(arr, x => Math.exp(-((x - v) ** 2) / 0.3));
cats.forEach(c => {
const arr = data.filter(d => d.variety === c).map(d => d.petalLength);
const cx = x(c) + x.bandwidth() / 2;
const ys = d3.range(y.domain()[0], y.domain()[1], 0.1);
const area = d3.area()
.y(d => y(d))
.x0(d => cx - kde(arr, d) * 70)
.x1(d => cx + kde(arr, d) * 70)
.curve(d3.curveBasis);
svg.append("path").datum(ys).attr("d", area).style("fill", color(c)).style("opacity", 0.6);
});
};
//读取csv文件
document.getElementById('csvFile').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
const text = event.target.result;
//解析CSV文件
const data = d3.csvParseRows(text, d => {
return {
sepalLength: +d[0],
sepalWidth: +d[1],
petalLength: +d[2],
petalWidth: +d[3],
variety: d[4]
};
});
// 画图
console.log("解析完成,数据行数:", data.length); // 调试用
drawParallel(data);
drawScatter(data);
drawBubble(data);
drawRadar(data);
drawViolin(data);
}
reader.readAsText(file);
});
</script>
</body>
</html>
4. 修改代码时容易犯的错
首先,要分清楚自执行函数和普通函数:
java
// 自执行函数
(function drawParallel() { ... })();
// 普通函数
function drawParallel(data) { ... }
由于本人一开始想直接把CSV中的数据内置在代码里,故使用自执行函数。在开头设置好数据后,直接调用可以生成图像。
但后续尝试读取CSV内容生成图像会报错,需要将五个绘图函数改成普通函数。同时,代码顺序应该是:绘图函数定义------读取CSV------解析CSV------调用绘图函数,顺序乱会画不出来。
其次,函数参数要正确:
读取CSV中特征值是d.feature形式,但特征值定义和绘图函数定义部分却是d["feature"]形式,生成不出来。
5. 结果




6. 心得
很好的可视化题目,使我的脑仁旋转。