Day8-D3 资料整理的API们:Array、Time Formats、Number Formats、Random

整理资料的API们:Array、Time Formats、Number Formats、Random

上一篇介绍完怎麽取得不同格式的资料後,接着要来看的是:怎麽把资料整理或筛选出我们想要的内容~

D3 Array

由於大多数的情况下,使用 d3 的 API 时都必须带入阵列格式的资料,因此 d3 Array 这个分类也提供最多能协助我们整理资料的API ,先来看看 array 分类中包含的子项目:

  • Statistics 统计数据
    用来运算基本的数据,常用的 API 有: d3.min、d3.max、d3.extent、d3.sum

  • Search 寻找
    用来搜寻阵列给指定的DOM元素使用,常用的 API 有:d3.ascending、d3.descending

  • Transformations 改变结构
    用来改变阵列并生成一个新的阵列,常用的 API 有:d3.merge、d3.range

  • Iterables 迭代
    常用的 API 有: d3.every、d3.some、d3.map、d3.filter、d3.sort

  • Sets 数组
    比较多组资料集的交集/差集状态,并根据使用的 API 返还一个物件

  • Histograms 直条图
    把离散的资讯变成连续、不重叠的整数,来产生可以绘制直条图的数据资料

  • Interning
    使用阵列的key或value

D3 Array 包含非常多API,涵盖几乎所有阵列相关的资料处理,有兴趣的人可以自行上官方文件查找,我这边只挑几个最常用到的来介绍:

  • d3.min
  • d3.max
  • d3.extent
  • d3.sum
  • d3.every
  • d3.some
  • d3.filter
  • d3.sort

d3.mind3.max ⇒ 取最小值/最大值

这两个方法跟 JS 的 Math.min、Math.max 基本上是一样的,都是在一个阵列中找出最小或最大值,但有一个细微的差异是,d3.min/max 会忽略掉 undefined, null 或 NaN 的值。我们直接看到以下范例:

const data = [7, 5, 1, 13, 55, 2, 64, null];
const min = d3.min(data);
const max = d3.max(data);
console.log(min, max) // 1, 64

要特别注意的是,这两个方法只能用在比较数字,如果阵列中的内容是字串的话,使用这两个方法虽然不会报错,但你只会得到随机的两个值,如下

const dataString = ['狗', '猫', '羊', '猪']
const min = d3.min(dataString )
const max = d3.max(dataString );
console.log(min, max) // '狗' '猫'

d3.extent ⇒ 同时返回最大与最小值

这是超级方便的一个方法,能同时把资料中的最小值与最大值挑出来,并返还成一个阵列。这个方法通常在设定 scale 那边会用到,等之後到了 scale 的章节会再讲解~

const data = [7, 5, 1, 13, 55, 2, 64, null];
const extent = d3.extent(data); // [1, 64]

d3.sum ⇒ 把资料加总

这个方法会把传入的资料阵列都加总起来,并返还加总的数值;如果资料阵列中没有可以加总的数值的话 (例如全部都是字串),就会返还0。

// 数字可以加总
const data = [7, 5, 1, 13, 55, 2, 64, null];
const sum = d3.sum(data)
console.log(sum); // 147

// 字串无法加总
const dataString = ['狗', '猫', '羊', '猪']
const sumString = d3.sum(dataString)
console.log(sumString);  // 0

d3.every ⇒ 遍历资料阵列,确认阵列内的值是否全都符合条件

每次遇到新的API时,我都会先去查官方文件的资料,确认是否有必须要带入的参数。我们这次一样看到官方文件的 API 设定,文件上写着 # d3.some(iterable, test) ,只要参数没有用 [ ] 括起来,就代表是必须填入的参数,只要没填就会报错
https://ithelp.ithome.com.tw/upload/images/20210920/20134930QZD4GtPFqE.jpg

这个方法跟 JS 的 array.every 很相似,一样需要带入两个参数:d3.every ( 资料, 方法 ),带入的这个方法会遍历带入的资料阵列,如果阵列内的每个数都符合设定的条件,就会回传true;如果其中任何一个数不符合条件,则会回传false

// 全部的数字都非空值
const data = [7, 5, 1, 13, 55, 2, 64];
const every = d3.every(data, d=>d)
console.log(every);  // true 

// 全部的数字都大於20
const data = [7, 5, 1, 13, 55, 2, 64];
const every = d3.every(data, d => d > 20)
console.log(every);  // false

d3.some ⇒ 遍历资料阵列,确认阵列内的任一资料是否符合条件

跟 d3.every 一样是带入两个参数:d3.some( 资料, 方法 ),但用法刚好相反;带入的这个方法会遍历资料阵列,如果阵列内的任一资料符合设定的条件,就会回传true;如果全部都不符合,才会回传false

// 其中任意一个数字大於20
const data = [7, 5, 1, 13, 55, 2, 64];
const some = d3.some(data, d => d > 10)
console.log('some',some); // true

d3.filter ⇒ 遍历资料阵列,返还阵列内所有符合条件的资料

这个方法跟 array.filter 很相似,一样是带入两个参数:d3.filter( 资料, 方法 ),在带入的方法内设定条件,并设定只返还所有符合条件的资料。

const data = [7, 5, 1, 13, 55, 2, 64];
const filter = d3.filter(data, d=>d>10)
console.log('filter',filter); // [13, 55, 64]

d3.sort ⇒ 按照条件排序资料阵列

这个方法是用来整理并排序阵列中的资料,它一样要带入两个参数:d3.sort( 资料, 方法 ),比较特别的是,如果参数没有带入方法的话,d3.sort就会自动使用 d3.ascending 当作它的参数,并按照 d3.ascending 的方法排序阵列 (由小排到大)

const data = [7, 5, 1, 13, 55, 2, 64, null];
const sort= d3.sort(data)
console.log('sort',sort); // [1, 2, 5, 7, 13, 55, 64, null]

阵列最常用的方法就介绍到这边~接着我们来看看另外一个类型吧!


D3 Time Formats

除了整理阵列资料之外,有时候我们的图表会需要带上一些日期、时间等数据资料,这时候就需要 转换日期或时间的API 啦~ 我个人认为时间或日期的转换实在颇复杂,还好 D3 有一些内建好的API 可以使用。
https://ithelp.ithome.com.tw/upload/images/20210920/20134930xHTGqMflbM.jpg

一开始看到 Time Format 的官方文件时,会发现大部分的API 都写着:alias for _____ on the default local. 这是因为D3 v3 以前的版本是用 locale 这个 API 在处理日期、时间、语言的转换,而 v4 之後的版本将它重新改名变成 Time Formats 系列,但它们的方法都是一样的。

这边一样介绍几个比较常用到的API,有其他需求的人可以直接上 Time Formats 官方文件 去找自己想使用的方法。我自己比较常用的API 是:

  • d3.timeParse
  • d3.timeFormate

d3.timeParse ⇒ 将日期等资讯转换成 D3 看得懂的数值

这个方法是 D3 用来处理时间格式的 API。只要是跟时间、日期相关的图表,都要用这个方法先把资料转换成D3能读懂、能够去计算的数值,之後才能去建构图表,通常都是用在设定图表的 scale( ) 或 domain( )。

d3.timeParse( ) 本身是一个方法,我们呼叫它时要带入特定的参数 (符合要处理的资料格式);之後 d3.timeParse( ) 会回传另一个方法,我们再接着使用这个回传的方法去计算要带入的资料,实际运作如下:

const timeData = '2021-09-07'
const timeParse = d3.timeParse('%Y-%m-%d')
timeParse(timeData) // Tue Sep 07 2021 00:00:00 GMT+0800 (台北标准时间)

要注意的是,这边带入的参数格式一定要符合资料的格式哦,如果资料格式是 "2021/09/07",参数也要改成 d3.timeParse ('%Y/%m/%d'),否则你就会得到 null 的结果。

至於可以带入那些参数呢?我常用的基本上有 (按照常用顺序排列):

参数 格式
%Y 西元年
%y 西元年最末的两位数
%m 一年的某一个月 ( 01 到 12 )
%d 一月的某一天 (1 到 31)
%j 一年的某一天 ( 001 到 366 )
%B 月份
%b 月份的缩写
%A 星期几
%a 星期几的缩写

其他的就比较没那麽常用,如果有需要或是想看详细版的人,可以上官方文件 locale.format 去查找~

d3.timeFormat ⇒ 将D3的日期时间数值转换成我们看得懂的文字

d3.timeFormat( ) 的写法跟 d3.timeParse( ) 是一样的,只是作用刚好相反。d3.timeFormat( ) 是把 D3能处理的数值,转成我们看得懂的文字,通常都用在 axis( ) 跟 ticks( ) 画轴线、标示刻度时使用。

这个 API 通常是在画轴线、标示刻度时使用,写法跟 d3.timeParse( ) 稍微不同的地方是 d3.timeFormat( ) 可以自己设定转换後日期的显示格式,所以就不用按照原始资料的日期格式来进行参数配置。直接看例子

// Time Formats 转换成 D3 看得懂的数值
const timeData = '2021-09-07'
const timeParse = d3.timeParse('%Y-%m-%d')
const parsedData = timeParse(timeData)
console.log(parsedData);  // 得到D3看得懂的数值

// Time Formate 转换成人类看得懂的数值
const timeFormat = d3.timeFormat('%Y/%m/%d') // 转换後想变成用 "/" 分隔
console.log(timeFormat(parsedData)) // 2021/09/07

有关 Time Formats 的方法目前先了解这样就够了,之後的「轴线与刻度 Axis & ticks 」篇章会有实际的应用~


D3 Number Formats

除了 Time Formats 之外,还有另外一个分类的 Number Formats 专门在处理数字格式的转换。其实数字并不需要经过特殊处理就能直接使用在d3.scale 的 API 中,但如果希望呈现特定的数字格式,就可以用 D3 Time Formats 提供的 API 快速处理,方便许多~

Time Formats 的 API 包含以下几种:
https://ithelp.ithome.com.tw/upload/images/20210920/20134930Y9wtCM5FOl.jpg

我自己偶尔会用到的就是 d3.format,而且通常也是在处理 Axis 轴线时使用。

d3.format ⇒ 转换数字格式

d3 number format 系列跟 d3 Time Format 很相似,在v3版以前都是归类在local 下方的,也因此如果你查官方文件的话,会被指引到 local.format 的页面。

d3.format( ) 一样是要带入参数,参数填入欲设定的数字格式,接着呼叫此API後会回传一个方法,透过这个方法就能将资料转成我们想要的格式

// Number Formate
const originNumber = 10
const numberFormat = d3.format('b') // 参数 b 是指二进位制
console.log(numberFormat(originNumber)); // 1010

至於哪些参数分别代表什麽样的格式呢? 这边列出几个最常用的,如果想看详细参数设定,可以参考这边

参数 格式 范例
d 返回这个数字的字串格式,忽略任何非整数值 d3.format('d')(12.35)
g 指定位数 (参数前需要填入几位数) d3.format('2g')(120)
f 指定小数点後的位数( 参数前需要先填上几位数) d3.format('.2f')(3.14159)

D3 Random

最後再讲另一类偶尔会用到的API: D3 Random Numbers 。D3 Random Numbers 这类的方法主要是快速产生一些乱数以供我们使用。
https://ithelp.ithome.com.tw/upload/images/20210920/20134930glpwHspQsx.jpg

D3 一样提供了超级多乱数相关的API,以下介绍比较常用到的:

d3.randomInt ⇒ 产生一个随机整数

我们一样先来看看官方文件对这个 API 的解释
https://ithelp.ithome.com.tw/upload/images/20210920/20134930JoPqgyUCC9.jpg

文件上说明,d3.randomInt 这个 API 会回传一个方法,我们可以使用这个方法在指定的范围生出一个随机整数。至於怎麽设定乱数的范围呢?这个 API 可以带入两个参数,这两个参数分别代表着范围的最小值与最大值,举例来说,如果我们想要一个介於50跟100之间的随机整数:

//使用 API 设定范围
d3.randomInt(50,100)

// 使用方法生成乱数
d3.randomInt(50,100)() //76

这样就能成功得到一个随机整数了,是不是非常简单呢?

好啦!整理资料的 API 就讲到这边~常用到的我都已经搜集在这篇文了,如果有没提到但你又需要使用的 API,建议直接去看官方文件哦~会省下很多东查查西查查的时间。如果还不太知道这些 API 要用在哪边也没关系,等到之後实作的篇章时,就更清楚该如何去使用~今天就先讲到这边啦,我们明天见!


Github Page 图表与 Github 程序码

最後附上本章的程序码与图表 GithubGithub Page,今天的页面一样要开启devTool 来看console 内容哦,需要的人请自行取用~


<<:  Day 5 - 用 canvas 复刻 小画家 挑选颜色(颜色选取器)

>>:  DAY 08 让Linebot回覆特定讯息

OpenStack 介绍 2

本系列文章同步发布於笔者网站 前一篇文章以比较非技术角度介绍了 OpenStack 这个专案。今天开...

EP06 - 从零开始,在 AWS 上建置 Jenkins 使用 Terraform

前几天我们使用 terraform 配置好 gitlab 环境, 会单纯使用 gitlab 来管理程...

Day20 React 回圈渲染多个元件

进行专案常需要把从API获取的资讯转成阵列,把阵列里的每项物件资料,用回圈套在元件上,依序渲染多个元...

Youtube API - 简介与建立 Google Cloud Platform 专案

「鲑鱼均,因为一场鲑鱼之乱被主管称为鲑鱼世代,广义来说以年龄和脸蛋分类的话这应该算是一种 KNN 的...

Re: 新手让网页 act 起来: Day19 - React Hooks 之 useReducer

前言 如果有接触过 Redux 的话,应该会对这个 hook 有亲切感。如果是像我一样没有接触的话也...