我想添加一个工具提示,显示将鼠标悬停在绘图中的一行上时的确切值。当直线悬停时,如何在直线与轴接触的每个轴上添加工具提示?如有任何帮助,我们将不胜感激。示例:将鼠标悬停在car
行上时,应显示第一、第二、第三和第四个属性。测试和训练数据集也应该如此。
var dataSet = [{
"type": "car",
"dataset": "test",
"first": 0.65,
"second": 0.34,
"third": 0.55,
"fourth": 0.39
},
{
"type": "car",
"dataset": "train",
"first": 0.59,
"second": 0.33,
"third": 0.50,
"fourth": 0.40
},
{
"type": "bicycle",
"dataset": "test",
"first": 200,
"second": 230,
"third": 250,
"fourth": 300
},
{
"type": "bicycle",
"dataset": "train",
"first": 200,
"second": 280,
"third": 225,
"fourth": 278
},
{
"type": "boat",
"dataset": "test",
"first": 320,
"second": 324,
"third": 532,
"fourth": 321
},
{
"type": "boat",
"dataset": "train",
"first": 128,
"second": 179,
"third": 166,
"fourth": 234
},
{
"type": "airplane",
"dataset": "test",
"first": 1500,
"second": 2000,
"third": 2321,
"fourth": 1793
},
{
"type": "airplane",
"dataset": "train",
"first": 1438,
"second": 2933,
"third": 2203,
"fourth": 2000
}
];
var processedData = [];
dataSet.forEach(function(d) {
var match = processedData.find(function(p) { return p.type === d.type; });
if(!match) {
match = {
type: d.type,
};
processedData.push(match);
}
var values = [d.first, d.second, d.third, d.fourth];
if(d.dataset === "train") {
match.train = values;
} else {
match.test = values;
}
});
processedData.forEach(function(d) {
// Normalise the values in the arrays
const min = Math.min(d3.min(d.train), d3.min(d.test));
const max = Math.max(d3.max(d.train), d3.max(d.test));
d.trainNormalised = d.train.map(function(v) {
return (v - min) / (max - min);
});
d.testNormalised = d.test.map(function(v) {
return (v - min) / (max - min);
});
});
var margin = {
top: 5,
right: 50,
bottom: 5,
left: 70
},
width = 600 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom;
var categoryScale = d3.scale.ordinal()
.domain(processedData.map(function(d) { return d.type; }))
.rangePoints([0, height]);
var y = d3.scale.linear()
.domain([0, 1])
.range([height, 0]);
var x = d3.scale.ordinal()
.domain(d3.range(5))
.rangePoints([0, width]);
var line = d3.svg.line()
.defined(function(d) {
return !isNaN(d[1]);
});
// CREATE A COLOR SCALE
var color = d3.scale.ordinal()
.range(["#4683b8", "#79add2", "#a6c9de", "#cadbed", "#9d9bc4", "#bcbed9", "#dadaea", "#f6d2a8", "#f2b076", "#ef914e", "#d65e2a"])
var svg = d3.select("#parallel_coor")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.selectAll(".dimension.axis")
.data([categoryScale, y, y, y, y])
.enter()
.append("g")
.attr("class", "dimension axis")
.attr("transform", function(d, i) {
return "translate(" + x(i) + ")";
})
.each(function(d) {
const yAxis = d3.svg.axis()
.scale(d)
.ticks([])
.orient("left");
d3.select(this).call(yAxis);
});
function parallel(data) {
// Draw one line group per type (car, boat)
// Each line group consists of a train and a test line;
var lineGroup = svg.append("g")
.selectAll(".lineGroup")
.data(data)
.enter()
.append("g")
.attr("class", "lineGroup")
.each(function(d) {
if(d.train)
d3.select(this).append("path")
.datum([d, "train"]);
if(d.test)
d3.select(this).append("path")
.datum(function(d) { return [d, "test"]; });
})
lineGroup
.attr("stroke", function(d) {
var company = d.type.slice(0, d.type.indexOf(' '));
return color(company);
})
.selectAll("path")
.attr("class", function(d) { return d[1]; })
.attr("d", draw);
lineGroup
.on("mouseover", function(d) {
// show train when click others
d3.select(this).classed("active", true);
lineGroup
.filter(function(e) { return e.type !== d.type; })
.style('opacity', 0.2);
})
.on("mouseout", function(d) {
d3.select(this).classed("active", false);
lineGroup.style('opacity', null);
});
function draw(d) {
var data = d[0], type = d[1];
var points = data[type + "Normalised"].map(function(v, i) {
return [x(i + 1), y(v)];
});
points.unshift([x(0), categoryScale(data.type)]);
return line(points);
}
}
parallel(processedData);
svg {
font: 12px sans-serif;
}
.lineGroup path {
fill: none;
}
.lineGroup.active .train {
visibility: visible;
}
.train {
visibility: hidden;
stroke-dasharray: 5 5;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div id="parallel_coor"></div>
您可以执行以下操作。每当你把鼠标悬停在一条线上时,为每个数据点绘制矩形,添加一些文本和样式,就完成了。我给了工具提示不同的类,这样它们就可以根据它们是用于训练还是测试数据来进行样式设置
var dataSet = [{
"type": "car",
"dataset": "test",
"first": 0.65,
"second": 0.34,
"third": 0.55,
"fourth": 0.39
},
{
"type": "car",
"dataset": "train",
"first": 0.59,
"second": 0.33,
"third": 0.50,
"fourth": 0.40
},
{
"type": "bicycle",
"dataset": "test",
"first": 200,
"second": 230,
"third": 250,
"fourth": 300
},
{
"type": "bicycle",
"dataset": "train",
"first": 200,
"second": 280,
"third": 225,
"fourth": 278
},
{
"type": "boat",
"dataset": "test",
"first": 320,
"second": 324,
"third": 532,
"fourth": 321
},
{
"type": "boat",
"dataset": "train",
"first": 128,
"second": 179,
"third": 166,
"fourth": 234
},
{
"type": "airplane",
"dataset": "test",
"first": 1500,
"second": 2000,
"third": 2321,
"fourth": 1793
},
{
"type": "airplane",
"dataset": "train",
"first": 1438,
"second": 2933,
"third": 2203,
"fourth": 2000
}
];
var processedData = [];
dataSet.forEach(function(d) {
var match = processedData.find(function(p) { return p.type === d.type; });
if(!match) {
match = {
type: d.type,
};
processedData.push(match);
}
var values = [d.first, d.second, d.third, d.fourth];
if(d.dataset === "train") {
match.train = values;
} else {
match.test = values;
}
});
processedData.forEach(function(d) {
// Normalise the values in the arrays
const min = Math.min(d3.min(d.train), d3.min(d.test));
const max = Math.max(d3.max(d.train), d3.max(d.test));
d.trainNormalised = d.train.map(function(v) {
return (v - min) / (max - min);
});
d.testNormalised = d.test.map(function(v) {
return (v - min) / (max - min);
});
});
var margin = {
top: 5,
right: 50,
bottom: 5,
left: 70
},
width = 600 - margin.left - margin.right,
height = 280 - margin.top - margin.bottom;
var categoryScale = d3.scale.ordinal()
.domain(processedData.map(function(d) { return d.type; }))
.rangePoints([0, height]);
var y = d3.scale.linear()
.domain([0, 1])
.range([height, 0]);
var x = d3.scale.ordinal()
.domain(d3.range(5))
.rangePoints([0, width]);
var line = d3.svg.line()
.defined(function(d) {
return !isNaN(d[1]);
});
// CREATE A COLOR SCALE
var color = d3.scale.ordinal()
.range(["#4683b8", "#79add2", "#a6c9de", "#cadbed", "#9d9bc4", "#bcbed9", "#dadaea", "#f6d2a8", "#f2b076", "#ef914e", "#d65e2a"])
var svg = d3.select("#parallel_coor")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.selectAll(".dimension.axis")
.data([categoryScale, y, y, y, y])
.enter()
.append("g")
.attr("class", "dimension axis")
.attr("transform", function(d, i) {
return "translate(" + x(i) + ")";
})
.each(function(d) {
const yAxis = d3.svg.axis()
.scale(d)
.ticks([])
.orient("left");
d3.select(this).call(yAxis);
});
function parallel(data) {
// Draw one line group per type (car, boat)
// Each line group consists of a train and a test line;
var lineGroup = svg.append("g")
.selectAll(".lineGroup")
.data(data)
.enter()
.append("g")
.attr("class", "lineGroup")
.each(function(d) {
if(d.train)
d3.select(this).append("path")
.datum([d, "train"]);
if(d.test)
d3.select(this).append("path")
.datum(function(d) { return [d, "test"]; });
})
lineGroup
.attr("stroke", function(d) {
var company = d.type.slice(0, d.type.indexOf(' '));
return color(company);
})
.selectAll("path")
.attr("class", function(d) { return d[1]; })
.attr("d", draw);
lineGroup
.on("mouseover", function(d) {
// show train when click others
d3.select(this).classed("active", true);
lineGroup
.filter(function(e) { return e.type !== d.type; })
.style('opacity', 0.2);
// add tooltip elements for all values
drawTips(d, "train");
drawTips(d, "test");
})
.on("mouseout", function(d) {
d3.select(this).classed("active", false);
lineGroup.style('opacity', null);
svg.selectAll(".tooltip").remove();
});
function draw(d) {
var data = d[0], type = d[1];
var points = data[type + "Normalised"].map(function(v, i) {
return [x(i + 1), y(v)];
});
points.unshift([x(0), categoryScale(data.type)]);
return line(points);
}
function drawTips(data, type) {
const newTips = svg.selectAll(".tooltip.tooltip-" + type)
.data(data[type])
.enter()
.append("g")
.attr("class", "tooltip tooltip-" + type)
.attr("transform", function(d, i) {
const v = data[type + "Normalised"][i];
return "translate(" + [x(i +1), y(v)] + ")";
});
newTips.append("rect")
.attr("width", 40)
.attr("height", 16);
newTips.append("text")
.attr("dx", 4)
.attr("dy", 8)
.text(function(d) { return d; });
}
}
parallel(processedData);
svg {
font: 12px sans-serif;
}
.lineGroup path {
fill: none;
}
.lineGroup.active .train {
visibility: visible;
}
.train {
visibility: hidden;
stroke-dasharray: 5 5;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tooltip-test rect {
fill: #444;
}
.tooltip-train rect {
fill: #888;
}
.tooltip text {
dominant-baseline: middle;
fill: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<div id="parallel_coor"></div>