我想得到点图表定制
I want to get dot chart customize
我正在学习 d3 图表,我想得到像图像一样的结果。
数据是 json,看起来像这样:
[{
"date": "2020.12.1",
"pay": 1
},
{
"date": "2021.1.2",
"pay": 1
},
{
"date": "2021.2.1",
"pay": 1
},
...
pay = 1 //on time,
pay = 2 // missed,
pay = 3 // no data
谢谢关心。
举个例子。为简单起见,我对颜色图例中条目的位置进行了硬编码。在实践中,最好在 HTML 中做颜色图例,以便您可以利用自动水平布局。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
/* --- set up --- */
const margin = { top: 10, bottom: 50, left: 10, right: 10 };
const width = 500 - margin.left - margin.right;
const height = 140 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
/* --- data --- */
const parseTime = d3.timeParse('%Y.%-m.%-d');
const data = [
{ date: "2015.1.1", pay: 2 },
{ date: "2015.2.1", pay: 2 },
{ date: "2015.3.1", pay: 2 },
{ date: "2015.4.1", pay: 2 },
{ date: "2015.5.1", pay: 2 },
{ date: "2015.6.1", pay: 2 },
{ date: "2015.7.1", pay: 2 },
{ date: "2015.8.1", pay: 2 },
{ date: "2015.9.1", pay: 2 },
{ date: "2015.10.1", pay: 2 },
{ date: "2015.11.1", pay: 2 },
{ date: "2015.12.1", pay: 2 },
{ date: "2016.1.1", pay: 2 },
{ date: "2016.2.1", pay: 2 },
{ date: "2016.3.1", pay: 2 },
{ date: "2016.4.1", pay: 2 },
{ date: "2016.5.1", pay: 2 },
{ date: "2016.6.1", pay: 2 },
{ date: "2016.7.1", pay: 1 },
{ date: "2016.8.1", pay: 1 },
{ date: "2016.9.1", pay: 1 },
{ date: "2016.10.1", pay: 1 },
{ date: "2016.11.1", pay: 1 },
{ date: "2016.12.1", pay: 1 },
{ date: "2017.1.1", pay: 1 },
{ date: "2017.2.1", pay: 1 },
{ date: "2017.3.1", pay: 1 },
{ date: "2017.4.1", pay: 1 },
{ date: "2017.5.1", pay: 1 },
{ date: "2017.6.1", pay: 1 },
{ date: "2017.7.1", pay: 1 },
{ date: "2017.8.1", pay: 1 },
{ date: "2017.9.1", pay: 1 },
{ date: "2017.10.1", pay: 1 },
{ date: "2017.11.1", pay: 1 },
{ date: "2017.12.1", pay: 1 },
{ date: "2018.1.1", pay: 1 },
{ date: "2018.2.1", pay: 1 },
{ date: "2018.3.1", pay: 3 },
{ date: "2018.4.1", pay: 2 },
{ date: "2018.5.1", pay: 1 },
{ date: "2018.6.1", pay: 3 },
{ date: "2018.7.1", pay: 1 },
{ date: "2018.8.1", pay: 3 },
{ date: "2018.9.1", pay: 3 },
{ date: "2018.10.1", pay: 3 },
{ date: "2018.11.1", pay: 1 },
{ date: "2018.12.1", pay: 1 },
{ date: "2019.1.1", pay: 3 },
{ date: "2019.2.1", pay: 1 },
{ date: "2019.3.1", pay: 2 },
{ date: "2019.4.1", pay: 3 },
{ date: "2019.5.1", pay: 3 },
{ date: "2019.6.1", pay: 1 },
{ date: "2019.7.1", pay: 1 },
{ date: "2019.8.1", pay: 1 },
{ date: "2019.9.1", pay: 3 },
{ date: "2019.10.1", pay: 2 },
{ date: "2019.11.1", pay: 2 },
{ date: "2019.12.1", pay: 2 },
{ date: "2020.1.1", pay: 1 },
{ date: "2020.2.1", pay: 2 },
{ date: "2020.3.1", pay: 2 },
{ date: "2020.4.1", pay: 1 },
{ date: "2020.5.1", pay: 3 },
{ date: "2020.6.1", pay: 1 },
{ date: "2020.7.1", pay: 1 },
{ date: "2020.8.1", pay: 3 },
{ date: "2020.9.1", pay: 1 },
{ date: "2020.10.1", pay: 2 },
{ date: "2020.11.1", pay: 1 },
{ date: "2020.12.1", pay: 1 },
{ date: "2021.1.1", pay: 3 },
{ date: "2021.2.1", pay: 2 },
{ date: "2021.3.1", pay: 1 },
{ date: "2021.4.1", pay: 1 },
{ date: "2021.5.1", pay: 1 },
{ date: "2021.6.1", pay: 2 },
{ date: "2021.7.1", pay: 3 },
{ date: "2021.8.1", pay: 3 },
{ date: "2021.9.1", pay: 2 },
{ date: "2021.10.1", pay: 2 },
{ date: "2021.11.1", pay: 3 },
{ date: "2021.12.1", pay: 3 },
]
// convert the date strings to Date objects
.map(({ date, pay }) => ({ date: parseTime(date), pay }));
// group the payments by year
const groupedByYear = d3.group(data, d => d.date.getFullYear());
/* --- scales --- */
// scale to place the groups according to the year
const x = d3.scaleBand()
.domain(groupedByYear.keys())
.range([0, width]);
// scales to place the dots in a group
const numRows = 3;
const numCols = 4;
const row = d3.scalePoint()
.domain(d3.range(numRows))
.range([0, height])
.padding(1);
const col = d3.scalePoint()
.domain(d3.range(numCols))
.range([0, x.bandwidth()])
.padding(1);
// color scale for circles
const ontime = 1;
const missing = 2;
const nodata = 3;
const color = d3.scaleOrdinal()
.domain([ontime, missing, nodata])
.range(['DarkSlateGray', 'MediumVioletRed', 'Gainsboro']);
// color scale for year labels
const colorYear = d3.scaleSequential()
// input is number of missed payments for that year
.domain([0, 12])
// output interpolates between the color for on time
// and the color for missing
.interpolator(d3.interpolateHcl(color(ontime), color(missing)));
/* --- draw circles --- */
// add one group for each year
const groups = g.selectAll('g')
.data(groupedByYear)
.join('g')
.attr('transform', ([year, payments]) => `translate(${x(year)})`);
// calculate max radius size
const radius = (Math.min(row.step(), col.step()) / 2) - 2;
// add circles
groups.selectAll('circle')
.data(([year, payments]) => payments)
.join('circle')
.attr('transform', (d, i) => {
const rowIndex = Math.floor(i / numCols);
const colIndex = i % numCols;
return `translate(${col(colIndex)},${row(rowIndex)})`;
})
.attr('fill', d => color(d.pay))
.attr('r', radius);
/* --- add axis for year labels --- */
const xAxis = d3.axisBottom(x).tickSize(0);
g.append('g')
// move to the bottom of the chart
.attr('transform', `translate(0,${height})`)
// add axis
.call(xAxis)
// remove baseline
.call(g => g.select('.domain').remove())
// increase font size of the labels and set their color
.call(
g => g.selectAll('text')
.attr('font-size', 14)
.attr('fill', year => colorYear(
// get number of months with missing payments
groupedByYear.get(year)
.filter(d => d.pay === missing).length
))
);
/* --- add color legend --- */
const fontSize = 14;
const legendData = [
{label: 'ON TIME', color: color(ontime), x: 0},
{label: 'MISSED PAYMENT', color: color(missing), x: 100},
{label: 'NO DATA', color: color(nodata), x: 270},
];
const legendCells = g.append('g')
.attr('transform', `translate(${margin.left},${height + 40})`)
.selectAll('g')
.data(legendData)
.join('g')
.attr('transform', d => `translate(${d.x})`);
legendCells.append('circle')
.attr('r', fontSize / 2)
.attr('fill', d => d.color);
legendCells.append('text')
.attr('dominant-baseline', 'middle')
.attr('font-family', 'sans-serif')
.attr('fill', 'black')
.attr('font-size', fontSize)
.attr('x', fontSize)
.text(d => d.label);
</script>
</body>
</html>
我正在学习 d3 图表,我想得到像图像一样的结果。 数据是 json,看起来像这样:
[{
"date": "2020.12.1",
"pay": 1
},
{
"date": "2021.1.2",
"pay": 1
},
{
"date": "2021.2.1",
"pay": 1
},
...
pay = 1 //on time,
pay = 2 // missed,
pay = 3 // no data
谢谢关心。
举个例子。为简单起见,我对颜色图例中条目的位置进行了硬编码。在实践中,最好在 HTML 中做颜色图例,以便您可以利用自动水平布局。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
/* --- set up --- */
const margin = { top: 10, bottom: 50, left: 10, right: 10 };
const width = 500 - margin.left - margin.right;
const height = 140 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
/* --- data --- */
const parseTime = d3.timeParse('%Y.%-m.%-d');
const data = [
{ date: "2015.1.1", pay: 2 },
{ date: "2015.2.1", pay: 2 },
{ date: "2015.3.1", pay: 2 },
{ date: "2015.4.1", pay: 2 },
{ date: "2015.5.1", pay: 2 },
{ date: "2015.6.1", pay: 2 },
{ date: "2015.7.1", pay: 2 },
{ date: "2015.8.1", pay: 2 },
{ date: "2015.9.1", pay: 2 },
{ date: "2015.10.1", pay: 2 },
{ date: "2015.11.1", pay: 2 },
{ date: "2015.12.1", pay: 2 },
{ date: "2016.1.1", pay: 2 },
{ date: "2016.2.1", pay: 2 },
{ date: "2016.3.1", pay: 2 },
{ date: "2016.4.1", pay: 2 },
{ date: "2016.5.1", pay: 2 },
{ date: "2016.6.1", pay: 2 },
{ date: "2016.7.1", pay: 1 },
{ date: "2016.8.1", pay: 1 },
{ date: "2016.9.1", pay: 1 },
{ date: "2016.10.1", pay: 1 },
{ date: "2016.11.1", pay: 1 },
{ date: "2016.12.1", pay: 1 },
{ date: "2017.1.1", pay: 1 },
{ date: "2017.2.1", pay: 1 },
{ date: "2017.3.1", pay: 1 },
{ date: "2017.4.1", pay: 1 },
{ date: "2017.5.1", pay: 1 },
{ date: "2017.6.1", pay: 1 },
{ date: "2017.7.1", pay: 1 },
{ date: "2017.8.1", pay: 1 },
{ date: "2017.9.1", pay: 1 },
{ date: "2017.10.1", pay: 1 },
{ date: "2017.11.1", pay: 1 },
{ date: "2017.12.1", pay: 1 },
{ date: "2018.1.1", pay: 1 },
{ date: "2018.2.1", pay: 1 },
{ date: "2018.3.1", pay: 3 },
{ date: "2018.4.1", pay: 2 },
{ date: "2018.5.1", pay: 1 },
{ date: "2018.6.1", pay: 3 },
{ date: "2018.7.1", pay: 1 },
{ date: "2018.8.1", pay: 3 },
{ date: "2018.9.1", pay: 3 },
{ date: "2018.10.1", pay: 3 },
{ date: "2018.11.1", pay: 1 },
{ date: "2018.12.1", pay: 1 },
{ date: "2019.1.1", pay: 3 },
{ date: "2019.2.1", pay: 1 },
{ date: "2019.3.1", pay: 2 },
{ date: "2019.4.1", pay: 3 },
{ date: "2019.5.1", pay: 3 },
{ date: "2019.6.1", pay: 1 },
{ date: "2019.7.1", pay: 1 },
{ date: "2019.8.1", pay: 1 },
{ date: "2019.9.1", pay: 3 },
{ date: "2019.10.1", pay: 2 },
{ date: "2019.11.1", pay: 2 },
{ date: "2019.12.1", pay: 2 },
{ date: "2020.1.1", pay: 1 },
{ date: "2020.2.1", pay: 2 },
{ date: "2020.3.1", pay: 2 },
{ date: "2020.4.1", pay: 1 },
{ date: "2020.5.1", pay: 3 },
{ date: "2020.6.1", pay: 1 },
{ date: "2020.7.1", pay: 1 },
{ date: "2020.8.1", pay: 3 },
{ date: "2020.9.1", pay: 1 },
{ date: "2020.10.1", pay: 2 },
{ date: "2020.11.1", pay: 1 },
{ date: "2020.12.1", pay: 1 },
{ date: "2021.1.1", pay: 3 },
{ date: "2021.2.1", pay: 2 },
{ date: "2021.3.1", pay: 1 },
{ date: "2021.4.1", pay: 1 },
{ date: "2021.5.1", pay: 1 },
{ date: "2021.6.1", pay: 2 },
{ date: "2021.7.1", pay: 3 },
{ date: "2021.8.1", pay: 3 },
{ date: "2021.9.1", pay: 2 },
{ date: "2021.10.1", pay: 2 },
{ date: "2021.11.1", pay: 3 },
{ date: "2021.12.1", pay: 3 },
]
// convert the date strings to Date objects
.map(({ date, pay }) => ({ date: parseTime(date), pay }));
// group the payments by year
const groupedByYear = d3.group(data, d => d.date.getFullYear());
/* --- scales --- */
// scale to place the groups according to the year
const x = d3.scaleBand()
.domain(groupedByYear.keys())
.range([0, width]);
// scales to place the dots in a group
const numRows = 3;
const numCols = 4;
const row = d3.scalePoint()
.domain(d3.range(numRows))
.range([0, height])
.padding(1);
const col = d3.scalePoint()
.domain(d3.range(numCols))
.range([0, x.bandwidth()])
.padding(1);
// color scale for circles
const ontime = 1;
const missing = 2;
const nodata = 3;
const color = d3.scaleOrdinal()
.domain([ontime, missing, nodata])
.range(['DarkSlateGray', 'MediumVioletRed', 'Gainsboro']);
// color scale for year labels
const colorYear = d3.scaleSequential()
// input is number of missed payments for that year
.domain([0, 12])
// output interpolates between the color for on time
// and the color for missing
.interpolator(d3.interpolateHcl(color(ontime), color(missing)));
/* --- draw circles --- */
// add one group for each year
const groups = g.selectAll('g')
.data(groupedByYear)
.join('g')
.attr('transform', ([year, payments]) => `translate(${x(year)})`);
// calculate max radius size
const radius = (Math.min(row.step(), col.step()) / 2) - 2;
// add circles
groups.selectAll('circle')
.data(([year, payments]) => payments)
.join('circle')
.attr('transform', (d, i) => {
const rowIndex = Math.floor(i / numCols);
const colIndex = i % numCols;
return `translate(${col(colIndex)},${row(rowIndex)})`;
})
.attr('fill', d => color(d.pay))
.attr('r', radius);
/* --- add axis for year labels --- */
const xAxis = d3.axisBottom(x).tickSize(0);
g.append('g')
// move to the bottom of the chart
.attr('transform', `translate(0,${height})`)
// add axis
.call(xAxis)
// remove baseline
.call(g => g.select('.domain').remove())
// increase font size of the labels and set their color
.call(
g => g.selectAll('text')
.attr('font-size', 14)
.attr('fill', year => colorYear(
// get number of months with missing payments
groupedByYear.get(year)
.filter(d => d.pay === missing).length
))
);
/* --- add color legend --- */
const fontSize = 14;
const legendData = [
{label: 'ON TIME', color: color(ontime), x: 0},
{label: 'MISSED PAYMENT', color: color(missing), x: 100},
{label: 'NO DATA', color: color(nodata), x: 270},
];
const legendCells = g.append('g')
.attr('transform', `translate(${margin.left},${height + 40})`)
.selectAll('g')
.data(legendData)
.join('g')
.attr('transform', d => `translate(${d.x})`);
legendCells.append('circle')
.attr('r', fontSize / 2)
.attr('fill', d => d.color);
legendCells.append('text')
.attr('dominant-baseline', 'middle')
.attr('font-family', 'sans-serif')
.attr('fill', 'black')
.attr('font-size', fontSize)
.attr('x', fontSize)
.text(d => d.label);
</script>
</body>
</html>