整理资料的API们:Array、Time Formats、Number Formats、Random
上一篇介绍完怎麽取得不同格式的资料後,接着要来看的是:怎麽把资料整理或筛选出我们想要的内容~
由於大多数的情况下,使用 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
⇒ 取最小值/最大值这两个方法跟 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)
,只要参数没有用 [ ] 括起来,就代表是必须填入的参数,只要没填就会报错
这个方法跟 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]
阵列最常用的方法就介绍到这边~接着我们来看看另外一个类型吧!
除了整理阵列资料之外,有时候我们的图表会需要带上一些日期、时间等数据资料,这时候就需要 转换日期或时间的API
啦~ 我个人认为时间或日期的转换实在颇复杂,还好 D3 有一些内建好的API 可以使用。
一开始看到 Time Format 的官方文件时,会发现大部分的API 都写着:alias for _____ on the default local. 这是因为D3 v3 以前的版本是用 locale 这个 API 在处理日期、时间、语言的转换,而 v4 之後的版本将它重新改名变成 Time Formats 系列,但它们的方法都是一样的。
这边一样介绍几个比较常用到的API,有其他需求的人可以直接上 Time Formats 官方文件 去找自己想使用的方法。我自己比较常用的API 是:
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 」篇章会有实际的应用~
除了 Time Formats 之外,还有另外一个分类的 Number Formats 专门在处理数字格式的转换。其实数字并不需要经过特殊处理就能直接使用在d3.scale 的 API 中,但如果希望呈现特定的数字格式,就可以用 D3 Time Formats 提供的 API 快速处理,方便许多~
Time Formats 的 API 包含以下几种:
我自己偶尔会用到的就是 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) |
最後再讲另一类偶尔会用到的API: D3 Random Numbers 。D3 Random Numbers 这类的方法主要是快速产生一些乱数以供我们使用。
D3 一样提供了超级多乱数相关的API,以下介绍比较常用到的:
d3.randomInt
⇒ 产生一个随机整数我们一样先来看看官方文件对这个 API 的解释
文件上说明,d3.randomInt 这个 API 会回传一个方法,我们可以使用这个方法在指定的范围生出一个随机整数。至於怎麽设定乱数的范围呢?这个 API 可以带入两个参数,这两个参数分别代表着范围的最小值与最大值,举例来说,如果我们想要一个介於50跟100之间的随机整数:
//使用 API 设定范围
d3.randomInt(50,100)
// 使用方法生成乱数
d3.randomInt(50,100)() //76
这样就能成功得到一个随机整数了,是不是非常简单呢?
好啦!整理资料的 API 就讲到这边~常用到的我都已经搜集在这篇文了,如果有没提到但你又需要使用的 API,建议直接去看官方文件哦~会省下很多东查查西查查的时间。如果还不太知道这些 API 要用在哪边也没关系,等到之後实作的篇章时,就更清楚该如何去使用~今天就先讲到这边啦,我们明天见!
最後附上本章的程序码与图表 Github 、 Github Page,今天的页面一样要开启devTool 来看console 内容哦,需要的人请自行取用~
<<: Day 5 - 用 canvas 复刻 小画家 挑选颜色(颜色选取器)
本系列文章同步发布於笔者网站 前一篇文章以比较非技术角度介绍了 OpenStack 这个专案。今天开...
前几天我们使用 terraform 配置好 gitlab 环境, 会单纯使用 gitlab 来管理程...
进行专案常需要把从API获取的资讯转成阵列,把阵列里的每项物件资料,用回圈套在元件上,依序渲染多个元...
「鲑鱼均,因为一场鲑鱼之乱被主管称为鲑鱼世代,广义来说以年龄和脸蛋分类的话这应该算是一种 KNN 的...
前言 如果有接触过 Redux 的话,应该会对这个 hook 有亲切感。如果是像我一样没有接触的话也...