做可視化決策的過程,為了節省人的工作量及提效,一個好的方案是推薦。
現有的智能可視化推薦系統分為兩類: 基於規則的 和 基於機器學習的。
前者一般是根據專家經驗或實驗得到的可視化准則; 后者則是直接學習從數據到可視化的模型。
本文是在研究基於規則的可視化推薦的過程中,查看背后源碼規則部分實現的過程后的衍生物。
一、AVA 背景介紹
AVA 是阿里出品。定義如下:
AVA (AVA logo Visual Analytics) 是為了更簡便的可視分析而生的技術框架。 其名稱中的第一個 A 具有多重涵義:它說明了這是一個出自阿里巴巴集團(Alibaba)技術框架,其目標是成為一個自動化(Automated)、智能驅動(AI driven)、支持增強分析(Augmented)的可視分析解決方案。
二、推薦流程

數據有特征,每一種類型也有特征。
數據特征在所有的圖表類型特征中判斷后,當數據高度滿足某一種類型的所有特征時,那么就找到了指定的類型,渲染推薦給用戶。

- 給定數據
[
{ f1: 'a', f2: 70 },
{ f1: 'b', f2: 120 },
{ f1: 'c', f2: 900 },
{ f1: 'd', f2: 630 },
]
- 經過 @antv/dw-analyzer 分析處理后得到的具有更多特征的數據集:
[
{
"count": 4,
"distinct": 4,
"type": "string",
"recommendation": "string",
"missing": 0,
"samples": [
"a",
"b",
"c",
"d"
],
"valueMap": {
"a": 1,
"b": 1,
"c": 1,
"d": 1
},
"maxLength": 1,
"minLength": 1,
"meanLength": 1,
"containsChars": true,
"containsDigits": false,
"containsSpace": false,
"containsNonWorlds": false,
"name": "f1",
"levelOfMeasurements": [
"Nominal"
]
},
{
"count": 4,
"distinct": 4,
"type": "integer",
"recommendation": "integer",
"missing": 0,
"samples": [
70,
120,
900,
630
],
"valueMap": {
"70": 1,
"120": 1,
"630": 1,
"900": 1
},
"minimum": 70,
"maximum": 900,
"mean": 430,
"percentile5": 70,
"percentile25": 70,
"percentile50": 120,
"percentile75": 630,
"percentile95": 900,
"sum": 1720,
"variance": 121650,
"stdev": 348.78360053190573,
"zeros": 0,
"name": "f2",
"levelOfMeasurements": [
"Interval",
"Discrete"
]
}
]
- 數據集是否是某一種圖表類型,那么數據集需要符合該類圖表的條件(特征)。因此知識庫中定義了43中圖表類型(g2plot中的圖表類型)的類型知識特征 @antv/knowledge
舉例 basic_pie_chart 類型:
basic_pie_chart: {
id: 'basic_pie_chart',
name: 'Pie Chart',
alias: ['Circle Chart', 'Pie'], // 別名
family: ['PieCharts'], // 大類
def: // 定義
'A pie chart is a chart that the classification and proportion of data are represented by the color and arc length (angle, area) of the sector.',
purpose: ['Comparison', 'Composition', 'Proportion'], // 分析目的:對比、成分、占比
coord: ['Polar'], // 坐標系:極坐標系 - 多用於圓形的圖形布局
category: ['Statistic'], // 圖形類別:統計圖表 - 折線圖、餅圖等用來表示數據的統計或聚合結果的經典圖表
shape: ['Round'], // 形狀:圓形 - 如:餅圖、雷達圖,等
dataPres: [ // 所需數據條件:下面解讀為圖表中有且只有 1 個 數值 或 無序名詞 字段
{ minQty: 1, maxQty: 1, fieldConditions: ['Nominal'] },
{ minQty: 1, maxQty: 1, fieldConditions: ['Interval'] },
],
channel: ['Angle', 'Area', 'Color'], // 視覺通道:角度、面積、顏色
},
- 數據特征、圖表類型特征有了,接着便是是特征條件契合判斷。找出滿足條件的。@antv/chart-advisor
最終繪制圖如下:

三、規則探索
數據特征多(>10),圖表類型多(43),每一種圖表類型特征也多(>10),而且特征權重不同。
數據特征和圖表類型特征,條件判斷該怎么設計呢,才能使代碼易閱讀理解、易擴展呢?
條件判斷, 常見方式 if-else,當判斷條件較多,一般大於 3 時,采用 switch-case 實現。這兒都不適用。
龐大的條件判斷,這個庫給出了一種方案:規則 + 計分 實現。

1.規則過濾代碼片段:過濾
const allTypes = Object.keys(Wiki) as ChartID[];
const list: Advice[] = allTypes.map((t) => {
// anaylze score
let score = 0;
let hardScore = 1;
Rules.filter((r: Rule) => r.hardOrSoft === 'HARD' && r.specChartTypes.includes(t as ChartID)).forEach(
(hr: Rule) => {
const score = hr.check({ dataProps, chartType: t, purpose, preferences });
hardScore *= score;
record[hr.id] = score;
}
);
let softScore = 0;
Rules.filter((r: Rule) => r.hardOrSoft === 'SOFT' && r.specChartTypes.includes(t as ChartID)).forEach(
(sr: Rule) => {
const score = sr.check({ dataProps, chartType: t, purpose, preferences });
softScore += score;
record[sr.id] = score;
}
);
score = hardScore * (1 + softScore);
// ....
}
allTypes 來源於圖表類型;
import { CKBJson, LevelOfMeasurement as LOM, ChartID } from '@antv/knowledge';
const Wiki = CKBJson('en-US', true);
其中dataProps就是data提取特征后的數據集:數據特征提取
export function dataToDataProps(data: any[]): FieldInfo[] {
const dataTypeInfos = DWAnalyzer.typeAll(data);
const dataProps: FieldInfo[] = [];
dataTypeInfos.forEach((info) => {
const lom = [];
if (DWAnalyzer.isNominal(info)) lom.push('Nominal');
if (DWAnalyzer.isOrdinal(info)) lom.push('Ordinal');
if (DWAnalyzer.isInterval(info)) lom.push('Interval');
if (DWAnalyzer.isDiscrete(info)) lom.push('Discrete');
if (DWAnalyzer.isContinuous(info)) lom.push('Continuous');
if (DWAnalyzer.isTime(info)) lom.push('Time');
const newInfo: FieldInfo = { ...info, levelOfMeasurements: lom as LOM[] };
dataProps.push(newInfo);
});
return dataProps;
}
每個圖表類,所有規則按 hard 和 soft 分別過濾,把規則集合中符合當前圖表類的規則,再遍歷一遍,傳遞數據,調用check計算得分,最后計算綜合得分。
softScore = 0;
softScore += score;
hardScore = 1;
hardScore *= score;
score = hardScore * (1 + softScore)
2.規則類定義屬性和方法:規則類:定義了以下方法和屬性
this._id = id;
this._hardOrSoft = hardOrSoft;
this._specChartTypes = specChartTypes;
this._weight = weight;
this.validator = validator;
在check的時候,將規則定義的權重加上
check(args: Info) {
return this.validator(args) * this._weight;
}
3.規則集合定義規則:規則集合
const ChartRules: Rule[] = [
// Data must satisfy the data prerequisites
new Rule('data-check', 'HARD', allChartTypes, 1.0, (args): number => {
let result = 0;
const { dataProps, chartType } = args;
if (dataProps && chartType && Wiki[chartType]) {
result = 1;
const dataPres = Wiki[chartType].dataPres || [];
for (const dataPre of dataPres) {
if (!verifyDataProps(dataPre, dataProps)) {
result = 0;
return result;
}
}
}
return result;
}),
// ...
// Some charts should has at most N series.
new Rule(
'series-qty-limit',
'SOFT',
['pie_chart', 'donut_chart', 'radar_chart', 'rose_chart'],
0.8,
(args): number => {
let result = 0;
const { dataProps, chartType } = args;
let limit = 6;
if (chartType === 'pie_chart' || chartType === 'donut_chart' || chartType === 'rose_chart') limit = 6;
if (chartType === 'radar_chart') limit = 8;
if (dataProps) {
const field4Series = dataProps.find((field) => hasSubset(field.levelOfMeasurements, ['Nominal']));
const seriesQty = field4Series && field4Series.count ? field4Series.count : 0;
if (seriesQty >= 2 && seriesQty <= limit) {
result = 2 / seriesQty;
}
}
return result;
}
),
// ...
]
hard 可以理解為硬性條件,weight 都為 1。
soft 理解為非硬性條件,因此 weight 視條件而定,(0,1) 直接取值。
result 可以理解為每個規則的得分。
結合規則 rule 類理解,每個規則針對特定的圖表類型集合,有名稱、hardorsoft、weight、得分計算規則。
一共定義了11個規則:
["data-check", "data-field-qty", "no-redundant-field", "purpose-check", "series-qty-limit", "bar-series-qty", "line-field-time-ordinal", "landscape-or-portrait", "diff-pie-sector", "nominal-enum-combinatorial", "limit-series"]
每個規則針對多個圖表類型。
每個規則內部將數特征和圖表類型特征所需條件進行比較判斷,計算出分值。
4.代碼日志
如上面的舉例的data:
在遍歷所有圖表類型,通過所有規則后,其中 pie_chart 和 donut_chart 得分相同 1.8303440007183807,最高,因此最后渲染的是 餅圖:
經過了這些規則得分:
data-check: 1 hard
data-field-qty: 1 hard
diff-pie-sector: 0.4303440007183805 soft
limit-series: 0 soft
no-redundant-field: 1 hard
nominal-enum-combinatorial: 0 soft
purpose-check: 1 hard
series-qty-limit: 0.4 soft
43中類型得分圖如下:

推薦一個的話,就推薦得分最高的那個圖表類型。
推薦多個的話,就推薦得分大於0或得分大於1的圖表類型(看實際需求)。
總結
- 規則的代碼設計邏輯可應用於分類。優點是易閱讀理解、易擴展。
舉例:鮮花識別
當你在花園里看到一種花,很漂亮,你想知道它叫什么名字,有什么故事,寓意着什么?
會不會也向玫瑰一樣,有美麗的名字,象征着愛。

- 對於可視化的認知更深了,特別推薦仔細閱讀知識庫文檔。