数组
当你使用D3处理数据可视化时,通常会倾向于做大量的数组操作(array manipulation)。那是因为数组是D3的标准的数据呈现形式。数组处理的一些常见形式包括:取数组的一个连续片段(子集),使用判定函数过滤数组,使用变换函数映射数组为一组平行的值。在看到D3框架提供的一系列方法处理数组时,你应当很熟悉强大的JavaScript内置的数组的方法.
JavaScript包含修改数组的赋值方法(mutator methods):
- array.pop -删除数组最后一位元素。
- array.push - 往数组的末尾新增一个或多个元素。
- array.reverse - 把数组元素的逆转顺序。
- array.shift - 删除数组第一位元素。
- array.sort - 给数组排序。
- array.splice - 给数组添加或者删除元素。
- array.unshift - 往数组的第一位新增一个或多个元素。
还有一些数组的存取方法(accessor methods),返回数组的一些描述:
- array.concat - 合并数组或合并数组的值。
- array.join - 合并数组所有元素拼接成字符串。
- array.slice - 提取数组的一个选择。
- array.indexOf - 定位到数组第一个值。
- array.lastIndexOf - 定位到数组内最后一个值。
最后,对数组中的元素使用用函数的迭代方法(iteration methods):
- array.filter - 由满足特定条件的元素创建一个新的数组。
- array.forEach - 为数组中每一个元素调用一个函数。
- array.every - See if every element in the array satisfies a predicate.
- array.map - Create a new array with the result of calling a function on every element in the array.
- array.some - See if at least one element in the array satisfies a predicate.
- array.reduce - Apply a function to reduce the array to a single value (from left-to-right).
- array.reduceRight - Apply a function to reduce the array to a single value (from right-to-left).
排序 (Ordering)
# d3.ascending(a, b)
如果a < b
返回-1,a > b
返回1,a = b
返回0。 这是固有的比较器方法,也可用于关联内置数组排序的方法来给元素升序排序:
function ascending(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
注意,如果没有给数组的内置排序方法没有指定比较器函数,那默认的排序是字典排序(按字母顺序排序),而非自然排列!所以当以数组的数字来排序时会导致bug。
# d3.descending(a, b)
如果a > b
返回-1,a < b
返回1,a = b
返回0。 这是固有的比较器方法,也可用于关联内置数组排序的方法来给元素降序排序:
function descending(a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
注意,如果没有给数组的内置排序方法没有指定比较器函数,那默认的排序是字典排序(按字母顺序排序),而非自然排列!所以当以数组的数字来排序时会导致bug。
# d3.min(array[, accessor])
返回给定数组(array)中自然排序最小的值。如果数组为空,返回undefined。如果指定了accessor 参数,等同与在计算最小值之前调用了array.map(accessor)方法。不同于内置的Math.min,这个方法会忽略未定义的值;这对比例尺([[d3.scale|比例尺]])定义域计算很有用处,当只考虑数据的定义区域。另外,元素的比较用的是自然排序而不是数字排序。例如,["20","3"]的最小值是20,然而[20,3]的最小值是3。
# d3.max(array[, accessor])
返回给定数组(array)中自然排序最大的值。如果数组为空,返回undefined。如果指定了accessor 参数,等同与在计算最大值之前调用了array.map(accessor)方法。而并非内置的Math.max,这个方法会忽略未定义的值;这对当只需要定义数据的区域的比例尺定义域计算很有用处。另外,元素的比较用的是自然排序而不是数字排序。例如,["20","3"]的最大值是3,然而[20,3]的最大值是20。
# d3.extent(array[, accessor])
返回给定数组(array)自然排序的最小值和最大值,等同于同时调用d3.min和d3.max.
# d3.sum(array[, accessor])
返回给定数组(array)的和。如果数组为空,返回 0。可选参数accessor函数 被指定,等同于在计算和之前调用array.map(accessor) 。此方法忽略无效值(如NaN
和undefined
);当只考虑明确定义的值时,这个方法可用于计算数据的和。
# d3.mean(array[, accessor])
返回给定数组(array)的平均数。如果数组为空,返回 undefined
。可选参数accessor函数 被指定,等同在计算平均数之前调用array.map(accessor) 。此方法忽略无效值(如NaN
和undefined
),当只考虑明确定义的值时这个方法计算数据和是很有用的。
# d3.median(array[, accessor])
返回给定数组(array)以R-7算法得出的中位数。如果数组为空,返回 undefined。可选参数accessor 被指定,等同在计算中位数之前调用array.map(accessor) 。此方法忽略无效值(如NaN
和undefined
) ,当只考虑明确定义的值时这个方法计算数据和是很有用的。
# d3.quantile(numbers, p)
返回给定数组numbers的p分位数,其中p 是一个0到1范围的数。例如,中位数可以由p = 0.5计算,第一个四分位数是p = 0.25,第三个四分位数是p = 0.75。这个特别实现了R-7算法,这是R编程语言和Excel默认的方式。这个方法需要数组numbers包含数字且数字升序顺序排列,例如使用 d3.ascending排序。
var a = [0, 1, 3];
d3.quantile(a, 0); // return 0
d3.quantile(a, 0.5); // return 1
d3.quantile(a, 1); // return 3
d3.quantile(a, 0.25); // return 0.5
d3.quantile(a, 0.75); // return 2
d3.quantile(a, 0.1); // return 0.19999999999999996
# d3.variance(array[, accessor])
返回给定数组(array)的无偏总体方差(unbiased estimator of the population variance)。如果数组的长度小于2,返回undefined
。可选参数accessor 被指定,等同在计算中位数之前调用array.map(accessor) 。此方法忽略无效值(如NaN
和undefined
)。
# d3.deviation(array[, accessor])
返回给定数组(array)的标准差,即方差(bias-corrected variance)的平方根。如果数组的长度小于2,返回undefined
。可选参数accessor 被指定,等同在计算中位数之前调用array.map(accessor) 。此方法忽略无效值(如NaN
和undefined
)。
# d3.bisectLeft(array, x[, lo[, hi]])
定位数组 array 中的 x 的插入点,以保持已有序列。参数 lo 和 hi 用来指定数组的子集;默认情况下整个数组都被使用。如果 x 在 array 中已存在,插入点在所有元素之前(左侧)。返回值适合用作拼接(splice)已经排序的数组array 的第一个参数。返回的插入点i把array 分为两个区:数组中所有array.slice(lo, i)中v < x
的v在左边,数组中所有 array.slice(i, hi)中v >= x
的v在右边。
# d3.bisect(array, x[, lo[, hi]])</br> # d3.bisectRight(array, x[, lo[, hi]])
和bisectLeft类似,但返回插入点来自于数组array中任意实体x之后(右侧)。返回的插入点i把array 分为两个区:数组中所有array.slice(lo, i)中v <= x
的v在左边,数组中所有 array.slice(i, hi)中v > x
的v在右边。
# d3.bisector(accessor)
# d3.bisector(comparator)
使用指定参数accessor或者comparator 函数返回一个二等分线。返回的对象有left
和right
属性,分别类似于bisectLeft和bisectRight方法。这个方法能用于二等分对象数组而不适用于原始的简单数组。例如下列对象的数组:
var data = [
{date: new Date(2011, 1, 1), value: 0.5},
{date: new Date(2011, 2, 1), value: 0.6},
{date: new Date(2011, 3, 1), value: 0.7},
{date: new Date(2011, 4, 1), value: 0.8}
];
一个合适的二等分函数可定义为:
var bisect = d3.bisector(function(d) { return d.date; }).right;
然后调用bisect(data, new Date(2011, 1, 2))
,返回索引。 如果你想使用不同于自然排序的方法对值进行排序,那么可以使用比较器(comparator)而不是访问器(accessor),例如降序排序而不是升序排序的时候。
var bisect = d3.bisector(function(a, b) { return a.date - b.date; }).right;
# d3.shuffle(array[, lo[, hi]])
使用Fisher–Yates shuffle来把传入参数array随机排序.
关联数组 (Associative Arrays)
关联数组(字典)和数组类似,由以名称作为键的字段和方法组成。 它包含标量数据,可用索引值来单独选择这些数据,和数组不同的是, 关联数组的索引值不是非负的整数而是任意的标量。这些标量称为Keys,可以在以后用于检索数组中的数值. JavaScript 中另一种常见数据类型就是关联数组,或者简单说就是具有一系列命名属性的对象。在Java中简称映射(键值对) map,而在Python中称为字典dictionary。JavaScript为关联数组中键(属性名称)的迭代提供一个标准机制:那就是 for…in loop。然而,注意迭代的次序是未定义的。D3提供了一些将关联数组转化为索引数组的方法。
# d3.keys(object)
返回一个包含指定对象(关联数组) 属性名称的数组。返回数组的顺序未定义。
# d3.values(object)
返回一个包含指定对象(关联数组) 属性值的数组。返回数组的顺序未定义。
# d3.entries(object)
返回一个包含对象(object)(一个关联数组)中名称以及值(键和值, key and value)的数组 (array)。每一个实体都是有键值对的对象,例如{key: "foo", value: 42}
。返回数组的顺序未定义。
d3.entries({foo: 42, bar: true}); // returns [{key: "foo", value: 42}]
映射(Maps)
当你尝试在JavaScript中用空对象作为map,当内部属性名称(键)作键时,会导致意外的行为(unexpected behavior)。比如,当你设置object["__proto__"] = 42
时,最终不会达到你理想中的结果。又如你尝试查询给定key是否在map中定义了;"hasOwnProperty" in object 返回true,因为空对象(从对象原型)继承了hasOwnProperty方法。为避免这些问题,ES6提出了简单映射和集合(simple maps and sets) 理论。直到现代浏览器支持了这些集合,你可以使用[d3.map]替代.
注意:不同于建议的 ES6 map ,d3.map的key仍然强制使用字符串,而不是严格的相等。
# d3.map([object][, key])
构建一个新的map,如果指定参数object,复制参数object对象内所有枚举属性到map中。参数对象可能是数组. 可以使用一个键key函数来计算数组里每个数值的键. 如下:
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
m.get("foo"); // {"name": "foo"}
m.get("bar"); // {"name": "bar"}
m.get("baz"); // undefined
参见 d3.nest。
# map.has(key)
当且仅当map有指定key的实体时返回true。注意:该值可能是null
或undefined
。
# map.get(key)
返回参数key的值。如果map中没有参数key相同元素,返回undefined
。
# map.set(key, value)
指定key的value ;返回新的value。如果map之前同样的key 有一个实体了,那么旧实体被新值替代。
# map.remove(key)
若map有指定key的实体,删除此实体并返回true
。否则,此方法不做任何操作,返回false
。
# map.keys()
返回在这个map所有的键的数组。返回键的集合顺序是随机的。
# map.values()
返回在这个map所有的值的数组,返回值的集合顺序是随机的。
# map.entries()
返回一个map内所有键-值对象的数组。返回元素的集合顺序是随机的。任何元素的键必须是字符串类型,但值可为任何类型。
# map.forEach(function)
给map中每个元素调用一个指定函数function,传递元素的键和值作为两个参数。function的使用的this
指针将指向这个map。返回undefined
。迭代的顺序是随机的。
# map.empty()
返回true
当且仅当map中没有元素。
# map.size()
返回map中元素的个数
集合(Sets)
# d3.set([array])
新建一个集合,如果指定了array ,添加array 的字符串值到返回集合中。
# set.has(value)
当且仅当集合中具有指定参数value 字符串相同的实体,返回true
。
# set.add(value)
添加指定参数value 字符串到集合中.
# set.remove(value)
如果集合中含有指定参数value 字符串相同元素,返回true
并删除元素。否则,这个方法不做任何操作,并返回false
。
# set.values()
返回一个由集合中所有字符串类型值组成的数组。数组中的值的顺序为随机的。可作为集合中唯一值的简便计算方法(去重)。例如:
d3.set(["foo", "bar", "foo", "baz"]).values(); // "foo", "bar", "baz"
# set.forEach(function)
给集合中每个元素调用一个指定function,传递元素的值作为参数。function的使用的this
指针将指向这个map。返回undefined
,迭代的顺序是随机的。
# set.empty()
当且仅当集合中没有值,返回true
。
# set.size()
返回集合中值的个数。
数组运算符 (Array Operators)
# d3.merge(arrays)
合并指定参数arrays 为一个数组,此方法和数组内置方法concat
类似;唯一不同是当你要处理二维数组时,d3.merge(arrays)
方法更方便。
d3.merge([ [1], [2, 3] ]); // returns [1, 2, 3]
# d3.range([start, ]stop[, step])
生成一个包含算数级数的数组,类似于Python的内置函数range。这个方法常用来遍历一个数字序列或者整型数值。例如数组中的索引。不同于Python版本,这个参数不必是整形。尽管如果它们是浮点精度类型时这个结果更加可预测。如果省略step,默认值是1。如果省略start参数,默认值就是0。结果中不包含stop值。完整的形式是返回一个数字数组[start,start+step,start+2 *step,…]
。如果step是正的,则最后一个元素是小于stop
的start+ i*step
中的最大数值;如果step是负的,最后一个元素是大于stop
的start + i*step
中的最小数值。如果返回的数组将包含值无限多数字,就会抛出一个错误,而不是造成无限循环。
# d3.permute(array, indexes)
使用指定的indexes数组返回指定数组的转置。返回数组包含indexes数组中索引对应的元素,按顺序。例如,permute(["a", "b", "c"], [1, 2, 0])
返回 ["b", "c", "a"]
。indexes数组的长度和array中的元素长度不一样是可以接受的,并且允许indexes数组重复或者省略。 这个方法可以用来按固定顺序提取对象中的值到一个数组中。(在JavaScript中indexes数组是和.length
有特殊关系的简单属性)。按顺序提取带键的值可以用来生成嵌套选择中的数据数组。例如,我们可以用表格形式展示上述的一些明尼苏达州大麦产量数据:
var cols = ["site", "variety", "yield"];
thead.selectAll('th').data(cols)
.enter().append('th').text(function (d) { return d.toUpperCase(); });
tbody.selectAll('tr').data(yields)
.enter().append('tr').selectAll('td').data(function (row) { return d3.permute(row, cols); })
.enter().append('td').text(function (d) { return d; });
# d3.zip(arrays…)
返回的数组的数组,其中,第i个数组包含来自每个arrays参数的第i个元素。返回的数组长度被截断为arrays的最短的数组的长度。如果arrays只包含一个数组,则返回的数组是包含一个元素的数组。不带任何参数,则返回的数组是空的。
d3.zip([1, 2], [3, 4]); // returns [[1, 3], [2, 4]]
# d3.transpose(matrix)
等价于d3.zip.apply(null, matrix)
;使用zip操作符作为二维矩阵变换(matrix transpose)。
# d3.pairs(array)
对指定参数array中元素的每个相邻对,返回元组(元素i和元素i-1)的新数组。例如:
d3.pairs([1, 2, 3, 4]); // returns [[1, 2], [2, 3], [3, 4]]
如果指定参数array 中少于两个元素,则返回一个空数组。
嵌套 (Nest)
嵌套允许数组中的元素被组织为分层树型结构;类似SQL语句里面的GROUP BY方法,但不能多级别分组,而且输出的结果是树而不是一般的表。树的层级由key方法指定。树的叶节点可以按值来排序,而内部节点可以按键来排序。可选参数汇总(rollup)函数可以使用加法函数瓦解每个叶节点的元素. nest 操作符(d3.nest返回的对象)是可以重复使用的,不保留任何嵌套数据的引用。
例如,思考下面1931-2年间明尼苏达州(美国州名)麦田地皮的表格数据结构:
var yields = [{yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm"},
{yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca"},
{yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris"}, ...]
为了方便查看,可以嵌套元素首先按year然后按variety,如下:
var nest = d3.nest()
.key(function(d) { return d.year; })
.key(function(d) { return d.variety; })
.entries(yields);
返回的嵌套数组中。每个元素的外部数组是键-值对,列出每个不同键的值:
[{key: 1931, values: [
{key: "Manchuria", values: [
{yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm"},
{yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca"},
{yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris"}, ...]},
{key: "Glabron", values: [
{yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm"},
{yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca"}, ...]}, ...]},
{key: 1932, values: ...}]
嵌套的形式可以在SVG或HTML很容易迭代和生成层次结构。
有关 d3.nest
详见:
- Phoebe Bright’s D3 Nest Tutorial and examples
- Shan Carter’s Mister Nester
# d3.nest()
创建一个新操作符。keys集合初始为空。如果map或entries 操作符在任何键函数被注册之前被调用,这个嵌套操作符通常返回输入数组。例如http://bl.ocks.org/phoebebright/raw/3176159/
# nest.key(function)
注册一个新的键函数function。键函数将被输入数组中的每个元素调用,并且必须返回一个用于分配元素到它的组内的字符串标识符。通常,这个函数被实现为一个简单的访问器,如上面例子中year和variety的访问器。 输入的数组的引导(index)并没有传递给function。每当一个key 被注册,它被放到一个内部键数组的末端,和由此产生的map或实体将有一个额外的层级。当前没有一个方法可以删除或查询注册的键。最近注册的键在后续的方法中被当作当前键。
# nest.sortKeys(comparator)
使用指定的参数comparator来给当前键值排序,等效于d3.descending。如果没有指定comparator 参数键的排序则返回undefined。注意:只影响实体操作符的结果;map操作符返回键的顺序永远是undefined,无论什么比较器。
var nest = d3.nest()
.key(function(d) { return d.year; })
.sortKeys(d3.ascending)
.entries(yields);
# nest.sortValues(comparator)
使用指定的comparator参数给叶子元素排序,等效于d3.descending。这相当于在应用nest操作符前给输入的数组排序;然而,当每一组更小时它通常是更有效的。如果没有指定值的比较器,元素则按输入数组中显示的顺序排列。通常用于map和实体(entries)的操作符。
# nest.rollup(function)
为每组中的叶子元素指定汇总函数(rollup )function。汇总函数的返回值会覆盖叶子值数组。不论是由map操作符返回的关联数组,还是实体操作符返回的每个实体的值属性。
# nest.map(array[, mapType])
对指定的数组使用nest操作符,返回一个关联数组。返回的关联数组array中每个实体对应由第一个key函数返回的不同的key值。实体值决定于注册的key函数的数量:如果有一个额外的key,值就是另外一个嵌套的关联数组;否则,值就是过滤自含有指定key值得输入数组array的元素数组。
如果指定了mapType,指定的函数就会用来构造一个map而不是返回一个简单的JavaScript对象。推荐使用d3.map 实现这个目的,例如:
var yieldsByYearAndVariety = d3.nest()
.key(function(d) { return d.year; })
.key(function(d) { return d.variety; })
.map(yields, d3.map);
使用d3.map而不是一个对象提供便捷(例如:返回的map含有 keys 和 values 函数),防止不寻常的键名与JavaScript内置的属性冲突,例如__proto__
。
# nest.entries(array)
为指定的array参数应用nest操作符,返回一个键值对数组。从概念上讲,这和对 map 返回的关联数组应用 d3.entries 操作符类似,但是这个是用于每一层而不是只有第一层(最外层)。返回数组中的每个实体对应于第一个key函数返回的不同的key值。实体值取决于注册的key函数的数量:如果有一个关联的key,值就是另外一个嵌套实体数组;否则,值就是含有指定key值得输入数组array过滤得到的元素数组。嵌套的案例: http://bl.ocks.org/phoebebright/raw/3176159/
[1] accessor function, 亦为getter, callback, 访问器, 常译作‘回调(函数)’。术语翻译 (计算机软件/编程) // Howard Liang 注 Nov 27, 2015
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论