使用 dataset 管理數據
ECharts 4 開始支持了 dataset
組件用於單獨的數據集聲明,從而數據可以單獨管理,被多個組件複用,並且可以基於數據指定數據到視覺的映射。這在不少場景下能帶來使用上的方便。
ECharts 4 以前,數據只能聲明在各個“系列(series)”中,例如:
option: {
xAxis: {
type: 'category',
data: ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie']
},
yAxis: {}
series: [
{
type: 'bar',
name: '2015',
data: [89.3, 92.1, 94.4, 85.4]
},
{
type: 'bar',
name: '2016',
data: [95.8, 89.4, 91.2, 76.9]
},
{
type: 'bar',
name: '2017',
data: [97.7, 83.1, 92.5, 78.1]
}
]
}
這種方式的優點是,直觀易理解,以及適於對一些特殊圖表類型進行一定的數據類型定製。但是缺點是,爲匹配這種數據輸入形式,常需要有數據處理的過程,把數據分割設置到各個系列(和類目軸)中。此外,不利於多個系列共享一份數據,也不利於基於原始數據進行圖表類型、系列的映射安排。
於是,ECharts 4 提供了 數據集
(dataset
)組件來單獨聲明數據,它帶來了這些效果:
- 能夠貼近這樣的數據可視化常見思維方式:基於數據(
dataset
組件來提供數據),指定數據到視覺的映射(由 encode 屬性來指定映射),形成圖表。 - 數據和其他配置可以被分離開來,使用者相對便於進行單獨管理,也省去了一些數據處理的步驟。
- 數據可以被多個系列或者組件複用,對於大數據,不必爲每個系列創建一份。
- 支持更多的數據的常用格式,例如二維數組、對象數組等,一定程度上避免使用者爲了數據格式而進行轉換。
入門例子
下面是一個最簡單的 dataset
的例子:
option = {
legend: {},
tooltip: {},
dataset: {
// 提供一份數據。
source: [
['product', '2015', '2016', '2017'],
['Matcha Latte', 43.3, 85.8, 93.7],
['Milk Tea', 83.1, 73.4, 55.1],
['Cheese Cocoa', 86.4, 65.2, 82.5],
['Walnut Brownie', 72.4, 53.9, 39.1]
]
},
// 聲明一個 X 軸,類目軸(category)。默認情況下,類目軸對應到 dataset 第一列。
xAxis: {type: 'category'},
// 聲明一個 Y 軸,數值軸。
yAxis: {},
// 聲明多個 bar 系列,默認情況下,每個系列會自動對應到 dataset 的每一列。
series: [
{type: 'bar'},
{type: 'bar'},
{type: 'bar'}
]
}
效果如下:
或者也可以使用常見的對象數組的格式:
option = {
legend: {},
tooltip: {},
dataset: {
// 這裏指定了維度名的順序,從而可以利用默認的維度到座標軸的映射。
// 如果不指定 dimensions,也可以通過指定 series.encode 完成映射,參見後文。
dimensions: ['product', '2015', '2016', '2017'],
source: [
{product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
{product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
{product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
{product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
]
},
xAxis: {type: 'category'},
yAxis: {},
series: [
{type: 'bar'},
{type: 'bar'},
{type: 'bar'}
]
};
數據到圖形的映射
本篇裏,我們製作數據可視化圖表的邏輯是這樣的:基於數據,在配置項中指定如何映射到圖形。
概略而言,可以進行這些映射:
- 指定 dataset 的列(column)還是行(row)映射爲圖形系列(series)。這件事可以使用 series.seriesLayoutBy 屬性來配置。默認是按照列(column)來映射。
- 指定維度映射的規則:如何從 dataset 的維度(一個“維度”的意思是一行/列)映射到座標軸(如 X、Y 軸)、提示框(tooltip)、標籤(label)、圖形元素大小顏色等(visualMap)。這件事可以使用 series.encode 屬性,以及 visualMap 組件(如果有需要映射顏色大小等視覺維度的話)來配置。上面的例子中,沒有給出這種映射配置,那麼 ECharts 就按最常見的理解進行默認映射:X 座標軸聲明爲類目軸,默認情況下會自動對應到 dataset.source 中的第一列;三個柱圖系列,一一對應到 dataset.source 中後面每一列。
下面詳細解釋。
按行還是按列做映射
有了數據表之後,使用者可以靈活得配置:數據如何對應到軸和圖形系列。
用戶可以使用 seriesLayoutBy
配置項,改變圖表對於行列的理解。seriesLayoutBy
可取值:
- 'column': 默認值。系列被安放到
dataset
的列上面。 - 'row': 系列被安放到
dataset
的行上面。
看這個例子:
option = {
legend: {},
tooltip: {},
dataset: {
source: [
['product', '2012', '2013', '2014', '2015'],
['Matcha Latte', 41.1, 30.4, 65.1, 53.3],
['Milk Tea', 86.5, 92.1, 85.7, 83.1],
['Cheese Cocoa', 24.1, 67.2, 79.5, 86.4]
]
},
xAxis: [
{type: 'category', gridIndex: 0},
{type: 'category', gridIndex: 1}
],
yAxis: [
{gridIndex: 0},
{gridIndex: 1}
],
grid: [
{bottom: '55%'},
{top: '55%'}
],
series: [
// 這幾個系列會在第一個直角座標系中,每個系列對應到 dataset 的每一行。
{type: 'bar', seriesLayoutBy: 'row'},
{type: 'bar', seriesLayoutBy: 'row'},
{type: 'bar', seriesLayoutBy: 'row'},
// 這幾個系列會在第二個直角座標系中,每個系列對應到 dataset 的每一列。
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1}
]
}
效果如下:
維度(dimension)
介紹 encode
之前,首先要介紹“維度(dimension)”的概念。
常用圖表所描述的數據大部分是“二維表”結構,上述的例子中,我們都使用二維數組來容納二維表。現在,當我們把系列(series)對應到“列”的時候,那麼每一列就稱爲一個“維度(dimension)”,而每一行稱爲數據項(item)。反之,如果我們把系列(series)對應到錶行,那麼每一行就是“維度(dimension)”,每一列就是數據項(item)。
維度可以有單獨的名字,便於在圖表中顯示。維度名(dimension name)可以在定義在 dataset 的第一行(或者第一列)。例如上面的例子中,'score'
、'amount'
、'product'
就是維度名。從第二行開始,纔是正式的數據。dataset.source
中第一行(列)到底包含不包含維度名,ECharts 默認會自動探測。當然也可以設置 dataset.sourceHeader: true
顯示聲明第一行(列)就是維度,或者 dataset.sourceHeader: false
表明第一行(列)開始就直接是數據。
維度的定義,也可以使用單獨的 dataset.dimensions
或者 series.dimensions
來定義,這樣可以同時指定維度名,和維度的類型(dimension type):
var option1 = {
dataset: {
dimensions: [
{name: 'score'},
// 可以簡寫爲 string,表示維度名。
'amount',
// 可以在 type 中指定維度類型。
{name: 'product', type: 'ordinal'}
],
source: [...]
},
...
};
var option2 = {
dataset: {
source: [...]
},
series: {
type: 'line',
// 在系列中設置的 dimensions 會更優先採納。
dimensions: [
null, // 可以設置爲 null 表示不想設置維度名
'amount',
{name: 'product', type: 'ordinal'}
]
},
...
};
大多數情況下,我們並不需要去設置維度類型,因爲會自動判斷。但是如果因爲數據爲空之類原因導致判斷不足夠準確時,可以手動設置維度類型。
維度類型(dimension type)可以取這些值:
'number'
: 默認,表示普通數據。'ordinal'
: 對於類目、文本這些 string 類型的數據,如果需要能在數軸上使用,須是 'ordinal' 類型。ECharts 默認會自動判斷這個類型。但是自動判斷也是不可能很完備的,所以使用者也可以手動強制指定。'time'
: 表示時間數據。設置成'time'
則能支持自動解析數據成時間戳(timestamp),比如該維度的數據是 '2017-05-10',會自動被解析。如果這個維度被用在時間數軸(axis.type 爲'time'
)上,那麼會被自動設置爲'time'
類型。時間類型的支持參見 data。'float'
: 如果設置成'float'
,在存儲時候會使用TypedArray
,對性能優化有好處。'int'
: 如果設置成'int'
,在存儲時候會使用TypedArray
,對性能優化有好處。
數據到圖形的映射(encode)
瞭解了維度的概念後,我們就可以使用 encode 來做映射。總體是這樣的感覺:
var option = {
dataset: {
source: [
['score', 'amount', 'product'],
[89.3, 58212, 'Matcha Latte'],
[57.1, 78254, 'Milk Tea'],
[74.4, 41032, 'Cheese Cocoa'],
[50.1, 12755, 'Cheese Brownie'],
[89.7, 20145, 'Matcha Cocoa'],
[68.1, 79146, 'Tea'],
[19.6, 91852, 'Orange Juice'],
[10.6, 101852, 'Lemon Juice'],
[32.7, 20112, 'Walnut Brownie']
]
},
xAxis: {},
yAxis: {type: 'category'},
series: [
{
type: 'bar',
encode: {
// 將 "amount" 列映射到 X 軸。
x: 'amount',
// 將 "product" 列映射到 Y 軸。
y: 'product'
}
}
]
};
效果如下:
encode
聲明的基本結構如下,其中冒號左邊是座標系、標籤等特定名稱,如 'x'
, 'y'
, 'tooltip'
等,冒號右邊是數據中的維度名(string 格式)或者維度的序號(number 格式,從 0 開始計數),可以指定一個或多個維度(使用數組)。通常情況下,下面各種信息不需要所有的都寫,按需寫即可。
下面是 encode 支持的屬性:
// 在任何座標系和系列中,都支持:
encode: {
// 使用 “名爲 product 的維度” 和 “名爲 score 的維度” 的值在 tooltip 中顯示
tooltip: ['product', 'score']
// 使用 “維度 1” 和 “維度 3” 的維度名連起來作爲系列名。(有時候名字比較長,這可以避免在 series.name 重複輸入這些名字)
seriesName: [1, 3],
// 表示使用 “維度2” 中的值作爲 id。這在使用 setOption 動態更新數據時有用處,可以使新老數據用 id 對應起來,從而能夠產生合適的數據更新動畫。
itemId: 2,
// 指定數據項的名稱使用 “維度3” 在餅圖等圖表中有用,可以使這個名字顯示在圖例(legend)中。
itemName: 3
}
// 直角座標系(grid/cartesian)特有的屬性:
encode: {
// 把 “維度1”、“維度5”、“名爲 score 的維度” 映射到 X 軸:
x: [1, 5, 'score'],
// 把“維度0”映射到 Y 軸。
y: 0
}
// 單軸(singleAxis)特有的屬性:
encode: {
single: 3
}
// 極座標系(polar)特有的屬性:
encode: {
radius: 3,
angle: 2
}
// 地理座標系(geo)特有的屬性:
encode: {
lng: 3,
lat: 2
}
// 對於一些沒有座標系的圖表,例如餅圖、漏斗圖等,可以是:
encode: {
value: 3
}
下面給出個更豐富的 encode
的示例:
視覺通道(顏色、尺寸等)的映射
我們可以使用 visualMap 組件進行視覺通道的映射。詳見 visualMap
文檔的介紹。這是一個示例:
默認的映射
指的一提的是,ECharts 針對最常見直角座標系中的圖表(折線圖、柱狀圖、散點圖、K線圖等)、餅圖、漏斗圖,給出了簡單的默認的映射,從而不需要配置 encode
也可以出現圖表(一旦給出了 encode
,那麼就不會採用默認映射)。默認的映射規則不易做得複雜,基本規則大體是:
- 在座標系中(如直角座標系、極座標系等)
- 如果有類目軸(axis.type 爲 'category'),則將第一列(行)映射到這個軸上,後續每一列(行)對應一個系列。
- 如果沒有類目軸,假如座標系有兩個軸(例如直角座標系的 X Y 軸),則每兩列對應一個系列,這兩列分別映射到這兩個軸上。
- 如果沒有座標系(如餅圖)
- 取第一列(行)爲名字,第二列(行)爲數值(如果只有一列,則取第一列爲數值)。
默認的規則不能滿足要求時,就可以自己來配置 encode
,也並不複雜。
幾個常見的映射設置方式
問:如何把第三列設置爲 X 軸,第五列設置爲 Y 軸?
答:
series: {
// 注意維度序號(dimensionIndex)從 0 開始計數,第三列是 dimensions[2]。
encode: {x: 2, y: 4},
...
}
問:如何把第三行設置爲 X 軸,第五行設置爲 Y 軸?
答:
series: {
encode: {x: 2, y: 4},
seriesLayoutBy: 'row',
...
}
問:如何把第二列設置爲標籤?
答: 關於標籤的顯示 label.formatter,現在支持引用特定維度的值,例如:
series: {
label: {
// `'{@score}'` 表示 “名爲 score” 的維度裏的值。
// `'{@[4]}'` 表示引用序號爲 4 的維度裏的值。
formatter: 'aaa{@product}bbb{@score}ccc{@[4]}ddd'
}
}
問:如何讓第 2 列和第 3 列顯示在提示框(tooltip)中?
答:
series: {
encode: {
tooltip: [1, 2]
...
},
...
}
問:數據裏沒有維度名,那麼怎麼給出維度名?
答:
dataset: {
dimensions: ['score', 'amount'],
source: [
[89.3, 3371],
[92.1, 8123],
[94.4, 1954],
[85.4, 829]
]
}
問:如何把第三列映射爲氣泡圖的點的大小?
答:
var option = {
dataset: {
source: [
[12, 323, 11.2],
[23, 167, 8.3],
[81, 284, 12],
[91, 413, 4.1],
[13, 287, 13.5]
]
},
visualMap: {
show: false,
dimension: 2, // 指向第三列(列序號從 0 開始記,所以設置爲 2)。
min: 2, // 需要給出數值範圍,最小數值。
max: 15, // 需要給出數值範圍,最大數值。
inRange: {
// 氣泡尺寸:5 像素到 60 像素。
symbolSize: [5, 60]
}
},
xAxis: {},
yAxis: {},
series: {
type: 'scatter'
}
};
問:encode 裏指定了映射,但是不管用?
答:可以查查有沒有拼錯,比如,維度名是:'Life Expectancy'
,encode 中拼成了 'Life Expectency'
。
數據的各種格式
多數常見圖表中,數據適於用二維表的形式描述。廣爲使用的數據表格軟件(如 MS Excel、Numbers)或者關係數據數據庫都是二維表。他們的數據可以導出成 JSON 格式,輸入到 dataset.source
中,在不少情況下可以免去一些數據處理的步驟。
假如數據導出成 csv 文件,那麼可以使用一些 csv 工具如 dsv 或者 PapaParse 將 csv 轉成 JSON。
在 JavaScript 常用的數據傳輸格式中,二維數組可以比較直觀的存儲二維表。前面的示例都是使用二維數組表示。
除了二維數組以外,dataset 也支持例如下面 key-value 方式的數據格式,這類格式也非常常見。但是這類格式中,目前並不支持 seriesLayoutBy 參數。
dataset: [{
// 按行的 key-value 形式(對象數組),這是個比較常見的格式。
source: [
{product: 'Matcha Latte', count: 823, score: 95.8},
{product: 'Milk Tea', count: 235, score: 81.4},
{product: 'Cheese Cocoa', count: 1042, score: 91.2},
{product: 'Walnut Brownie', count: 988, score: 76.9}
]
}, {
// 按列的 key-value 形式。
source: {
'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],
'count': [823, 235, 1042, 988],
'score': [95.8, 81.4, 91.2, 76.9]
}
}]
多個 dataset 和他們的引用
可以同時定義多個 dataset。系列可以通過 series.datasetIndex 來指定引用哪個 dataset。例如:
var option = {
dataset: [{
// 序號爲 0 的 dataset。
source: [...],
}, {
// 序號爲 1 的 dataset。
source: [...]
}, {
// 序號爲 2 的 dataset。
source: [...]
}],
series: [{
// 使用序號爲 2 的 dataset。
datasetIndex: 2
}, {
// 使用序號爲 1 的 dataset。
datasetIndex: 1
}]
}
ECharts 3 的數據設置方式(series.data)仍正常使用
ECharts 4 之前一直以來的數據聲明方式仍然被正常支持,如果系列已經聲明瞭 series.data, 那麼就會使用 series.data 而非 dataset
。
{
xAxis: {
type: 'category'
data: ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie']
},
yAxis: {},
series: [{
type: 'bar',
name: '2015',
data: [89.3, 92.1, 94.4, 85.4]
}, {
type: 'bar',
name: '2016',
data: [95.8, 89.4, 91.2, 76.9]
}, {
type: 'bar',
name: '2017',
data: [97.7, 83.1, 92.5, 78.1]
}]
}
其實,series.data 也是種會一直存在的重要設置方式。一些特殊的非 table 格式的圖表,如 treemap、graph、lines 等,現在仍不支持在 dataset 中設置,仍然需要使用 series.data。另外,對於巨大數據量的渲染(如百萬以上的數據量),需要使用 appendData 進行增量加載,這種情況不支持使用 dataset
。
其他
目前並非所有圖表都支持 dataset。支持 dataset 的圖表有: line
、bar
、pie
、scatter
、effectScatter
、parallel
、candlestick
、map
、funnel
、custom
。 後續會有更多的圖表進行支持。
最後,給出一個示例,多個圖表共享一個 dataset
,並帶有聯動交互: