d3学习第二集-scale,datajoin,画布的margin,建立简单散点图

发布时间 2023-09-28 10:34:27作者: matthew's_follower

来自observable官方教程:https://www.youtube.com/playlist?list=PLOHIJAFwtkEcK_mLLScnX2B-yHDjzSxuR

本文主要讲
scale:定义一个数学函数将一种数据转化成另一种数据,比如把连续变量转化成离散变量,将0-1的实数,转化成1-100的实数,d3中的通常有scaleLinear, scaleOrdinal
建立一个可以有margin的画布:画图的时候坐标轴很可能会需要在保留的边框上画(可能我理解错了,错了的话,以后修改)

1.散点图通常需要些什么

坐标轴(和数据有关)、点(和数据有关)、标签
scale、画布、样式、数据

2.scale

用来定义一个数学函数将一种数据转化成另一种数据,比如把连续变量转化成离散变量,将0-1的实数,转化成1-100的实数,d3中的通常有scaleLinear, scaleOrdinal

创建scale的方式有两种

第一种:d3.scaleLinear().domain(自变量范围).range(因变量范围)

//LS=f(n),f(n)=100*x
let LS=d3.scaleLinear().domain([0,1]).range([0,100])
//a=2000 超过的范围时候也会计算
let a=LS(20)
//os返回一个函数,当输入是apple返回red,orange返回orange,banana返回yellow,输入其他会进入循环,比如第一次输出的是yellow,下一次使用os("lovelyMe"),就会返回red
let os=d3.scaleOrdinal()
  .domain(["apple", "orange", "banana"])
  .range(["red", "orange", "yellow"])
//返回red
let a=LS(apple)

在html使用建立的scale函数:

htl.html`<div style='background-color: ${myOrdinalScale(
  "banana"
)}'>${"banana"}</div>`

第二种:d3.scaleLinear(自变量范围,因变量范围)

let LS=d3.ordinalLinear([0,1],[0,100])
let LS=d3.scaleLinear(["apple", "orange", "banana"],["red", "orange", "yellow"])

根据数据自动获取自变量范围

A.获取连续型变量d3.extent(数值列表),或者d3.extent(对象列表,属性函数)
let sL=d3.extent(iris.map(d=>d.sepalLength))
let sL1=d3.extent(iris,d=>d.sepalLength)

返回的都是[min,max]
另外d3也有min,max函数,同样可以直接加数值列表,亦可以加(对象列表,属性函数)
要注意一下,以下写法是错误的

let sL1=d3.extent(iris,"sepalLength")

B.获取离散变量的范围,用map函数获取列表,然后转化成set去重,然后转化成列表

用map获取列表

let sLExtent=iris.map(x=>x.sepalLength)

转化成set去重,获得{a,b,c}这样的set

sLExtent=New Set(sLExtent)

转化成列表,两种方式set.ArrayFrom或者展开语法[...set]
最后的格式也就是

let sLExtent=[...New Set(iris.map(x=>x.sepalLength))]

获得的extent可以加到d3.scaleLinear(自变量范围,因变量范围)的自变量范围里面

3.画散点图

A.构建画布和容器

画布

let svg=d3.create("svg")
.attr("width",width+margin.left+margin.right)
.attr("length",length+margin.top+margin.bottom)
.style("border", "1px dotted #000");

style可以加可以不加,主要需要确定长度和宽度,定义的宽高实际上是容器的宽高,所以建立画布的时候需要加上margin的尺寸
容器
然后在顶上加一个group,并平移left和top,然后就可以画图,因为点的范围都是在width和length之内,所以不用害怕下面超过边界

const g = svg
    .append("g")
    .attr("transform", `translate(${margin.left}, ${margin.top})`);

另外需要注意的是给点定纵向高度范围的时候是反过来的

B.数据绑定点的过程

目标是数据绑定点,数据是列表,点则可以通过选择,然后确定svg中的元素,然后得到一个列表,数据绑定的过程就是比较两个列表数量,如果相同就更改点的位置,不同就添加或删除点然后更新点的位置
所以整个过程的第一步是在建立的svggroup里面选择点select("circle"),如果svg上没有点也是要进行这一步
之后有三个过程enter、update、exit,不过不是今天的内容
新版本有一个join函数可以实现上述所有功能,但不是说上面的功能可以不了解,因为一些交互的时候还是需要精细调整过程
一下是一个简单的用法

  g.append("g")//建立点集
    .selectAll("circle")//不管有没有直接选择
    .data(iris)//给这个集合iris数据
    .join("circle")//将每个点和iris相对应,如果少添加点,如果多删除点
    .attr("r", 3)//设置圆的半径
    .attr("cx", (d) => x(d.sepalLength))
    .attr("cy", (d) => y(d.sepalWidth))//设置圆的中心
    .style("fill", (d) => colorScale(d.species));//设置圆的样式

C.创建一个散点图

步骤:建立画布,设置数据范围和高度的映射关系,将数据绑定到图上面

//数据准备(可以最后缺啥补啥)
//建立画布
let svg=d3.create("svg")
.attr("width",width+scale.left+scale.right)
.attr("height",width+scale.top+scale.bottom)
let g=svg.append("g").attr("transform",`translate(${margin.left},${margin.top}`)
//设置数据范围和宽度高度的映射关系
let x=d3.linearScale(iris.map(x=>x.spedalHeight)),[0,width])
let y=d3.linearScale(d3.extents(iris.map(x=>x.spedalWith)),[height,0])
let color=d3.ordinalScale([...New Set(iris.map(x=>x.species))],["red","orange","yellow"])
//绑定数据
g.append("g")
.selectAll("circle")
.data(iris)
.join("circle")
.attr("r",)
.attr("cx",d=>x(d.sepalLength))
.attr("cy",d=>y(d.sepalWidth))
.attr("color",d=>color(d.species))
//在observable中还要返回svg.node()
return svg.node()