分面,将一份数据按照某个维度分隔成若干子集,然后创建一个图表的矩阵,将每一个数据子集绘制到图形矩阵的窗格中。

总结起来,分面其实提供了两个功能:

  1. 按照指定的维度划分数据集;

  2. 对图表进行排版。

对于探索型数据分析来说,分面是一个强大有力的工具,能帮你迅速地分析出数据各个子集模式的异同。Facet 分面 - 图1

如何设置分面

  1. chart.facet(type, {
  2. fileds: [field1, field2...],
  3. showTitle: true, // 显示标题
  4. autoSetAxis: true,// 自动设置坐标轴的文本,避免重复和遮挡
  5. padding: 10, // 每个view 之间的间距
  6. /**
  7. * 创建每个分面中的视图
  8. * @param {object} view 视图对象
  9. * @param {object} facet facet中有行列等信息,常见属性:data rows cols rowIndex colIndex rowField colField
  10. * @return {null}
  11. */
  12. eachView(view, facet) {},
  13. // 列标题
  14. colTitle: {
  15. offsetY: -15,
  16. style: {
  17. fontSize: 14,
  18. textAlign: 'center',
  19. fill: '#444'
  20. }
  21. },
  22. // 行标题
  23. rowTitle: {
  24. offsetX: 15,
  25. style: {
  26. fontSize: 14,
  27. textAlign: 'center',
  28. rotate: 90,
  29. fill: '#444'
  30. }
  31. }
  32. })

说明:

  • 第一个参数 type 用于指定分面的类型;

  • fileds 属性用于指定数据集划分依据的字段;

  • eachView 回调函数中创建各个视图的图表类型;

也可以设置每个分面之间的间距 padding

  1. chart.facet('list', {
  2. fileds: [ 'cut', 'carat' ],
  3. padding: 20 // 各个分面之间的间距,也可以是数组 [top, right, bottom, left]
  4. });

更多配置信息,请查阅 Facet API

分面的类型

G2 支持的分面类型如下表所示:

分面类型 说明
rect 默认类型,指定 2 个维度作为行列,形成图表的矩阵。
list 指定一个维度,可以指定一行有几列,超出自动换行。
circle 指定一个维度,沿着圆分布。
tree 指定多个维度,每个维度作为树的一级,展开多层图表。
mirror 指定一个维度,形成镜像图表。
matrix 指定一个维度,形成矩阵分面。

rect 矩形分面

rect 矩形分面是 G2 的默认分面类型。支持按照一个或者两个维度的数据划分,按照先列后行的顺序。

  1. chart.facet('rect', {
  2. fields: [ 'cut', 'clarity' ],
  3. eachView(view) {
  4. view.point().position('carat*price').color('cut');
  5. }
  6. });

分面矩阵每列按照 cut 字段划分,每行按照 clarity 字段划分。

Facet 分面 - 图2

  1. $.getJSON('/assets/data/diamond.json', function(data) {
  2. const chart = new G2.Chart({
  3. container: 'c1',
  4. forceFit: true,
  5. height: 600,
  6. padding: [ 30, 80, 80, 80 ]
  7. });
  8. chart.source(data, {
  9. carat: {
  10. sync: true
  11. },
  12. price: {
  13. sync: true
  14. },
  15. cut: {
  16. sync: true
  17. }
  18. });
  19. chart.facet('rect', {
  20. fields: [ 'cut', 'clarity' ],
  21. eachView(view) {
  22. view.point().position('carat*price').color('cut');
  23. }
  24. });
  25. chart.render();
  26. });

说明:

  • 可以将 fields 字段中表示行和列的字段名时,可以设置行或者列为 null,会变成单行或者单列的分面

list 水平列表分面

该类型分面可以通过设置 cols 属性来指定每行可显示分面的个数,超出时会自动换行。

Facet 分面 - 图3

  1. $.getJSON('/assets/data/diamond.json', function(data) {
  2. const chart = new G2.Chart({
  3. container: 'c2',
  4. width: 800,
  5. height: 400,
  6. padding: [ 30, 90, 80, 80 ]
  7. });
  8. chart.source(data, {
  9. carat: {
  10. sync: true
  11. },
  12. price: {
  13. sync: true
  14. },
  15. cut: {
  16. sync: true
  17. }
  18. });
  19. chart.facet('list', {
  20. fields: [ 'cut' ],
  21. cols: 3, // 超过3个换行
  22. padding: 30,
  23. eachView(view) {
  24. view.point().position('carat*price').color('cut');
  25. }
  26. });
  27. chart.render();
  28. });

circle 圆形分面

Facet 分面 - 图4

  1. const DataView = DataSet.DataView;
  2. $.getJSON('/assets/data/diamond.json',function (data) {
  3. const chart = new G2.Chart({
  4. container: 'c3',
  5. width: 600,
  6. height: 600,
  7. animate: false,
  8. padding: [ 20, 20, 70, 20 ]
  9. });
  10. chart.source(data, {
  11. mean: {
  12. sync: true
  13. },
  14. cut: {
  15. sync: true
  16. }
  17. });
  18. chart.coord('polar');
  19. chart.axis(false);
  20. chart.facet('circle', {
  21. fields: [ 'clarity' ],
  22. padding: 0,
  23. eachView(view, facet) {
  24. const data = facet.data;
  25. const dv = new DataView();
  26. dv.source(data).transform({
  27. type: 'aggregate',
  28. fields: [ 'price' ],
  29. operations: [ 'mean' ],
  30. as: [ 'mean' ],
  31. groupBy: [ 'cut' ]
  32. });
  33. view.source(dv);
  34. view.interval().position('cut*mean').color('cut');
  35. }
  36. }); // 分面设置
  37. chart.render();
  38. });

tree 树形分面

树形分面一般用于展示存在层次结构的数据,展示的是整体和部分之间的关系

提供了 linelineSmooth 两个属性,用于配置连接各个分面的线的样式,其中:

  • line,用于配置线的显示属性。

  • lineSmooth,各个树节点的连接线是否是平滑的曲线,默认为 false。

下图展示了树形多层级的分面。

Facet 分面 - 图5

  1. const data = [
  2. { gender: '男', count: 40, class: '一班', grade: '一年级' },
  3. { gender: '女', count: 30, class: '一班', grade: '一年级' },
  4. { gender: '男', count: 35, class: '二班', grade: '一年级' },
  5. { gender: '女', count: 45, class: '二班', grade: '一年级' },
  6. { gender: '男', count: 20, class: '三班', grade: '一年级' },
  7. { gender: '女', count: 35, class: '三班', grade: '一年级' },
  8. { gender: '男', count: 30, class: '一班', grade: '二年级' },
  9. { gender: '女', count: 40, class: '一班', grade: '二年级' },
  10. { gender: '男', count: 25, class: '二班', grade: '二年级' },
  11. { gender: '女', count: 32, class: '二班', grade: '二年级' },
  12. { gender: '男', count: 28, class: '三班', grade: '二年级' },
  13. { gender: '女', count: 36, class: '三班', grade: '二年级' }
  14. ];
  15. const DataView = DataSet.DataView;
  16. const chart = new G2.Chart({
  17. container: 'c4',
  18. width: 800,
  19. height: 400,
  20. animate: false,
  21. padding: [ 0, 90, 80, 80 ]
  22. });
  23. chart.source(data);
  24. chart.coord('theta');
  25. chart.tooltip({
  26. showTitle: false
  27. });
  28. chart.facet('tree', {
  29. fields: [ 'grade','class' ],
  30. line: {
  31. stroke: '#00a3d7'
  32. },
  33. lineSmooth: true,
  34. eachView(view, facet) {
  35. const data = facet.data;
  36. const dv = new DataView();
  37. dv.source(data).transform({
  38. type: 'percent',
  39. field: 'count',
  40. dimension: 'gender',
  41. as: 'percent'
  42. });
  43. view.source(dv, {
  44. percent: {
  45. formatter(val) {
  46. return (val * 100).toFixed(2) + '%';
  47. }
  48. }
  49. });
  50. view.intervalStack().position('percent').color('gender');
  51. }
  52. });
  53. chart.render();

mirror 镜像分面

镜像分面一般用于对比两类数据的场景,例如 男女的比例、正确错误的对比等

通过配置 transpose 属性为 true,可以将镜像分面翻转。

Facet 分面 - 图6

  1. $.getJSON('/assets/data/population.json', function(data) {
  2. const tmp = [];
  3. const dates = [];
  4. const selEl = $('#selYear');
  5. data.male.values.forEach(function(obj) {
  6. if (dates.indexOf(obj.date) === -1) {
  7. dates.push(obj.date);
  8. }
  9. obj.age_groups.forEach(function(subObject) {
  10. subObject.gender = 'male';
  11. subObject.date = obj.date;
  12. tmp.push(subObject);
  13. });
  14. });
  15. data.female.values.forEach(function(obj) {
  16. obj.age_groups.forEach(function(subObject) {
  17. subObject.gender = 'female';
  18. subObject.date = obj.date;
  19. tmp.push(subObject);
  20. });
  21. });
  22. dates.forEach(date => {
  23. $('<option value="' + date + '">' + new Date(date * 1000).getFullYear() + '</option>').appendTo(selEl);
  24. });
  25. const ds = new DataSet({
  26. state: {
  27. date: dates[0]
  28. }
  29. });
  30. const dv = ds.createView()
  31. .source(tmp)
  32. .transform({
  33. type: 'filter',
  34. callback(row) { // 判断某一行是否保留,默认返回true
  35. return new Date(row.date * 1000).getFullYear() === new Date(ds.state.date * 1000).getFullYear();
  36. }
  37. });
  38. const chart = new G2.Chart({
  39. container: 'c5',
  40. forceFit: true,
  41. height: 600
  42. });
  43. chart.source(dv, {
  44. age: {
  45. sync: true,
  46. tickCount: 11
  47. },
  48. total_percentage: {
  49. sync: true,
  50. formatter(v) {
  51. return v + '%';
  52. }
  53. },
  54. gender: {
  55. sync: true
  56. }
  57. });
  58. chart.facet('mirror', {
  59. fields: [ 'gender' ],
  60. transpose: true,
  61. eachView(view) {
  62. view.interval().position('age*total_percentage')
  63. .color('gender', [ 'rgb(113,192,235)', 'rgb(246,170,203)' ]);
  64. }
  65. });
  66. chart.render();
  67. selEl.on('change', function() {
  68. const val = selEl.val();
  69. const date = parseInt(val);
  70. ds.setState('date', date);
  71. });
  72. });

matrix 矩阵分面

矩阵分面主要对比数据中多个字段之间的关系,例如常见的散点矩阵图
Facet 分面 - 图7

  1. const DataView = DataSet.DataView;
  2. $.getJSON('/assets/data/iris.json', function(data) {
  3. const chart = new G2.Chart({
  4. container: 'c6',
  5. forceFit: true,
  6. height: 600
  7. });
  8. chart.source(data, {
  9. Species: {
  10. sync: true
  11. }
  12. });
  13. chart.facet('matrix', {
  14. fields: [ 'SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth' ],
  15. eachView(view, facet) {
  16. if (facet.rowIndex === facet.colIndex) {
  17. const dv = new DataView();
  18. dv.source(facet.data)
  19. .transform({
  20. type: 'bin.histogram',
  21. field: facet.colField, // 对应数轴上的一个点
  22. bins: 30, // 分箱个数
  23. as: [ facet.colField, 'count' ],
  24. groupBy: [ 'Species' ]
  25. });
  26. view.source(dv.rows);
  27. view.intervalStack()
  28. .position(facet.colField + '*count')
  29. .color('Species', [ '#880000', '#008800', '#000088' ]);
  30. } else {
  31. view.point()
  32. .position([ facet.colField, facet.rowField ])
  33. .color('Species', [ '#880000', '#008800', '#000088' ]);
  34. }
  35. }
  36. });
  37. chart.render();
  38. });