D3JsDay22给我两个以上的变数,给你呈现资料的散布—散布图

散布图

结束完地图的内容後,这几天将会介绍先前没有绘制过的图表,之前使用长条图来表示各个行政区的人口数,散布图则适合做两个变数以上的关系性,如果呈现的状况聚集的点像是在一直线上的话,表示可能带有正相关或是负相关,这次我们使用d3JS来绘制散布图,X轴表示平方公里、Y轴表示人口数,

tickSize()函式

还之前教过如何设置座标轴吗?如果忘记可以先去看第十二天完成制作的压轴,画出图表座标轴的文章,这里要再介绍一个函式tickSize()用来方便观看每个点的确切位置。

d3官方tickSize()

官方说明如下,可以设置外部轴和内部轴的大小,至於什麽是外部轴和内部轴呢?

於是你点了连结的蓝色字它又告诉你如下,这边提到外部刻度是path

我们可以撰写程序码实际观看一下

tickSizeOuter()

const svg = d3.select("body")
            .append("svg")
            .attr("width", 800)
            .attr("height", 600);
let scaleY = d3.scaleLinear()
      .domain([0, 100])
      .range([500,0]);
let axisY = d3.axisRight(scaleY)
            .tickSizeOuter(100);
const g = svg.append("g")
.attr("transform",`translate(0,10)`);
axisY(g);

这个时候你打开主控台可以发现他将<path>的d改变了数值,另外看画面头和尾的长度也增加了如下图

tickSizeInner()函式

带入tickSizeInner()看看

    let axisY = d3.axisRight(scaleY)
                .tickSizeInner(100);

一样打开主控台观看数值的变化和网页画面如下图

这边可以发现tickSizeInner()改变的数值是class为tick的g元素里面的<line>,而tickSizeOuter()改变的数值是最上面的<path>元素里面的属性d。最後我们尝试改用tickSize()试试看就可以发现他将会改变<path><line>,这边就不展示成果罗

  let axisY = d3.axisRight(scaleY)
                .tickSize(100)

因此当我们没有设置tickSize()时,预设将会给<path><line>数值是6,另外值得一提的是如果输入负值轴线将会往反方向伸展

实际带入地图资料

了解後我们带入上次的行政区人口密度的资料,这时候我们就可以将tickSize()设定的数值和scale的上限也就是range()相等,记得注意伸展方向,因此我们设置负值,另外我们将对座标轴的样式做小部分的修改,因此加入class名字。
具体程序码如下

 const newTaipei = taipei.map((el) => {
                    el.people_total = Number(el.people_total);
                    el.area = Number(el.area);
                    el.population_density = Number(el.population_density);
                    el.site_id = el.site_id.substr(3);
                    return el;
                });
const svg = d3.select("body")
            .append("svg")
            .attr("width", 600)
            .attr("height", 600);
const scaleX = d3.scaleLinear().domain([0,70]).range([0,500]);
const scaleY = d3.scaleLinear().domain([0, 320000]).range([500, 0]);
const axisX = d3.axisBottom(scaleX)
            .ticks(15)
            .tickFormat(function (d) { 
               return  d+"km²";
            })
            .tickSize(-500); 
const axisY = d3.axisLeft(scaleY)
                .ticks(15)
                .tickFormat(function (d) {
                    return d / 10000 + "万";
                })
                .tickSize(-500); 
const gX = svg.append("g")
            .attr("transform",`translate(50,550)`)
            .classed("xAxis",true);
axisX(gX);
const gY = svg.append("g")
            .attr("transform",`translate(50,50)`)
            .classed("yAxis",true);
axisY(gY);

接下来将会看到的画面如下图

这时候当网格渲染出来之後如果在内容加入散布图的圆点时可能会让人觉得网格影响视觉比重,因此我们使用css选取器选到刚刚所加入的class并将里面的<line>的颜色进行微调程序码如下

.xAxis line, .yAxis line {
    stroke:rgba(0,0,0,.1);
}

接下来我们将要进行资料绑定,与先前制作的长条图的差别在於这次绑定资料在位置设定使用两种变数资料
程序码如下

const circle = svg.selectAll("circle")
                    .data(newTaipei)
                    .join("circle");
const joinCircle = circle.data(newTaipei).join("circle");
joinCircle.attr("cx",d=>(scaleX(d.area)))
        .attr("cy",d=>(scaleY(d.people_total)))
        .attr("r",5)
        .attr("fill","red")
        .attr("transform", "translate(50,50)");   

接下来就会看到如下图

由於每个红点表示的是每个行政区,因此我们得再加入一项g群组来显示文字,它的位置得到的方式和刚刚圆点的位置得道的方式一样,皆来自於绑定资料的区域面积和人口总数所转换的Scale函式,但是必须微调往右移,因此使用gText.attr("transform","translate(60,50)");

具体程序码如下

let gText = svg.append("g");
gText.attr("transform",`translate(60,50)`);
let joinText = gText.selectAll("text").data(newTaipei).join("text");
joinText.text((d) => {
    return d.site_id;
    })
    .attr("x", (d, i) => {
        return scaleX(d.area);
    })
    .attr("y", (d) => {
        return scaleY(d.people_total);
    })
    .attr("font-size",9);

最後应当可以看见如下图

本日完整程序码如下

   d3.json("populationDensity.json")
    .then((data) => {
        return data.result.records;
    })
    .then((data) => {
        let reg = RegExp(/台北市/);
        return data.filter((el) => {
            return reg.test(el.site_id);
        });
    })
    .then((taipei) => {
        const newTaipei = taipei.map((el) => {
            el.people_total = Number(el.people_total);
            el.area = Number(el.area);
            el.population_density = Number(el.population_density);
            el.site_id = el.site_id.substr(3);
            return el;
        });
        const svg = d3.select("body")
                    .append("svg")
                    .attr("width", 600)
                    .attr("height", 600);
        const scaleX = d3.scaleLinear().domain([0,70]).range([0,500]);
        const scaleY = d3.scaleLinear().domain([0, 320000]).range([500, 0]);
        const axisX = d3.axisBottom(scaleX)
                    .ticks(15)
                    .tickFormat(function (d) { 
                       return  d+"km²";
                    })
                    .tickSize(-500)  ; 
                    ;
        const axisY = d3.axisLeft(scaleY)
                        .ticks(15)
                        .tickFormat(function (d) {
                            return d / 10000 + "万";
                        })
                        .tickSize(-500); 
        const gX = svg.append("g")
            .attr("transform",`translate(50,550)`)
            .classed("xAxis",true);
        axisX(gX);
        const gY = svg.append("g")
                    .attr("transform",`translate(50,50)`)
                    .classed("yAxis",true);
        axisY(gY);
        const circle = svg.selectAll("circle")
                    .data(newTaipei)
                    .join("circle");
        const joinCircle = circle.data(newTaipei).join("circle");
        joinCircle.attr("cx",d=>(scaleX(d.area)))
                    .attr("cy",d=>(scaleY(d.people_total)))
                    .attr("r",5)
                    .attr("fill","red")
                    .attr("transform", "translate(50,50)");            
        let gText = svg.append("g");
        gText.attr("transform",`translate(60,50)`);
        let joinText = gText.selectAll("text").data(newTaipei).join("text");
        joinText
            .text((d) => {
            return d.site_id;
            })
            .attr("x", (d, i) => {
                return scaleX(d.area);
            })
            .attr("y", (d) => {
                return scaleY(d.people_total);
            })
            .attr("font-size",9);
    });

本日GithubPage

tickSize()函式

人口数与土地面积散布图


<<:  t test vs ANOVA

>>:  Day 23 Selenium模组二

Day2 - P5基本操作 - 基本设定跟操作

其实为什麽P5这麽多人喜爱 阅读性真的很高,举例来讲好了 一个基本的设定 首先你先设定 setup ...

30天零负担轻松学会制作APP介面及设计【DAY 24】

大家好,我是YIYI,今天我要来聊聊为什麽要参加这次的IT30。 很多人一定觉得很奇怪,这不都是前几...

Day28:每天一个小练习 - JS30-13-Slide in on Scroll

参考资料: Alex老师教学 PJCHENder笔记 卷动出现图片。 题目预设的过滤效果,避免多次触...

【Day30】Pixi-ParticleContainer+结语

PIXI.ParticleContainer 今天介绍用PIXI.ParticleContainer...

CSV Import

CSV Import CSV Import 是在执行大量资料导入时一个非常常用的功能. 今天我们已更...