【问题标题】:I want to get dot chart customize我想自定义点图
【发布时间】:2021-12-12 19:04:43
【问题描述】:

我正在学习 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

谢谢。

【问题讨论】:

    标签: d3.js charts dot


    【解决方案1】:

    这是一个例子。为简单起见,我在颜色图例中硬编码了条目的位置。在实践中,最好在 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>

    【讨论】:

    • 但是你能把年份颜色作为颜色范围吗?例如2020年,如果ON TIME为12,则年份颜色变为ON TIME颜色,如果ON TIME为6,MISSED为6,则为ON TIME和MISSED颜色之间的中间色。
    • 当然,我已经更新了示例来展示如何做到这一点。您可以创建一个色标,在颜色之间插入准时付款和错过付款。此色标的输入将是一年中未付款的次数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-11
    • 2021-12-28
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多