使用transform進行數據轉換
# 使用 transform 進行數據轉換
ECharts5 開始支援了「數據轉換」( data transform )功能。在 echarts 中,「數據轉換」 這個詞指的是,給定一個已有的「數據集」(dataset)和一個「轉換方法」(transform),echarts 能產生一個新的「數據集」,然後可以使用這個新的「數據集」繪製圖表。這些工作都可以聲明式地完成。
抽像地來說,數據轉換是這樣一種公式:outData = f(inputData)
。f
是轉換方法,例如:filter
、sort
、regression
、boxplot
、cluster
、aggregate
(todo) 等等。有了數據轉換能力后,我們就至少可以做到這些事情:
- 把數據分成多份用不同的餅圖展現。
- 進行一些數據統計運算,並展示結果。
- 用某些數據視覺化演算法處理數據,並展示結果。
- 數據排序。
- 去除或直選擇數據項。
- ...
# 1. 數據轉換基礎使用
在 echarts 中,數據轉換是依託于數據集(dataset)來實現的. 我們可以設定 dataset.transform 來表示,此 dataset 的數據,來自於此 transform 的結果。例如。
var option = {
dataset: [{
// 這個 dataset 的 index 是 `0`。
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Cereal', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Dumpling', 341, 25, 2011],
['Biscuit', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Cereal', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Dumpling', 241, 27, 2012],
['Biscuit', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Cereal', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Dumpling', 281, 31, 2013],
['Biscuit', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Cereal', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Dumpling', 211, 35, 2014],
['Biscuit', 72, 24, 2014],
],
// id: 'a'
}, {
// 這個 dataset 的 index 是 `1`。
// 這個 `transform` 配置,表示,此 dataset 的數據,來自於此 transform 的結果。
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2011 }
},
// 我們還可以設定這些可選的屬性: `fromDatasetIndex` 或 `fromDatasetId`。
// 這些屬性,指定了,transform 的輸入,來自於哪個 dataset。例如,
// `fromDatasetIndex: 0` 表示輸入來自於 index 為 `0` 的 dataset 。又例如,
// `fromDatasetId: 'a'` 表示輸入來自於 `id: 'a'` 的 dataset。
// 當這些屬性都不指定時,預設認為,輸入來自於 index 為 `0` 的 dataset 。
}, {
// 這個 dataset 的 index 是 `2`。
// 同樣,這裡因為 `fromDatasetIndex` 和 `fromDatasetId` 都沒有被指定,
// 那麼輸入預設來自於 index 為 `0` 的 dataset 。
transform: {
// 這個型別為 "filter" 的 transform 能夠遍歷並篩選出滿足條件的數據項。
type: 'filter',
// 每個 transform 如果需要有配置參數的話,都須配置在 `config` 里。
// 在這個 "filter" transform 中,`config` 用於指定篩選條件。
// 下面這個篩選條件是:選出維度( dimension )'Year' 中值為 2012 的所有
// 數據項。
config: { dimension: 'Year', value: 2012 }
}
}, {
// 這個 dataset 的 index 是 `3`。
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2013 }
}
}],
series: [{
type: 'pie', radius: 50, center: ['25%', '50%'],
// 這個餅圖系列,引用了 index 為 `1` 的 dataset 。也就是,引用了上述
// 2011 年那個 "filter" transform 的結果。
datasetIndex: 1
}, {
type: 'pie', radius: 50, center: ['50%', '50%'],
datasetIndex: 2
}, {
type: 'pie', radius: 50, center: ['75%', '50%'],
datasetIndex: 3
}]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
- FastWeb TUgURLFrame示例:
如需在FastWeb中的TUgURLFrame中實現上述效果,則修改其HTML
屬性,使用下列內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js"></script>
</head>
<body>
<!-- 為ECharts準備一個具備大小(寬高)的Dom -->
<div id="main" style="width: 800px;height:400px;"></div>
<script type="text/javascript">
// 基於準備好的dom,初始化echarts實體
var myChart = echarts.init(document.getElementById('main'));
var option;
option = {
dataset: [{
// 這個 dataset 的 index 是 `0`。
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Cereal', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Dumpling', 341, 25, 2011],
['Biscuit', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Cereal', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Dumpling', 241, 27, 2012],
['Biscuit', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Cereal', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Dumpling', 281, 31, 2013],
['Biscuit', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Cereal', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Dumpling', 211, 35, 2014],
['Biscuit', 72, 24, 2014],
],
// id: 'a'
}, {
// 這個 dataset 的 index 是 `1`。
// 這個 `transform` 配置,表示,此 dataset 的數據,來自於此 transform 的結果。
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2011 }
},
// 我們還可以設定這些可選的屬性: `fromDatasetIndex` 或 `fromDatasetId`。
// 這些屬性,指定了,transform 的輸入,來自於哪個 dataset。例如,
// `fromDatasetIndex: 0` 表示輸入來自於 index 為 `0` 的 dataset 。又例如,
// `fromDatasetId: 'a'` 表示輸入來自於 `id: 'a'` 的 dataset。
// 當這些屬性都不指定時,預設認為,輸入來自於 index 為 `0` 的 dataset 。
}, {
// 這個 dataset 的 index 是 `2`。
// 同樣,這裡因為 `fromDatasetIndex` 和 `fromDatasetId` 都沒有被指定,
// 那麼輸入預設來自於 index 為 `0` 的 dataset 。
transform: {
// 這個型別為 "filter" 的 transform 能夠遍歷並篩選出滿足條件的數據項。
type: 'filter',
// 每個 transform 如果需要有配置參數的話,都須配置在 `config` 里。
// 在這個 "filter" transform 中,`config` 用於指定篩選條件。
// 下面這個篩選條件是:選出維度( dimension )'Year' 中值為 2012 的所有
// 數據項。
config: { dimension: 'Year', value: 2012 }
}
}, {
// 這個 dataset 的 index 是 `3`。
transform: {
type: 'filter',
config: { dimension: 'Year', value: 2013 }
}
}],
series: [{
type: 'pie', radius: 50, center: ['25%', '50%'],
// 這個餅圖系列,引用了 index 為 `1` 的 dataset 。也就是,引用了上述
// 2011 年那個 "filter" transform 的結果。
datasetIndex: 1
}, {
type: 'pie', radius: 50, center: ['50%', '50%'],
datasetIndex: 2
}, {
type: 'pie', radius: 50, center: ['75%', '50%'],
datasetIndex: 3
}]
};
// 使用剛指定的配置項和數據顯示圖表。
myChart.setOption(option);
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
下面是上述例子的效果,三個餅圖分別顯示了 2011、2012、2013 年的數據。
- 圖表來源:https://echarts.apache.org/examples/zh/editor.html?c=data-transform-multiple-pie (opens new window)
現在我們簡單總結下,使用 transform 時的幾個要點:
- 在一個空的 dataset 中聲明
transform
,fromDatasetIndex
/fromDatasetId
來表示我們要產生新的數據。 - 系列引用這個 dataset 。
# 2. 數據轉換的進階使用
# 2.1. 鏈式聲明 transform
transform
可以被鏈式聲明,這是一個語法糖。
option = {
dataset: [{
source: [ ... ] // 原始數據
}, {
// 幾個 transform 被聲明成 array ,他們構成了一個鏈,
// 前一個 transform 的輸出是后一個 transform 的輸入。
transform: [{
type: 'filter',
config: { dimension: 'Product', value: 'Tofu' }
}, {
type: 'sort',
config: { dimension: 'Year', order: 'desc' }
}]
}],
series: {
type: 'pie',
// 這個系列引用上述 transform 的結果。
datasetIndex: 1
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
注意:理論上,任何 transform 都可能有多個輸入或多個輸出。但是,如果一個 transform 被鏈式聲明,它只能獲取前一個 transform 的第一個輸出作為輸入(第一個 transform 除外),以及它只能把自己的第一個輸出給到后一個 transform (最後一個 transform 除外)。
# 2.2. 一個 transform 輸出多個 data
在大多數場景下,transform 只需輸出一個 data 。但是也有一些場景,需要輸出多個 data ,每個 data 可以被不同的 series 或者 dataset 所使用。
例如,在內建的 "boxplot" transform 中,除了 boxplot 系列所需要的 data 外,離群點( outlier )也會被產生,並且可以用例如散點圖系列顯示出來。例如,example (opens new window)。
我們提供配置 dataset.fromTransformResult 來滿足這種情況,例如:
option = {
dataset: [{
// 這個 dataset 的 index 為 `0`。
source: [...] // 原始數據
}, {
// 這個 dataset 的 index 為 `1`。
transform: {
type: 'boxplot'
}
// 這個 "boxplot" transform 產生了兩個數據:
// result[0]: boxplot series 所需的數據。
// result[1]: 離群點數據。
// 當其他 series 或者 dataset 引用這個 dataset 時,他們預設只能得到
// result[0] 。
// 如果想要他們得到 result[1] ,需要額外聲明如下這樣一個 dataset :
}, {
// 這個 dataset 的 index 為 `2`。
// 這個額外的 dataset 指定了數據來源於 index 為 `1` 的 dataset。
fromDatasetIndex: 1,
// 並且指定了獲取 transform result[1] 。
fromTransformResult: 1
}],
xAxis: {
type: 'category'
},
yAxis: {
},
series: [{
name: 'boxplot',
type: 'boxplot',
// Reference the data from result[0].
// 這個 series 引用 index 為 `1` 的 dataset 。
datasetIndex: 1
}, {
name: 'outlier',
type: 'scatter',
// 這個 series 引用 index 為 `2` 的 dataset 。
// 從而也就得到了上述的 transform result[1] (即離群點數據)
datasetIndex: 2
}]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
另外,dataset.fromTransformResult 和 dataset.transform 能同時出現在一個 dataset 中,這表示,這個 transform 的輸入,是上游的結果中以 fromTransformResult
獲取的結果。例如:
{
fromDatasetIndex: 1,
fromTransformResult: 1,
transform: {
type: 'sort',
config: { dimension: 2, order: 'desc' }
}
}
2
3
4
5
6
7
8
# 2.3. 在開發環境中 debug
使用 transform 時,有時候我們會配不對,顯示不出來結果,並且不知道哪裡錯了。所以,這裡提供了一個配置項 transform.print
方便 debug 。這個配置項只在開發環境中生效。如下例:
option = {
dataset: [{
source: [ ... ]
}, {
transform: {
type: 'filter',
config: { ... }
// 配置為 `true` 后, transform 的結果
// 會被 console.log 列印出來。
print: true
}
}],
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 數據轉換器 "filter"
echarts 內建提供了能起過濾作用的數據轉換器。我們只需聲明 transform.type: "filter"
,以及給出數據篩選條件。如下例:
option = {
dataset: [{
source: [
['Product', 'Sales', 'Price', 'Year'],
['Cake', 123, 32, 2011],
['Latte', 231, 14, 2011],
['Tofu', 235, 5, 2011],
['Milk Tee', 341, 25, 2011],
['Porridge', 122, 29, 2011],
['Cake', 143, 30, 2012],
['Latte', 201, 19, 2012],
['Tofu', 255, 7, 2012],
['Milk Tee', 241, 27, 2012],
['Porridge', 102, 34, 2012],
['Cake', 153, 28, 2013],
['Latte', 181, 21, 2013],
['Tofu', 395, 4, 2013],
['Milk Tee', 281, 31, 2013],
['Porridge', 92, 39, 2013],
['Cake', 223, 29, 2014],
['Latte', 211, 17, 2014],
['Tofu', 345, 3, 2014],
['Milk Tee', 211, 35, 2014],
['Porridge', 72, 24, 2014]
]
}, {
transform: {
type: 'filter',
config: { dimension: 'Year', '=': 2011 }
// 這個篩選條件表示,遍歷數據,篩選出維度( dimension )
// 'Year' 上值為 2011 的所有數據項。
}
}],
series: {
type: 'pie',
datasetIndex: 1
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
這是 filter 的另一個例子的寫法:
- FastWeb TUgURLFrame示例:
如需在FastWeb中的TUgURLFrame中實現上述效果,則修改其HTML
屬性,使用下列內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js"></script>
</head>
<body>
<!-- 為ECharts準備一個具備大小(寬高)的Dom -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基於準備好的dom,初始化echarts實體
var myChart = echarts.init(document.getElementById('main'));
var option;
var ROOT_PATH = 'https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/examples';
$.get(ROOT_PATH + '/data/asset/data/life-expectancy-table.json', function (_rawData) {
run(_rawData);
});
function run(_rawData) {
option = {
dataset: [{
id: 'dataset_raw',
source: _rawData
}, {
id: 'dataset_since_1950_of_germany',
fromDatasetId: 'dataset_raw',
transform: {
type: 'filter',
config: {
and: [
{ dimension: 'Year', gte: 1950 },
{ dimension: 'Country', '=': 'Germany' }
]
}
}
}, {
id: 'dataset_since_1950_of_france',
fromDatasetId: 'dataset_raw',
transform: {
type: 'filter',
config: {
and: [
{ dimension: 'Year', gte: 1950 },
{ dimension: 'Country', '=': 'France' }
]
}
}
}],
title: {
text: 'Income of Germany and France since 1950'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
nameLocation: 'middle'
},
yAxis: {
name: 'Income'
},
series: [{
type: 'line',
datasetId: 'dataset_since_1950_of_germany',
showSymbol: false,
encode: {
x: 'Year',
y: 'Income',
itemName: 'Year',
tooltip: ['Income'],
}
}, {
type: 'line',
datasetId: 'dataset_since_1950_of_france',
showSymbol: false,
encode: {
x: 'Year',
y: 'Income',
itemName: 'Year',
tooltip: ['Income'],
}
}]
};
myChart.setOption(option);
}
// 使用剛指定的配置項和數據顯示圖表。
myChart.setOption(option);
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
- 圖表來源:https://echarts.apache.org/examples/zh/editor.html?c=data-transform-filte>
在 "filter" transform 中,有這些要素:
關於維度( dimension ):
config.dimension
指定了維度,能設成這樣的值:
- 設定成聲明在 dataset 中的維度名,例如
config: { dimension: 'Year', '=': 2011 }
。不過, dataset 中維度名的聲明並非強制,所以我們也可以 - 設定成 dataset 中的維度 index (index 值從 0 開始)例如
config: { dimension: 3, '=': 2011 }
。
關於關係比較操作符:
關係操作符,可以設定這些:
>
(gt
)、>=
(gte
)、<
(lt
)、<=
(lte
)、=
(eq
)、!=
(ne
、<>
)、reg
。(小括號中的符號或名字,是別名,設定起來作用相同)。他們首先基本地能基於數值大小進行比較,然後也有些額外的功能特性:
- 多個關係操作符能聲明在一個 {} 中,例如
{ dimension: 'Price', '>=': 20, '<': 30 }
。這表示「與」的關係,即,篩選出價格大於等於 20 小於 30 的數據項。 - data 里的值,不僅可以是數值( number ),也可以是「類數值的字串」(「 numeric string 」)。「類數值的字串」本身是一個字串,但是可以被轉換為字面所描述的數值,例如
' 123 '
。轉換過程中,空格(全形半形空格)和換行符都能被消除( trim )。 - 如果我們需要對日期對像(JS
Date
)或者日期字串(如 '2012-05-12')進行比較,我們需要手動指定parser: 'time'
,例如config: { dimension: 3, lt: '2012-05-12', parser: 'time' }
。 - 純字串比較也被支援,但是隻能用在
=
或!=
上。而>
,>=
,<
,<=
並不支援純字串比較,也就是說,這四個操作符的右值,不能是字串。 reg
操作符能提供正規表示式比較。例如,{ dimension: 'Name', reg: /\s+Müller\s*$/ }
能在'Name'
維度上選出姓'Müller'
的數據項。
關於邏輯比較:
我們也支援了邏輯比較操作符 與或非( and
| or
| not
):
option = {
dataset: [{
source: [...]
}, {
transform: {
type: 'filter',
config: {
// 使用 and 操作符。
// 類似地,同樣的位置也可以使用 「or」 或 「not」。
// 但是注意 「not」 后應該跟一個 {...} 而非 [...] 。
and: [
{ dimension: 'Year', '=': 2011 },
{ dimension: 'Price', '>=': 20, '<': 30 }
]
}
// 這個表達的是,選出 2011 年價格大於等於 20 但小於 30 的數據項。
}
}],
series: {
type: 'pie',
datasetIndex: 1
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
and
/or
/not
自然可以被巢狀,例如:
transform: {
type: 'filter',
config: {
or: [{
and: [{
dimension: 'Price', '>=': 10, '<': 20
}, {
dimension: 'Sales', '<': 100
}, {
not: { dimension: 'Product', '=': 'Tofu' }
}]
}, {
and: [{
dimension: 'Price', '>=': 10, '<': 20
}, {
dimension: 'Sales', '<': 100
}, {
not: { dimension: 'Product', '=': 'Cake' }
}]
}]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
關於解析器( parser ):
還可以指定「解析器」( parser )來對值進行解析后再做比較。現在支援的解析器有:
parser: 'time'
:把原始值解析成時間戳( timestamp )后再做比較。這個解析器的行為,和echarts.time.parse
相同,即,當原始值為時間對像( JSDate
實體),或者是時間戳,或者是描述時間的字串(例如'2012-05-12 03:11:22'
),都可以被解析為時間戳,然後就可以基於數值大小進行比較。如果原始數據是其他不可解析為時間戳的值,那麼會被解析為 NaN。parser: 'trim'
:如果原始數據是字串,則把字串兩端的空格(全形半形)和換行符去掉。如果不是字串,還保持為原始數據。parser: 'number'
:強制把原始數據轉成數值。如果不能轉成有意義的數值,那麼轉成NaN
。在大多數場景下,我們並不需要這個解析器,因為按預設策略,「像數值的字串」就會被轉成數值。但是預設策略比較嚴格,這個解析器比較寬鬆,如果我們遇到含有尾綴的字串(例如'33%'
,12px
),我們需要手動指定parser: 'number'
,從而去掉尾綴轉為數值才能比較。
這個例子顯示瞭如何使用 parser: 'time'
:
option = {
dataset: [{
source: [
['Product', 'Sales', 'Price', 'Date'],
['Milk Tee', 311, 21, '2012-05-12'],
['Cake', 135, 28, '2012-05-22'],
['Latte', 262, 36, '2012-06-02'],
['Milk Tee', 359, 21, '2012-06-22'],
['Cake', 121, 28, '2012-07-02'],
['Latte', 271, 36, '2012-06-22'],
...
]
}, {
transform: {
type: 'filter',
config: {
{ dimension: 'Date', '>=': '2012-05', '<': '2012-06', parser: 'time' }
}
}
}]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
形式化定義:
最後,我們給出,數據轉換器 "filter" 的 config 的形式化定義:
type FilterTransform = {
type: 'filter';
config: ConditionalExpressionOption;
};
type ConditionalExpressionOption =
true | false | RelationalExpressionOption | LogicalExpressionOption;
type RelationalExpressionOption = {
dimension: DimensionName | DimensionIndex;
parser?: 'time' | 'trim' | 'number';
lt?: DataValue; // less than
lte?: DataValue; // less than or equal
gt?: DataValue; // greater than
gte?: DataValue; // greater than or equal
eq?: DataValue; // equal
ne?: DataValue; // not equal
'<'?: DataValue; // lt
'<='?: DataValue; // lte
'>'?: DataValue; // gt
'>='?: DataValue; // gte
'='?: DataValue; // eq
'!='?: DataValue; // ne
'<>'?: DataValue; // ne (SQL style)
reg?: RegExp | string; // RegExp
};
type LogicalExpressionOption = {
and?: ConditionalExpressionOption[];
or?: ConditionalExpressionOption[];
not?: ConditionalExpressionOption;
};
type DataValue = string | number | Date;
type DimensionName = string;
type DimensionIndex = number;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 4. 數據轉換器 "sort"
"sort" 是另一個內建的數據轉換器,用於排序數據。目前主要能用於在類目軸( axis.type: 'category'
)中顯示排過序的數據。例如:
option = {
dataset: [{
dimensions: ['name', 'age', 'profession', 'score', 'date'],
source: [
[' Hannah Krause ', 41, 'Engineer', 314, '2011-02-12'],
['Zhao Qian ', 20, 'Teacher', 351, '2011-03-01'],
[' Jasmin Krause ', 52, 'Musician', 287, '2011-02-14'],
['Li Lei', 37, 'Teacher', 219, '2011-02-18'],
[' Karle Neumann ', 25, 'Engineer', 253, '2011-04-02'],
[' Adrian Gro
2
3
4
5
6
7
8
9