UNPKG

20.2 kBMarkdownView Raw
1# Transform 数据转换
2
3一个数据视图(DataSet.View)通过 Transform 来进行数据转换操作,其语法如下:
4
5```javascript
6dv.transform({
7 type: connectorName,
8 ...otherOptions,
9});
10```
11
12举个例子:
13
14```javascript
15const testCSV = `Expt,Run,Speed
16 1,1,850
17 1,2,740
18 1,3,900
19 1,4,1070`;
20
21const dv = new DataSet.DataView().source(testCSV, {
22 type: 'csv',
23});
24console.log(dv.rows);
25/*
26 * dv.rows:
27 * [
28 * {Expt: " 1", Run: "1", Speed: "850"}
29 * {Expt: " 1", Run: "2", Speed: "740"}
30 * {Expt: " 1", Run: "3", Speed: "900"}
31 * {Expt: " 1", Run: "4", Speed: "1070"}
32 * ]
33 */
34
35dv.transform({
36 type: 'filter',
37 callback(row) {
38 return row.Run !== '1';
39 },
40});
41console.log(dv.rows);
42/*
43 * dv.rows:
44 * [
45 * {Expt: " 1", Run: "2", Speed: "740"}
46 * {Expt: " 1", Run: "3", Speed: "900"}
47 * {Expt: " 1", Run: "4", Speed: "1070"}
48 * ]
49 */
50```
51
52上述代码中,数据视图实例 `dv` 使用 `csv` 类型的 Connector 载入了一段 CSV 文本,之后执行 `filter` Transform,过滤了某些数据。
53
54目前 DataSet 支持以下 Transform:
55
56## 静态处理相关
57
58### filter 数据过滤
59
60具体用法见示例:
61
62```javascript
63import { DataView } from '@antv/data-set';
64
65const data = [
66 { year: 1990, sales: 200 },
67 { year: 1992, sales: 100 },
68 { year: 1994, sales: 120 },
69 { year: 1995, sales: 85 },
70];
71
72const dv = new DataView().source(data);
73dv.transform({
74 type: 'filter',
75 callback(row) {
76 return row.sales < 100;
77 },
78});
79
80console.log(dv.rows); // [ { year: 1995, sales: 85 } ]
81```
82
83### map 数据加工
84
85具体用法见示例:
86
87```javascript
88const data = [
89 { x: 'a', y: 1 },
90 { x: 'b', y: 11 },
91 { x: 'c', y: 21 },
92];
93
94const dv = new DataView().source(data);
95dv.transform({
96 type: 'map',
97 callback(row) {
98 row.z = 'z'; // 为每条记录新添加一个 z 字段
99 return row;
100 },
101});
102
103console.log(dv.rows);
104/*
105[
106 { x: 'a', y: 1, z: 'z' },
107 { x: 'b', y: 11, z: 'z' },
108 { x: 'c', y: 21, z: 'z' }
109]
110*/
111```
112
113### pick 字段过滤
114
115具体用法见示例:
116
117```javascript
118const data = [
119 { x: 1, y: 11 },
120 { x: 2, y: 12 },
121 { x: 3, y: 13 },
122 { x: 4, y: 14 },
123 { x: 5, y: 15 },
124];
125
126const dv = new DataView().source(data);
127dv.transform({
128 type: 'pick',
129 fields: ['x'],
130});
131
132console.log(dv.rows);
133/*
134[
135 { x: 1 },
136 { x: 2 },
137 { x: 3 },
138 { x: 4 },
139 { x: 5 }
140]
141*/
142```
143
144### rename 字段重命名
145
146> alias: rename-fields
147
148具体用法见示例:
149
150```javascript
151const data = [{ a: 1, b: 2 }];
152const dv = new DataView().source(data);
153dv.transform({
154 type: 'rename',
155 map: {
156 a: 'x',
157 b: 'y',
158 },
159});
160
161console.log(dv.rows); // [ { x: 1, y: 2 } ]
162```
163
164### reverse 逆序排列
165
166具体用法见示例:
167
168```javascript
169const data = [
170 { x: 1, y: 1 },
171 { x: 2, y: 2 },
172 { x: 3, y: 3 },
173];
174const dv = new DataView().source(data);
175dv.transform({
176 type: 'reverse',
177});
178
179console.log(dv.rows);
180/*
181[
182 { x: 3, y: 3 },
183 { x: 2, y: 2 },
184 { x: 1, y: 1 }
185]
186*/
187```
188
189### sort 数据排序
190
191具体用法见示例:
192
193```javascript
194dv.transform({
195 type: 'sort',
196 callback(a, b) {
197 // 排序依据,和原生 js 的排序 callback 一致
198 return a.year - b.year;
199 },
200});
201```
202
203### sort-by 按字段排序
204
205> alias: sortBy
206
207具体用法见示例:
208
209```javascript
210dv.transform({
211 type: 'sort-by',
212 fields: ['year'], // 根据指定的字段集进行排序,与lodash的sortBy行为一致
213 order: 'DESC', // 默认为 ASC,DESC 则为逆序
214});
215```
216
217### subset 获取子集
218
219具体用法见示例:
220
221```javascript
222dv.transform({
223 type: 'subset',
224 startRowIndex: 1, // 保留行的起始索引
225 endRowIndex: 2, // 保留行的结束索引
226 fields: ['year'], // 保留字段集
227});
228```
229
230### partition 数据分组
231
232> alias: group | groups
233
234具体用法见示例:
235
236```javascript
237dv.transform({
238 type: 'partition',
239 groupBy: ['year'], // 以year字段进行分组
240 orderBy: ['month'], // 以month字段进行排序
241});
242```
243
244## 数据形变 / 数据补全相关
245
246### fill-rows 补全行
247
248> alias: fillRows
249
250先按照 `groupBy``orderBy` 进行分组,如果以分组作为补全依据(`fillBy: 'group'`),那么就对比每个分组,以 `orderBy` 序列字段值最全的分组为标准补全数据行数不足的分组。如果以序列作为补全依据(`fillBy: 'order'`),那么就取所有 `orderBy` 序列字段的组合,为每个分组都补充全量的序列。
251
252> 注意!如果原始数据有除 groupBy 和 orderBy 指定的字段以外的字段,那么补充的数据行里会缺失这些字段。这时可以结合 impute Transform 来做字段数据补全。
253
254具体用法见示例:
255
256> fillBy: group
257
258```javascript
259const data = [
260 { a: 0, b: 0 },
261 { a: 0, b: 1 },
262 { a: 0, b: 2 },
263 { a: 1, b: 1 },
264 { a: 1, b: 2 },
265 { a: 1, b: 3 },
266 { a: 2, b: 0 },
267];
268const dv = new DataSet.DataView().source(data).transform({
269 type: 'fill-rows',
270 groupBy: ['a'],
271 orderBy: ['b'],
272 fillBy: 'group', // 默认为 group,可选值:order
273});
274console.log(dv.rows);
275/*
276 * dv.rows 变为:
277 * [
278 * { a: 0, b: 0 }, // 分组1 作为第一个序列字段最全(b)的组,被选为基准
279 * { a: 0, b: 1 },
280 * { a: 0, b: 2 },
281 * { a: 1, b: 1 }, // 分组2 序列字段个数和基准组一致,所以不补充数据
282 * { a: 1, b: 2 },
283 * { a: 1, b: 3 },
284 * { a: 2, b: 0 }, // 分组3 缺省数据,根据基准组进行数据补全
285 * { a: 2, b: 1 }, // 这行数据被补充
286 * { a: 2, b: 2 }, // 这行数据被补充
287 * ]
288 */
289```
290
291> fillBy: order
292
293```javascript
294// 使用和上例同样的数据
295const dv = new DataSet.DataView().source(data).transform({
296 type: 'fill-rows',
297 groupBy: ['a'],
298 orderBy: ['b'],
299 fillBy: 'order', // 默认为 group,可选值:order
300});
301console.log(dv.rows);
302/*
303 * dv.rows 变为:
304 * [
305 * { a: 0, b: 0 }, // 分组1
306 * { a: 0, b: 1 },
307 * { a: 0, b: 2 },
308 * { a: 0, b: 3 }, // 分组1 补充了这行数据,因为全量的序列字段(b)有这个值
309 * { a: 1, b: 0 }, // 分组2 补充了这行数据,因为全量的序列字段(b)有这个值
310 * { a: 1, b: 1 },
311 * { a: 1, b: 2 },
312 * { a: 1, b: 3 },
313 * { a: 2, b: 0 }, // 分组3 缺省数据,根据基准组进行数据补全
314 * { a: 2, b: 1 }, // 这行数据被补充
315 * { a: 2, b: 2 }, // 这行数据被补充
316 * { a: 2, b: 3 }, // 这行数据被补充
317 * ]
318 */
319```
320
321### impute 补全列 / 补全字段
322
323根据配置规则为某个字段补全缺失值。
324
325具体用法见示例:
326
327```javascript
328const data = [
329 { x: 0, y: 1 },
330 { x: 0, y: 2 },
331 { x: 0, y: 3 },
332 { x: 0 },
333 { x: 1, y: 5 },
334 { x: 1, y: 6 },
335 { x: 1, y: 7 },
336 { x: 1 },
337 { x: 1, y: 9 },
338 { x: 2 },
339];
340const dv = new DataSet.DataView().source(data).transform({
341 type: 'impute',
342 field: 'y', // 待补全字段
343 groupBy: ['x'], // 分组字段集(传空则不分组)
344 method: 'max', // 补全字段值时执行的规则
345});
346/*
347 dv.rows 变为
348[
349 { x: 0, y: 1 },
350 { x: 0, y: 2 },
351 { x: 0, y: 3 },
352 { x: 0, y: 3 },
353 { x: 1, y: 5 },
354 { x: 1, y: 6 },
355 { x: 1, y: 7 },
356 { x: 1, y: 7 },
357 { x: 1, y: 9 },
358 { x: 2, y: 9 }
359]
360 */
361```
362
363> 补全字段的规则(method)有常见的统计函数:max, min, median, mean
364
365> 还有补充固定值的写法:method 指定为 value,然后 value 指定为填充的常量,譬如
366
367```javascript
368dv.transform({
369 type: 'impute',
370 field: 'y', // 待补全字段
371 groupBy: ['x'], // 分组字段集(传空则不分组)
372 method: 'value', // 补全常量
373 value: 10, // 补全的常量为10
374});
375```
376
377### fold 字段展开
378
379以指定字段集为 key,展开数据。
380
381具体用法见示例:
382
383```javascript
384const data = [
385 { country: 'USA', gold: 10, silver: 20 },
386 { country: 'Canada', gold: 7, silver: 26 },
387];
388const dv = ds
389 .createView()
390 .source(data)
391 .transform({
392 type: 'fold',
393 fields: ['gold', 'silver'], // 展开字段集
394 key: 'key', // key字段
395 value: 'value', // value字段
396 retains: ['country'], // 保留字段集,默认为除 fields 以外的所有字段
397 });
398/*
399 dv.rows 变为
400[
401 { key: gold, value: 10, country: "USA" },
402 { key: silver, value: 20, country: "USA" },
403 { key: gold, value: 7, country: "Canada" },
404 { key: silver, value: 26, country: "Canada" }
405]
406 */
407```
408
409## 数据比例(百分比)相关 Transform
410
411### percent 总和百分比
412
413统计某个维度下某个字段的值的和占总和的比例(可以分组)。
414
415`field` 是统计发生的字段(求和,求百分比),`dimension` 是统计的维度字段,也就是"每个不同的 `dimension` 下,`field` 值占总和的百分比",`groupBy` 则是分组字段,每一个分组内部独立求百分比(每一个分组内,最后的 `percent` 字段相加之和为 1)。
416
417具体用法见示例:
418
419```javascript
420dv.transform({
421 type: 'percent',
422 field: 'sold', // 统计销量
423 dimension: 'year', // 每年的占比
424 groupBy: ['category'], // 以不同产品类别为分组,每个分组内部各自统计占比
425 as: 'percent', // 结果存储在 percent 字段
426});
427```
428
429### proportion 行数百分比
430
431统计某个维度下某个字段的数据条数占总条数的比例(可以分组)。和 percent Transform 类似,但统计的是数据条目的占比,而不是数据总和的占比。
432
433具体用法见示例:
434
435```javascript
436dv.transform({
437 type: 'proportion',
438 dimension: 'year', // 每年的占比
439 groupBy: ['category'], // 以不同产品类别为分组,每个分组内部各自统计占比
440 as: 'proportion', // 结果存储在proportion字段
441});
442```
443
444## 数据统计相关
445
446### aggregate 聚合统计
447
448统计处理,支持并行的多种统计。
449
450具体用法见示例:
451
452```javascript
453dv.transform({
454 type: 'aggregate', // 别名summary
455 fields: [], // 统计字段集
456 operations: [], // 统计操作集
457 as: [], // 存储字段集
458 groupBy: [], // 分组字段集
459});
460```
461
462> 以上 fields/operations/as 这三个数组元素一一对应。“对某个字段 field 进行某种统计操作 operation 结果存储在某个字段上 as。”
463
464支持的 operations: [详见 simple-statistics](https://simplestatistics.org/)。
465
466- count
467- max
468- min
469- mean
470- median
471- mode
472- product
473- standardDeviation
474- sum
475- sumSimple
476- variance
477
478### regression 回归曲线
479
480计算两个字段的回归拟合曲线。
481
482具体用法见示例:
483
484```javascript
485dv.transform({
486 type: 'regression',
487 method: 'linear', // 回归算法类型
488 fields: ['x', 'y'], // 统计字段
489 bandwidth: 0.1, // 步长
490 extent: [0, 4], // 结果集里,x的数值范围
491 as: ['x', 'y'], // 结果字段
492});
493```
494
495支持的回归算法类型:
496
497- linear
498- exponential
499- logarithmic
500- power
501- polynomial
502
503## 数据分箱相关
504
505### bin.histogram 直方图分箱
506
507单字段
508
509> alias: bin.dot
510
511具体用法见示例:
512
513```javascript
514dv.transform({
515 type: 'bin.histogram',
516 field: 'a', // 对应数轴上的一个点
517 bins: 30, // 分箱个数
518 binWidth: 10, // 分箱步长(会覆盖bins选项)
519 offset: 0, // 分箱偏移量
520 groupBy: [], // 分组(用于层叠直方图)
521 as: ['x', 'count'], // x 为数组,存储了某个分箱的上下限 [x0, x1]
522});
523```
524
525### bin.quantile 分位值分箱
526
527单字段
528
529具体用法见示例:
530
531```javascript
532dv.transform({
533 type: 'bin.quantile',
534 field: 'y', // 计算分为值的字段
535 as: '_bin', // 保存分为值的数组字段
536 groupBy: [], // 分组
537 fraction: 4, // 可选,默认四分位
538 p: [0.5, 0.3], // 可选,p参数列表,与 fraction 二选一
539});
540```
541
542### bin.hexagon 六边形分箱
543
544双字段
545
546> alias: bin.hex | hexbin
547
548具体用法见示例:
549
550```javascript
551dv.transform({
552 type: 'bin.hexagon',
553 fields: ['a', 'b'], // 对应坐标轴上的一个点
554 bins: [30, 30], // 两个方向上的分箱个数
555 binWidth: [10, 1000], // 两个方向上的分箱步长(会覆盖bins的配置)
556 offset: [0, 0], // 两个方向上的分箱偏移量
557 sizeByCount: false, // 是否根据分箱个数调整分箱大小(六边形的半径)
558 as: ['x', 'y', 'count'], // 这个点落在的六边形的顶点坐标集,以及每个分箱内的数据条数
559 // x: [ x0, x1, x2, x3, x4, x5 ], y: [ y0, y1, y2, y3, y4, y5 ]
560 // count: Number
561});
562/*
563 * 顶点顺序:
564 * 3
565 * 4 2
566 *
567 * 5 1
568 * 0
569 */
570```
571
572### bin.rectangle 矩形分箱
573
574双字段
575
576> alias: bin.rect
577
578具体用法见示例:
579
580```javascript
581dv.transform({
582 type: 'bin.rectangle',
583 fields: ['a', 'b'], // 对应坐标轴上的一个点
584 bins: [30, 30], // 两个方向上的分箱个数
585 binWidth: [10, 10], // 两个方向上的分箱步长(会覆盖bins配置)
586 offset: [0, 0], // 两个方向上的分箱偏移量
587 sizeByCount: false, // 是否根据分箱个数调整分箱大小
588 as: ['x', 'y', 'count'], // 这个点落在的六边形的顶点坐标集
589 // x: [ x0, x1, x2, x3 ], y: [ y0, y1, y2, y3 ]
590 // count: Number
591});
592/*
593 * 顶点顺序:
594 * 3 - 2
595 * | |
596 * 0 - 1
597 */
598```
599
600## 核函数相关
601
602### kernel-smooth.regression 核函数概率密度回归
603
604用于画核函数概率密度回归曲线,支持单字段或者双字段。
605
606具体用法见示例:
607
608```javascript
609dv.transform({
610 type: 'kernel-smooth.regression',
611 fields: ['x', 'y'], // 必填,1 或 2 字段
612 method: 'gaussian', // 采用的核函数类型。也可以指定为自定义的函数
613 extent: [min(x), max(x)], // 数值范围,默认为 x 字段的数值范围
614 bandwidth: 0.4, // 步长,默认采用 silverman 的算法计算
615 as: ['x', 'y'], // 结果字段,单字段时,y 为 x 值对应的概率
616});
617```
618
619支持的核函数类型:
620
621- cosine
622- epanechnikov
623- gaussian (default)
624- quartic
625- triangular
626- tricube
627- triweight
628- uniform
629
630### kernel-smooth.density 核函数概率密度分布
631
632用于画核函数概率密度分布热力图,双字段。
633
634具体用法见示例:
635
636```javascript
637dv.transform({
638 type: 'kernel-smooth.density',
639 fields: ['x', 'y'], // 必填
640 method: 'gaussian', // 采用的核函数类型。也可以指定为自定义的函数
641 extent: [
642 [min(x), max(x)],
643 [min(y), max(y)],
644 ], // 数值范围,默认为 x 以及 y 字段各自的数值范围
645 bandwidth: 0.4, // 步长,默认采用 silverman 的算法计算
646 as: ['x', 'y'], // 结果字段,单字段时,y 为 x 值对应的概率
647});
648```
649
650> silverman 提出的 bandwidth 计算算法: [paper](https://ned.ipac.caltech.edu/level5/March02/Silverman/paper.pdf)
651
652支持的核函数类型同上
653
654## 树相关
655
656### hierarchy.treemap 树形图
657
658> alias: treemap
659
660根据树形数据生成树形图 Treemap 布局。
661
662具体用法见示例:
663
664```javascript
665dv.transform({
666 type: 'hierarchy.treemap',
667 field: 'value',
668 tile: 'treemapSquarify', // 布局类型
669 size: [1, 1], // width, height
670 round: false,
671 // ratio: 1.618033988749895, // golden ratio
672 padding: 0, // 各种 padding 配置
673 paddingInner: 0,
674 paddingOuter: 0,
675 paddingTop: 0,
676 paddingRight: 0,
677 paddingBottom: 0,
678 paddingLeft: 0,
679 as: ['x', 'y'], // 矩形的顶点集
680 // x: [ x0, x1, x2, x3 ], y: [ y0, y1, y2, y3 ]
681});
682```
683
684支持的布局类型:
685
686- treemapBinary
687- treemapDice
688- treemapSlice
689- treemapSliceDice
690- treemapSquarify
691- treemapResquarify
692
693### hierarchy.partition 相邻层次图
694
695> alias: adjacency
696
697根据树形数据生成相邻层次图 Adjacency Diagram 布局,可以通过坐标变换变形为 Sunburst 图。
698
699具体用法见示例:
700
701```javascript
702dv.transform({
703 type: 'hierarchy.partition',
704 field: 'value',
705 size: [1, 1], // width, height
706 round: false,
707 // ratio: 1.618033988749895, // golden ratio
708 padding: 0, // 各种 padding 配置
709 as: ['x', 'y'], // 矩形的顶点集
710 // x: [ x0, x1, x2, x3 ], y: [ y0, y1, y2, y3 ]
711});
712```
713
714## 图相关
715
716### diagram.arc 弧长链接图
717
718弧长链接图(Arc Diagram)可以变形为和弦图(Chord Diagram)。
719
720> alias: arc
721
722具体用法见示例:
723
724```javascript
725dv.transform({
726 type: 'diagram.arc',
727 y: 0,
728 thickness: 0.05, // 节点高度,区间 (0, 1)
729 weight: false, // 是否带权重,无权重为弧长链接图,带权重为和弦图
730 marginRatio: 0.1, // 空隙比例,区间[0, 1)
731 id: (node) => node.id, // 获取节点id
732 source: (edge) => edge.source, // 获取边起始点id
733 target: (edge) => edge.target, // 获取边结束点id
734 sourceWeight: (edge) => edge.value, // 获取边起始点权重
735 targetWeight: (edge) => edge.value1, // 获取边结束点权重
736 sortBy: null, // 排序,可以按照id,权重('weight')或者边数量('frequency')排序,也可以自定排序函数
737});
738```
739
740> 注意!这个 Transform 做完之后,有两部分数据(顶点和边数据),G2 在使用是不能直接通过 chart.source(dv) 来导入数据,只能分别导入顶点和边集合,例如:
741
742```javascript
743const nodeView = chart.view();
744nodeView.source(dv.nodes);
745
746const edgeView = chart.view();
747edgeView.source(dv.edges);
748```
749
750### diagram.sankey 桑基图
751
752> alias: sankey
753
754具体用法见示例:
755
756```javascript
757dv.transform({
758 type: 'diagram.sankey',
759 source: (edge) => edge.source, // 边起点id
760 target: (edge) => edge.target, // 边终点id
761 nodeAlign: 'sankeyJustify', // sankeyLeft / sankeyRight / sankeyCenter
762 nodeWidth: 0.02, // 节点宽,范围:(0, 1)
763 nodePadding: 0.02, // 节点上下间距,范围:(0, 1)
764 sort: undefined | null | ((a: any, b: any) => number); // 同列节点排序 undefined 默认值 在每次迭代过程中按宽度递增、null 按照数据排序 不重新排序、function 根据指定函数进行排序,并在初始化的时候排序一次
765});
766```
767
768> 注意!这个 Transform 做完后同样需要注意上述 arc transform 一样的数据导入问题
769
770### diagram.voronoi
771
772voronoi 图
773
774> alias: voronoi
775
776具体用法见示例:
777
778```javascript
779dv.transform({
780 type: 'diagram.voronoi',
781 fields: ['field0', 'field1'], // 对应坐标轴上的一个点
782 extend: [
783 [x0, y0],
784 [x1, y1],
785 ], // 范围
786 size: [width, height], // 范围
787 as: ['x', 'y'], // 每个点包围多边形的顶点集
788 // x: [ x0, x1, x2, ... ], y: [ y0, y1, y2, ... ]
789});
790```
791
792## Geo 地理数据相关
793
794### geo.projection 地理映射
795
796具体用法见示例:
797
798```javascript
799dv.transform({
800 type: 'geo.projection',
801 projection: 'geoAiry', // 指定映射类型
802 as: ['x', 'y', 'centroid_x', 'centroid_y'], // x,y是对应多边形的顶点集
803 // centroid_x是中心点的x坐标
804 // centroid_y是中心点y坐标
805});
806```
807
808### geo.centroid 由地名获取地理位置点
809
810具体用法见示例:
811
812```javascript
813dv.transform({
814 type: 'geo.centroid',
815 field: 'name', // 标注地名的字段
816 geoDataView: geoDataView, // 使用的geo数据来源,可以是DataView实例,也可以是DataView实例的name
817 as: ['_centroid_x', '_centroid_y'], // _centroid_x是中心点的x坐标
818 // _centroid_y是中心点y坐标
819});
820```
821
822### geo.region 由地名获取地理位置区域
823
824具体用法见示例:
825
826```javascript
827dv.transform({
828 type: 'geo.region',
829 field: 'name', // 标注地名的字段
830 geoDataView: geoDataView, // 使用的geo数据来源,可以是DataView实例,也可以是DataView实例的name
831 as: ['_x', '_y'], // 多边形的顶点集
832 // _x: [ x0, x1, x2, ... ], _y: [ y0, y1, y2, ... ]
833});
834```
835
836## 其他
837
838### tag-cloud 词云布局
839
840> alias: word-cloud
841
842具体用法见示例:
843
844```javascript
845dv.transform({
846 type: 'tag-cloud',
847 fields: ['text', 'value'], // 参与标签云layout的字段集(前者为文本内容,后者为权重值)
848 font: 'serif', // 标签字体
849 size: [500, 500], // 画布size,[ width, height ]
850 padding: 0,
851 spiral: 'archimedean', // 标签螺旋排布规律函数 'archimedean' || 'rectangular' || {function}
852 fontSize(d) {
853 return d.value;
854 }, // 计算标签字体大小的回调函数,d为一行数据
855 timeInterval: Infinity, // 最大迭代时间,默认为无限大
856 imageMask: { Image }, // Image的实例,必须是 loaded 状态
857});
858```
859
860> 带图片形状的词云布局实例
861
862```javascript
863const imageMask = new Image();
864imageMask.crossOrigin = '';
865imageMask.src = 'https://zos.alipayobjects.com/rmsportal/EEFqYWuloqIHRnh.jpg';
866imageMask.onload = () => {
867 dv.transform({
868 type: 'tag-cloud',
869 imageMask,
870 });
871};
872```