gpt4 book ai didi

javascript - 更新使用双重嵌套数据绑定(bind)的 d3.js (v5) 选择

转载 作者:行者123 更新时间:2023-11-28 03:07:53 26 4
gpt4 key购买 nike

我在 d3 中有一个双重嵌套数据集。我想创建一个散点图,以针对第一个键(“时间”变量)的每个值进行更新,但每个点的数据都绑定(bind)到第二个键(“空间”变量)的值。因此,需要明确的是,每个点都应该转换为新的坐标,并且其半径也必须更新。

这是一个数据样本(在文件“prosperLoanData.csv”中)

BorrowerState,LoanOriginationDate,LoanOriginalAmount,LenderYield
AK,2007-01-01 00:00:01,1000,0.1
AK,2007-01-01 00:00:01,2000,0.11
AK,2007-01-01 00:00:01,1500,0.09
AK,2007-01-01 00:00:01,500,0.1
AK,2008-01-01 00:00:01,2500,0.07
AK,2008-01-01 00:00:01,3000,0.06
AK,2008-01-01 00:00:01,3500,0.0652
OK,2007-01-01 00:00:01,4000,0.08
OK,2007-01-01 00:00:01,4100,0.081
OK,2007-01-01 00:00:01,4500,0.0812
OK,2007-01-01 00:00:01,4600,0.0799
OK,2007-01-01 00:00:01,3900,0.08
OK,2008-01-01 00:00:01,5000,0.05

这是我的代码。我虽然删除 YearGroups1.exit().remove() 就足够了,但事实并非如此。我无法正确选择圆圈并将它们重新绑定(bind)到新数据集(在第二个嵌套级别)。你能帮我吗?

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>

div.tooltip {
position: absolute;
text-align: center;
width: 180px;
height: 45px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}

</style>
<script type="text/javascript">
</script>
</head>
<body>
<script type="text/javascript">
/*
Use D3 to load the loan data
*/

var parseTime = d3.timeParse("%Y-%m-%d %H:%M:%S")

// load data
d3.csv("prosperLoanData.csv").then(function(data) {

// Setting global parameters

"use strict";

// ----> Margin & size
var margin = 75,
width = 1400 - margin,
height = 600 - margin,
op = 0.5,
years = [2007, 2008];

// ----> Format for strings
var formatDec = d3.format(".0f"),
formatper = d3.format(".2%"),
formatM = d3.format("\$.2s");


// change string (from CSV) into number format
data.forEach( function(d) {
d["Year"] = parseTime(d.LoanOriginationDate).getYear() + 1900;
d["LoanOriginalAmount"] = +d["LoanOriginalAmount"];
d["LenderYield"] = +d["LenderYield"];
// debugger
return d; });

// Function definition

// function update() {};
function key_func(d) {
return d['key'];
}

// FIXED PART

// Define the svg element
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)
.attr("height", height + margin);

// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);

// Data preprocessing - grouping

var nested_year = d3.nest()
.key( function(d) { return d.Year } )
.key( function(d) { return d.BorrowerState })
.rollup( function(points) {

// debugger
var mean_loan = d3.mean(points, function(d) { return d.LoanOriginalAmount; } );
var mean_lenderyield = d3.mean(points, function(d) { return d.LenderYield; } );
// var std_loan = d3.deviation(points, function(d) { return d.LoanOriginalAmount; } );
var sum_loan = d3.sum(points, function(d) { return d.LoanOriginalAmount; } );

var max_loan = d3.max(points, function(d) { return d.LoanOriginalAmount; } );
var min_loan = d3.min(points, function(d) { return d.LoanOriginalAmount; } );

var max_ly = d3.max(points, function(d) { return d.LenderYield; } );
var min_ly = d3.min(points, function(d) { return d.LenderYield; } );

// debugger

return {
"meanLoan" : mean_loan,
"meanLenderyield" : mean_lenderyield,
// "stdLoan" : std_loan,
"sumLoan" : sum_loan,
};

}

)
.entries(data);

// Determining X/Y Max & Min

var LOA_E1 = d3.min(nested_year, function(d) {return d3.min(d.values, function(da) { return da.value.meanLoan; });})
var LOA_E2 = d3.max(nested_year, function(d) {return d3.max(d.values, function(da) { return da.value.meanLoan; });})

var LY_E1 = d3.min(nested_year, function(d) {return d3.min(d.values, function(da) { return da.value.meanLenderyield; });})
var LY_E2 = d3.max(nested_year, function(d) {return d3.max(d.values, function(da) { return da.value.meanLenderyield; });})

var LenderYield_Extent = [LY_E1 , LY_E2 ];
var LoanOriginalAmount_Extent = [LOA_E1 , LOA_E2];

// Creating a scale
var XScale = d3.scaleLinear()
.range([margin , width])
.domain([ 1.05 * LenderYield_Extent[0] - 0.05 * LenderYield_Extent[1] ,
1.05 * LenderYield_Extent[1] - 0.05 * LenderYield_Extent[0] ] );

// debugger

var YScale = d3.scaleLinear()
.range([height , margin])
.domain([ 0, 1.025 * LoanOriginalAmount_Extent[1]]);

var SUM_LOAN_Extent = [70E3 , 1.2E7]; // d3.extent(red_data.value, d => d.value.sumLoan);

var radius = d3.scaleSqrt()
.domain(SUM_LOAN_Extent)
.range([3,0.375 * margin/2]);

// Creating axis

var x_axis = d3.axisBottom()
.scale(XScale)
.tickFormat(d3.format(".2%"));

var y_axis = d3.axisLeft()
.scale(YScale)
.tickFormat(d3.format(".0f"));

svg.append('g')
.attr('transform', "translate(0," + height + ")")
.call(x_axis);

svg.append('g')
.attr('transform', "translate(" + margin + ",0)")
.call(y_axis);

// Text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2 + margin) + " ," +
(height + margin/2) + ")")
.style("text-anchor", "middle")
.style("font-size", 20)
.text("Mean lender yield (percentage)");

// Text label for the y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.style("font-size", 20)
.text("Mean loan original amount [US dollars]");

// Creating gridlines

function make_x_gridlines() {
return d3.axisBottom(XScale)
.ticks(5);
};

function make_y_gridlines() {
return d3.axisLeft(YScale)
.ticks(5);
};

svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-(height - 1 * margin ))
.tickFormat(""));

svg.append("g")
.attr("class", "grid")
.attr('transform', "translate(" + margin + ",0)")
.call(make_y_gridlines()
.tickSize(-(width - 1 * margin ))
.tickFormat(""));

// Add legend

var DELTA = SUM_LOAN_Extent[1] - SUM_LOAN_Extent[0];

var valuesToShow = [SUM_LOAN_Extent[0] + 0.00 * DELTA, SUM_LOAN_Extent[0] + 0.25 * DELTA ,
SUM_LOAN_Extent[0] + 0.50 * DELTA, SUM_LOAN_Extent[0] + 0.75 * DELTA ,
SUM_LOAN_Extent[0] + 1.00 * DELTA ];

var xCircle = width + 0.35 * margin;
var xLabel = 200;
var yCircle = 150;

var legend = svg.append("g")
.attr("class","legend")
.attr("trasform","translate(" + (width - 100) + "," + 20 + ")" )
.selectAll("g")
.data(valuesToShow)
.enter()
.append("g");

legend.append("circle")
.attr("cy", function(d,i) {
return (i+1)* 0.4 * yCircle + radius(d)/2; })
.attr("cx", xCircle)
.attr("r", function(d) {
return radius(d); })
.attr("stroke","black")
.attr("stroke-width",1.)
.attr("opacity", op);

// Add legend: labels
svg.selectAll("legend")
.data(valuesToShow)
.enter()
.append("text")
.attr('x', function(d,i){ return width + 0.55 * margin; } )
.attr('y', function(d,i){ return (i+1)* 0.4 * yCircle + radius(d)/2 } )
.text( function(d){ return formatM(d) } )
.style("font-size", 10)
.attr('alignment-baseline', 'middle');

svg.append("text")
.attr('x', 0.98 * xCircle )
.attr('y', 0.15 * yCircle )
.text( "Size = " )
.style("font-size", 12.5)
.attr('alignment-baseline', 'middle');

svg.append("text")
.attr('x', 0.98 * xCircle )
.attr('y', 0.15 * yCircle + 10 )
.text( "Total loan" )
.style("font-size", 12.5)
.attr('alignment-baseline', 'middle');

svg.append("text")
.attr('x', 0.98 * xCircle )
.attr('y', 0.15 * yCircle + 20)
.text( "amount (US \$)" )
.style("font-size", 12.5)
.attr('alignment-baseline', 'middle');

// Add color legend

var colors = d3.scaleOrdinal()
.domain([0, 1, 2])
.range(["blue", "green" , "black"]);

var stat = d3.scaleOrdinal()
.domain([0, 1, 2])
.range(["CA", "TX" , "Others"]);

var aaa = [ SUM_LOAN_Extent[0] + 1.00 * DELTA , SUM_LOAN_Extent[0] + 1.00 * DELTA, SUM_LOAN_Extent[0] + 1.00 * DELTA];

var legend1 = svg.append("g")
.attr("class","legend")
.attr("trasform","translate(" + (width - 100) + "," + 20 + ")" )
.selectAll("g")
.data(aaa)
.enter()
.append("g");

legend1.append("circle")
.attr("cy", function(d,i) { //debugger
return (6+i)* 0.4 * yCircle + radius(d)/2; })
.attr("cx", xCircle)
.attr("r", function(d) {
return radius(d); })
.attr("fill", function(d,i){ return colors(i)})
.attr("opacity", op);

// Add legend: labels
svg.selectAll(".legend1")
.data(aaa)
.enter()
.append("text")
.attr('x', function(d,i){ return width + 0.55 * margin; } )
.attr('y', function(d,i){ return (6+i)* 0.4 * yCircle + radius(d)/2 } )
.text( function(d,i){ return stat(i) } )
.style("font-size", 10)
.attr('alignment-baseline', 'middle');

// Appending first circles

// Accessing 1st group

var YearGroups = svg.selectAll(".YearGroups")
.data(nested_year, key_func)
.enter()
.append("g")
.attr("class", "YearGroups");

// Accessing 2nd group

var circles = YearGroups.selectAll("circle")
.data(function(d){
return d.values
})
.enter()
.append("circle")
.transition()
.duration(500)
.attr("cx", function(d) { return XScale(d.value.meanLenderyield) } )
.attr("cy", function(d) { return YScale(d.value.meanLoan ) } )
.attr("r" , function(d) { return radius(d.value.sumLoan ) } )
.attr("fill" , function(d) { if
(d.key == "CA") {return "blue"}
if (d.key == "TX") {return "green"}
else
{ return "black" }})
.attr("opacity" , op);

// debugger

// VARIABLE PART

function update(year) {

var filt = nested_year.filter(function(d) {return d.key == year;} );

var YearGroups1 = svg.selectAll(".YearGroups")
.data(filt, key_func);

YearGroups1.exit().remove();

var circles = YearGroups1.enter().append('g')
.attr("class", "YearGroups").selectAll('circle')
.data(function(d){ return d.values });

var CircPl = circles.enter()
.append("circle")
.transition()
.duration(500)
.attr("cx", function(d) { // debugger
return XScale(d.value.meanLenderyield); })
.attr("cy", function(d) { return YScale(d.value.meanLoan ); })
.attr("r" , function(d) { return radius(d.value.sumLoan ); })
.attr("fill" , function(d) { if
(d.key == "DC") {return "blue"}
if (d.key == "AR") {return "green"}
else
{ return "black" }})
.attr("opacity" , op)
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Lender yield : " + formatper(d.value.meanLenderyield) + "<br/>" +
"Loan original amount : " + formatDec(d.value.meanLoan) + " $ <br/>" +
"State : " + d.key)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});

CircPl


// debugger

// Chart Title
svg.selectAll(".Title").remove()

svg
.append("text")
.attr("class","Title")
.attr("x", (margin + width) / 2)
.attr("y", margin / 2)
.attr("font-weight", "bold")
.attr("text-anchor", "middle")
.style("font-size", "32px")
.text("US Loans per State in " + year); // Title updated
}

var year_idx = 0;

var year_interval = setInterval(function() {
update(years[year_idx]);

year_idx++;

if(year_idx >= years.length) {
clearInterval(year_interval);
}
}, 1000);


});

</script>
</body>
</html>

最佳答案

我找到了该问题的解决方案(感谢 Udacity Mentor 团队的 Myles C.)。

事实上,要访问的数据很容易存储在虚拟变量中,该变量随后用于数据绑定(bind),并且 .merge() 语句用于将两者更新到所有圆。

function update_video(year) {

var filt = nested_year.filter(function(d) {
return d.key == year;
});

var data = filt[0].values

// Selection and Bind Data
const update = YearGroups.selectAll("circle")
.data(data);

var upd = update.enter()
.select("circle")
.merge(update)
.transition()
.duration(1500)
.attr("class", "node_circle")
.attr("cx", function(d) {
return XScale(d.value.meanLenderyield);
})
.attr("cy", function(d) {
return YScale(d.value.meanLoan);
})
.attr("r", function(d) {
return radius(d.value.sumLoan);
})
.attr("fill", function(d) {
if (d.key == "CA") { return "blue";}
if (d.key == "TX") { return "green";}
else { return "black"}
});

// Command necessary to remove States that are not available in the
// database for that year
update.exit().remove();


// Chart Title
svg.selectAll(".Title").remove();

svg.append("text")
.attr("class", "Title")
.attr("x", (margin + width) / 2)
.attr("y", margin / 2)
.attr("font-weight", "bold")
.attr("text-anchor", "middle")
.style("font-size", "32px")
.text("US Loans per State in " + year); // Title update
};

使用此函数,无论状态如何(嵌套数据的第二个键),每个圆都会更新; .exit().remove() 命令已用于删除所有未绑定(bind)到数据的点。

然后,使用第二个函数让用户选择年份并更新圆圈而无需过渡,但添加工具提示以获取用鼠标悬停在点上的数据信息。

function update_with_tooltip(year) {

var filt = nested_year.filter(function(d) {
return d.key == year;
});

var data = filt[0].values;

// Selection and Bind Data
const update = YearGroups.selectAll("circle")
.data(data);

// Update and merge
var upd = update.enter()
.select("circle")
.merge(update)
.attr("class", "node_circle")
.attr("cx", function(d) {
return XScale(d.value.meanLenderyield);
})
.attr("cy", function(d) {
return YScale(d.value.meanLoan);
})
.attr("r", function(d) {
return radius(d.value.sumLoan);
})
.attr("fill", function(d) {
if (d.key == "CA") { return "blue";}
if (d.key == "TX") { return "green";}
else { return "black"}
})
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);

div.html("Lender yield : " + formatper(d.value.meanLenderyield) +
"<br/>" + "Loan original amount : " + formatDec(d.value.meanLoan)
+ " $ <br/>" + "State : " + d.key)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});

// Command necessary to remove States that are not available in the
// database for that year
update.exit().remove();

// Chart Title
svg.selectAll(".Title").remove();

svg.append("text")
.attr("class", "Title")
.attr("x", (margin + width) / 2)
.attr("y", margin / 2)
.attr("font-weight", "bold")
.attr("text-anchor", "middle")
.style("font-size", "32px")
.text("US Loans per State in " + year); // Title update
}

关于javascript - 更新使用双重嵌套数据绑定(bind)的 d3.js (v5) 选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60442199/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com