簡體   English   中英

如何規范化 d3 中的數據並繪制與其他線范圍無關的軸中的線

[英]How to normalize data in d3 and plot the lines in the axis independent of other line ranges

我有一個在 d3.js 中可視化的平行坐標圖,我的問題在於數據,“汽車”“自行車”“船”和“飛機”的不同線具有不同的數字范圍,因此數字低的線可以'不會被看到,而是被表示為一條平線。 例如,這出現在“汽車”線中,因為飛機數據要大得多。

我似乎無法弄清楚如何規范化數據,以便當“類型”是“汽車”時從 0 規范化到 1,而當類型是“自行車”或“船”時用 0 規范化到最大值。 但是在獨立於其他軸的軸中顯示這些數據。 如您所見,軸沒有范圍標簽。 相反,我計划在懸停在一條線上時顯示確切的數據。

這是 d3 代碼:

 var dataSet = [{ "type": "car", "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", "first": 200, "second": 230, "third": 250, "fourth": 300 }, { "type": "bicycle", "dataset": "train", "first": 200, "second": 280, "third": 225, "fourth": 278 }, { "type": "boat", "first": 320, "second": 324, "third": 532, "fourth": 321 }, { "type": "boat", "dataset": "train", "first": 128, "second": 179, "third": 166, "fourth": 234 }, { "type": "airplane", "first": 1500, "second": 2000, "third": 2321, "fourth": 1793 }, { "type": "airplane", "dataset": "train", "first": 1438, "second": 2933, "third": 2203, "fourth": 2000 } ] var margin = { top: 5, right: 50, bottom: 5, left: 70 }, width = 600 - margin.left - margin.right, height = 280 - margin.top - margin.bottom; var dimensions = [{ name: "type", scale: d3.scale.ordinal().rangePoints([0, height]), type: "string" }, { name: "first", scale: d3.scale.linear().range([height, 0]), type: "number" }, { name: "second", scale: d3.scale.linear().range([height, 0]), type: "number" }, { name: "third", scale: d3.scale.linear().range([height, 0]), type: "number" }, { name: "fourth", scale: d3.scale.linear().range([height, 0]), type: "number" } ]; var maxRange = d3.max(dataSet, function(d) { return Math.max(d.first, d.second, d.third, d.fourth); }); var x = d3.scale.ordinal() .domain(dimensions.map(function(d) { return d.name; })) .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 yAxis = d3.svg.axis() .orient("left"); 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 + ")"); var dimension = svg.selectAll(".dimension") .data(dimensions) .enter().append("g") .attr("class", "dimension") .attr("transform", function(d) { return "translate(" + x(d.name) + ")"; }); function parallel(dataSet) { dimensions.forEach(function(dimension) { dimension.scale.domain(dimension.type === "number" ? ([0, maxRange]) : dataSet.map(function(d) { return d[dimension.name]; })); }); svg.append("g") .attr("class", "background coorPath") .selectAll("path") .data(dataSet) .enter().append("path") .attr("d", draw); // USE THE COLOR SCALE TO SET THE STROKE BASED ON THE DATA foreground = svg.append("g") .attr("class", "foreground coorPath") .selectAll("path") .data(dataSet) .enter() .append("path") .attr("d", draw) .style("stroke", function(d) { var company = d.type.slice(0, d.type.indexOf(' ')); return color(company); }) dimension.append("g") .attr("class", "axis") .each(function(d) { d3.select(this).call(yAxis.scale(d.scale)); }) // remove axis numbers svg.selectAll(".tick") .each(function(d) { if (typeof d == 'number') { this.remove(); } }); var ordinal_labels = svg.selectAll(".axis text") .on("mouseover", mouseover) .on("mouseout", mouseout); var projection = svg.selectAll(".background path,.foreground path") .on("mouseover", mouseover) .on("mouseout", mouseout); // d3.selectAll("[dataset=train]").attr("visibility", "hidden"); let trainline = d3.selectAll(".coorPath path").filter(function(d) { return d.dataset == "train"; }) .attr("visibility", "hidden"); // On Click, we want to add data to the array and chart let lines = svg.selectAll(".coorPath path").on("mouseover", function(d) { // show train when click others trainline.attr("visibility", "visible") trainline.style("stroke-dasharray", ("5, 5")) lines.style('opacity', function(e) { return e.type === d.type ? 1 : 0.2; }); }); // On Click, we want to add data to the array and chart lines.on("mouseout", function(d) { // show train when click others lines.style('opacity', null); trainline.attr("visibility", "hidden") }); // making parallel coordinates plot interactive to highlight lines function mouseover(d) { svg.classed("active", true); if (typeof d === "string") { projection.classed("inactive", function(p) { return p.name !== d; }); projection.filter(function(p) { return p.name === d; }).each(moveToFront); ordinal_labels.classed("inactive", function(p) { return p !== d; }); ordinal_labels.filter(function(p) { return p === d; }).each(moveToFront); } else { projection.classed("inactive", function(p) { return p !== d; }); projection.filter(function(p) { return p === d; }).each(moveToFront); ordinal_labels.classed("inactive", function(p) { return p !== d.name; }); ordinal_labels.filter(function(p) { return p === d.name; }).each(moveToFront); } } function mouseout(d) { svg.classed("active", false); projection.classed("inactive", false); ordinal_labels.classed("inactive", false); } function moveToFront() { this.parentNode.appendChild(this); } function draw(d) { return line(dimensions.map(function(dimension) { return [x(dimension.name), dimension.scale(d[dimension.name])]; })); } } parallel(dataSet);
 svg { font: 12px sans-serif; } .background path { fill: none; stroke: none; stroke-width: 20px; pointer-events: stroke; } .foreground path { fill: none; stroke: steelblue; stroke-width: 1.5px; } .axis .title { font-size: 11px; font-weight: bold; text-transform: uppercase; } .axis line, .axis path { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis.string { font-size: 6px; } .label { -webkit-transition: fill 125ms linear; } .active .label:not(.inactive) { font-weight: bold; font-size: 11px; } .label.inactive { fill: #ccc; } .foreground path.inactive { stroke: #ccc; stroke-opacity: .2; stroke-width: 1.2px; } /* body { background-color: grey; } */ .node circle { fill: #fff; stroke: steelblue; stroke-width: 2px; } .node circle.highlighted { stroke: #00477F; stroke-width: 5px; } .node text { font: 12px sans-serif; } .link { fill: none; stroke: #ccc; stroke-width: 2px; } { box-sizing: border-box; } /* Create four equal columns that floats next to each other */ #svg0 { float: left; width: 120px; margin: 15px; padding: 20px; } #svg1 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg2 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg3 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg4 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg5 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg6 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg7 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg8 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } #svg9 { float: left; width: auto; length: auto; border: 0.8px solid grey; margin: 5px; margin-top: 10px; } .treeClass { width: 5000px; height: 300px; } /* Clear floats after the columns */ .treeClass:after { content: ""; display: table; clear: both; } #scatterplots { width: 3000px; } #parallel_coor { /* float: right; */ /* margin: 50px; */ /* margin-right: 40%; */ margin-top: 2px; margin-bottom: 2px; } #scatterplot { float: left; margin-left: 40px; /*margin: 20px;*/ } #ROCPlot { float: left; margin-right: 300px; margin-left: 300px; } svg rect.borderline { fill: white; stroke-width: 0.8; stroke: grey; } .circle { fill: red; } .dot { fill: black; stroke: grey; } .axis path, .axis line { fill: none; stroke: #aaa; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } ul { height: 28px; list-style-type: none; margin: -8px; padding: 0; overflow: hidden; background-color: #333; } li { float: left; border-right: 1px solid #bbb; } li:last-child { border-right: none; } li a { text-size: 10px; display: block; color: white; text-align: center; padding: 5px 7px; text-decoration: none; } li a:hover:not(.active) { background-color: #111; } .active { background-color: #1679af; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script> <div id="parallel_coor"></div>

任何幫助或想法如何使用 javascript 或 d3.js 解決這個問題將不勝感激。 謝謝!

你讓事情變得比必要的復雜,尤其是前景/背景邏輯。 我重寫了邏輯,為每個類別繪制一組線條,一條虛線,一條實線。 結果是一樣的,只是數據處理更容易:

 var dataSet = [{ "type": "car", "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", "first": 200, "second": 230, "third": 250, "fourth": 300 }, { "type": "bicycle", "dataset": "train", "first": 200, "second": 280, "third": 225, "fourth": 278 }, { "type": "boat", "first": 320, "second": 324, "third": 532, "fourth": 321 }, { "type": "boat", "dataset": "train", "first": 128, "second": 179, "third": 166, "fourth": 234 }, { "type": "airplane", "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>


編輯以指定要一起規范化的類別,您可以將每個type添加到數組中,將要規范化的類型分組在一起。

然后,遍歷組並計算常見的minmax 其余代碼與上面的相同。

 var dataSet = [{ "type": "car", "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", "first": 200, "second": 230, "third": 250, "fourth": 300 }, { "type": "bicycle", "dataset": "train", "first": 200, "second": 280, "third": 225, "fourth": 278 }, { "type": "boat", "first": 320, "second": 324, "third": 532, "fourth": 321 }, { "type": "boat", "dataset": "train", "first": 128, "second": 179, "third": 166, "fourth": 234 }, { "type": "airplane", "first": 1500, "second": 2000, "third": 2321, "fourth": 1793 }, { "type": "airplane", "dataset": "train", "first": 1438, "second": 2933, "third": 2203, "fourth": 2000 } ]; var normaliseTogether = [ ["car"], ["bicycle", "boat"], ["airplane"], ]; 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; } }); normaliseTogether.forEach(function(groups) { var groupData = processedData.filter(function(d) { return groups.includes(d.type); }); // Normalise the values in the arrays let min = Infinity, max = -Infinity; groupData.forEach(function(d) { min = Math.min(min, d3.min(d.train), d3.min(d.test)); max = Math.max(max, d3.max(d.train), d3.max(d.test)); }); groupData.forEach(function(d) { 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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM