細(xì)品Swift - 一目了然的圖表Swift Charts(2)
Bar Chart 條形圖
Bar Chart的維基百科解釋如下:
條形圖(英語(yǔ):bar chart),或 條圖(英語(yǔ):bar graph)芋忿,臺(tái)灣常稱(chēng)為 長(zhǎng)條圖,又稱(chēng)為 柱狀圖、棒形圖,是一種以長(zhǎng)方形的長(zhǎng)度為變量的統(tǒng)計(jì)圖表活孩。長(zhǎng)條圖用來(lái)比較兩個(gè)或以上的價(jià)值(不同時(shí)間或者不同條件),只有一個(gè)變量乖仇,通常利用于較小的數(shù)據(jù)集分析。長(zhǎng)條圖亦可橫向排列询兴,或用多維方式表達(dá)乃沙。繪制長(zhǎng)條圖時(shí),長(zhǎng)條柱或柱組中線(xiàn)須對(duì)齊項(xiàng)目刻度诗舰。相較之下警儒,折線(xiàn)圖則是將數(shù)據(jù)代表之點(diǎn)對(duì)齊項(xiàng)目刻度。在數(shù)字大且接近時(shí)眶根,兩者皆可使用波浪形省略符號(hào)蜀铲,以擴(kuò)大表現(xiàn)數(shù)據(jù)間的差距,增強(qiáng)理解和清晰度属百。
長(zhǎng)條圖通常適用于較小的數(shù)據(jù)集分析记劝,其每個(gè)數(shù)據(jù)標(biāo)記通常表示一個(gè)分類(lèi)的數(shù)量或者幾個(gè)分類(lèi)的數(shù)量總和。
基本BarChart
在一個(gè)圖表中可以對(duì)同一數(shù)據(jù)使用不同的標(biāo)記來(lái)描繪族扰,如BarMark是條狀圖樣式的標(biāo)記厌丑,PointMark是點(diǎn)狀樣式的標(biāo)記定欧,LineMark是折線(xiàn)樣式的標(biāo)記。
BarChart可以從不同的維度來(lái)對(duì)數(shù)據(jù)進(jìn)行可視化怒竿,比如本圖表按照食物的名字或者食物的種類(lèi)砍鸠,分類(lèi)來(lái)統(tǒng)計(jì)對(duì)應(yīng)的食物總重量。
BarChart會(huì)將x值相同的數(shù)據(jù)堆疊在一個(gè)條形圖上耕驰,通過(guò)修飾語(yǔ)句.foregroundStyle可以把同一條形上的不同數(shù)據(jù)按照指定的value分開(kāi)爷辱。 這里指定的value會(huì)被當(dāng)作圖例使用。同樣朦肘,可以指定圖例的顏色饭弓,這里是通過(guò)對(duì)Chart的修飾語(yǔ)句.chartForegroundStyleScale來(lái)指定圖例對(duì)應(yīng)的顏色。
var basicBarChart: some View {
VStack {
VStack {
Divider()
Text("用$0直接表示單個(gè)數(shù)據(jù)").font(.footnote)
// Chart使用類(lèi)似于List的方式厚骗,從foods中直接拆解處單個(gè)數(shù)據(jù)并繪制
Chart(foods) {
BarMark(x: .value("name", $0.name), y: .value("weight", $0.weight))
}
}
VStack {
Divider()
Text("數(shù)據(jù)標(biāo)記的選擇決定了圖表的類(lèi)別").font(.footnote)
// 數(shù)據(jù)標(biāo)記(mark)可以選擇多種表示方式(點(diǎn)PointMark示启、條形BarMark、折線(xiàn)LineMark领舰、矩形RectangleMark等)
// 本圖表對(duì)每個(gè)數(shù)據(jù)選擇了兩種標(biāo)記方式: BarMark和LineMark
Chart(foods) { food in
BarMark(x: .value("name", food.name), y: .value("weight", food.weight))
LineMark(x: .value("name", food.name), y: .value("weight", food.weight))
.foregroundStyle(.red)
}
}
VStack {
Divider()
Text("從不同維度對(duì)數(shù)據(jù)進(jìn)行觀察的BarChart").font(.footnote)
// 可以選擇不同的數(shù)據(jù)屬性作為X軸夫嗓,以從不同的維度對(duì)數(shù)據(jù)進(jìn)行觀察
// 如多個(gè)數(shù)據(jù)具有相同的x軸維度值,Chart會(huì)把他們直接合并冲秽,顯示所有對(duì)應(yīng)的y軸值之和舍咖。
// 本例中把所有的數(shù)據(jù)按照type維度,分成4個(gè)條狀圖锉桑。
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
}
}
VStack {
Divider()
Text("合并后在每個(gè)Bar中顯示原始數(shù)據(jù)所占部分").font(.footnote)
// 使用修飾指令.foregroundStyle(by: PlottableValue)
// 可以看出排霉,圖表中多了legend(圖例)部分,指定每種顏色對(duì)應(yīng)的數(shù)據(jù)民轴,但可以看出
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同攻柠,所有都在Fruit Bar上,進(jìn)一步后裸,他們的顏色都是Green瑰钮,所以以同樣的顏色顯示在Bar上。
.foregroundStyle(by: .value("color", food.color))
}
}
VStack {
Divider()
Text("指定顯示每種圖例對(duì)應(yīng)的顏色").font(.footnote)
// 用chartForgroundStyleScale來(lái)指定每種圖例對(duì)應(yīng)的顏色
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同微驶,所有都在Fruit Bar上浪谴,進(jìn)一步,他們的顏色都是Green因苹,所以以同樣的顏色顯示在Bar上苟耻。
.foregroundStyle(by: .value("color", food.color.description))
}
.chartForegroundStyleScale([
"yellow-color": .yellow,
"green-color": .green,
"red-color": .red,
"white-color": .gray.opacity(0.5), // 用半透明的gray來(lái)標(biāo)識(shí)白色
"pink-color": .pink,
"gray-color": .gray
])
}
VStack {
Divider()
Text("從顏色維度來(lái)分類(lèi)并顯示每條Bar中食物種類(lèi)分布").font(.footnote)
Chart(foods) { food in
BarMark(x: .value("color", food.color) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同,所有都在Fruit Bar上扶檐,進(jìn)一步凶杖,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上款筑。
.foregroundStyle(by: .value("type", food.type))
}
// 按照食物種類(lèi)指定顏色
.chartForegroundStyleScale([
"Meat": .pink,
"Vegetable": .green,
"Grain": .brown,
"Fruit": .orange
])
}
}
}
一維BarChart和區(qū)間BarChart
BarChart可以用于展示一維數(shù)據(jù)圖以及區(qū)間圖官卡。
如果需要在一維上展示數(shù)據(jù)蝗茁,只需要輸入x或y中的一個(gè)數(shù)據(jù),以及圖例基于的數(shù)據(jù)寻咒。
區(qū)間圖需要提供在一個(gè)維度上的數(shù)據(jù)的上下區(qū)間值哮翘,已經(jīng)另外一個(gè)維度的數(shù)據(jù)屬性。
var advancedBarChart: some View {
VStack {
VStack {
Divider()
Text("一維條形圖").font(.footnote)
// 按照種類(lèi)排序后毛秘,同種類(lèi)的食物連續(xù)顯示
Chart(foods.sorted(by: {$0.type < $1.type})) {
BarMark(x: .value("weight", $0.weight))
.foregroundStyle(by: .value("type", $0.type))
}
}
VStack {
Divider()
Text("區(qū)間條形圖").font(.footnote)
// 用條狀圖來(lái)標(biāo)識(shí)一個(gè)區(qū)間饭寺,特別是時(shí)間區(qū)間,如甘特圖叫挟。
Chart(foods) { food in
BarMark(x: .value("name", food.name),
yStart: .value("minGi", food.minGiValue),
yEnd: .value("maxGi", food.maxGiValue)
)
}
}
VStack {
Divider()
Text("橫向區(qū)間條形圖").font(.footnote)
Chart(foods) { food in
BarMark(xStart: .value("minGi", food.minGiValue), xEnd: .value("maxGi", food.maxGiValue),
y: .value("color", food.color))
.foregroundStyle(by: .value("type", food.type))
}
}
}
}
完整演示代碼
完整的演示代碼如下:
struct Food: Identifiable {
let id = UUID()
let name: String
let maxGiValue: Int
let minGiValue: Int
let maxLength: Double
let minLength: Double
let color: String
let type: String
let weight: Double
}
let foods: [Food] = [
Food(name: "Cabbage", maxGiValue: 13, minGiValue: 8, maxLength: 23, minLength: 10, color: "green-color", type: "Vegetable", weight: 2),
Food(name: "Beef", maxGiValue: 54, minGiValue: 18, maxLength: 53, minLength: 20, color: "pink-color", type: "Meat", weight: 1.8),
Food(name: "Rice", maxGiValue: 93, minGiValue: 78, maxLength: 5, minLength: 2, color: "gray-color", type: "Grain", weight: 8),
Food(name: "Tomato", maxGiValue: 33, minGiValue: 21, maxLength: 13, minLength: 5, color: "red-color", type: "Vegetable", weight: 5),
Food(name: "Pork", maxGiValue: 63, minGiValue: 38, maxLength: 63, minLength: 10, color: "pink-color", type: "Meat", weight: 5.3),
Food(name: "Fish", maxGiValue: 49, minGiValue: 28, maxLength: 53, minLength: 5, color: "white-color", type: "Meat", weight: 1.5),
Food(name: "Flour", maxGiValue: 83, minGiValue: 3, maxLength: 0.1, minLength: 10, color: "white-color", type: "Grain", weight: 6.5),
Food(name: "Banana", maxGiValue: 93, minGiValue: 78, maxLength: 33, minLength: 10, color: "yellow-color", type: "Fruit", weight: 3.8),
Food(name: "Avocado", maxGiValue: 33, minGiValue: 18, maxLength: 25, minLength: 8, color: "green-color", type: "Fruit", weight: 8),
Food(name: "GreenGrape", maxGiValue: 99, minGiValue: 68, maxLength: 23, minLength: 10, color: "green-color", type: "Fruit", weight: 8)
]
struct AllAboutBarChartTest: View {
var body: some View {
basicBarChart
advancedBarChart
}
var basicBarChart: some View {
VStack {
VStack {
Divider()
Text("用$0直接表示單個(gè)數(shù)據(jù)").font(.footnote)
// Chart使用類(lèi)似于List的方式艰匙,從foods中直接拆解處單個(gè)數(shù)據(jù)并繪制
Chart(foods) {
BarMark(x: .value("name", $0.name), y: .value("weight", $0.weight))
}
}
VStack {
Divider()
Text("數(shù)據(jù)標(biāo)記的選擇決定了圖表的類(lèi)別").font(.footnote)
// 數(shù)據(jù)標(biāo)記(mark)可以選擇多種表示方式(點(diǎn)PointMark、條形BarMark抹恳、折線(xiàn)LineMark员凝、矩形RectangleMark等)
// 本圖表對(duì)每個(gè)數(shù)據(jù)選擇了兩種標(biāo)記方式: BarMark和LineMark
Chart(foods) { food in
BarMark(x: .value("name", food.name), y: .value("weight", food.weight))
LineMark(x: .value("name", food.name), y: .value("weight", food.weight))
.foregroundStyle(.red)
}
}
VStack {
Divider()
Text("從不同維度對(duì)數(shù)據(jù)進(jìn)行觀察的BarChart").font(.footnote)
// 可以選擇不同的數(shù)據(jù)屬性作為X軸,以從不同的維度對(duì)數(shù)據(jù)進(jìn)行觀察
// 如多個(gè)數(shù)據(jù)具有相同的x軸維度值奋献,Chart會(huì)把他們直接合并健霹,顯示所有對(duì)應(yīng)的y軸值之和。
// 本例中把所有的數(shù)據(jù)按照type維度瓶蚂,分成4個(gè)條狀圖糖埋。
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
}
}
VStack {
Divider()
Text("合并后在每個(gè)Bar中顯示原始數(shù)據(jù)所占部分").font(.footnote)
// 使用修飾指令.foregroundStyle(by: PlottableValue)
// 可以看出,圖表中多了legend(圖例)部分窃这,指定每種顏色對(duì)應(yīng)的數(shù)據(jù)瞳别,但可以看出
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同,所有都在Fruit Bar上杭攻,進(jìn)一步祟敛,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上兆解。
.foregroundStyle(by: .value("color", food.color))
}
}
VStack {
Divider()
Text("指定顯示每種圖例對(duì)應(yīng)的顏色").font(.footnote)
// 用chartForgroundStyleScale來(lái)指定每種圖例對(duì)應(yīng)的顏色
Chart(foods) { food in
BarMark(x: .value("type", food.type) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同垒棋,所有都在Fruit Bar上,進(jìn)一步痪宰,他們的顏色都是Green,所以以同樣的顏色顯示在Bar上畔裕。
.foregroundStyle(by: .value("color", food.color.description))
}
.chartForegroundStyleScale([
"yellow-color": .yellow,
"green-color": .green,
"red-color": .red,
"white-color": .gray.opacity(0.5), // 用半透明的gray來(lái)標(biāo)識(shí)白色
"pink-color": .pink,
"gray-color": .gray
])
}
VStack {
Divider()
Text("從顏色維度來(lái)分類(lèi)并顯示每條Bar中食物種類(lèi)分布").font(.footnote)
Chart(foods) { food in
BarMark(x: .value("color", food.color) ,
y: .value("weight", food.weight))
// 在每個(gè)Bar上為每個(gè)color屬性的值賦不同的顏色
// 同一bar上同顏色的數(shù)據(jù)再次合并, 如Avocado和GreenGrape的食物類(lèi)型相同衣撬,所有都在Fruit Bar上,進(jìn)一步扮饶,他們的顏色都是Green具练,所以以同樣的顏色顯示在Bar上。
.foregroundStyle(by: .value("type", food.type))
}
// 按照食物種類(lèi)指定顏色
.chartForegroundStyleScale([
"Meat": .pink,
"Vegetable": .green,
"Grain": .brown,
"Fruit": .orange
])
}
}
}
var advancedBarChart: some View {
VStack {
VStack {
Divider()
Text("一維條形圖").font(.footnote)
// 按照種類(lèi)排序后甜无,同種類(lèi)的食物連續(xù)顯示
Chart(foods.sorted(by: {$0.type < $1.type})) {
BarMark(x: .value("weight", $0.weight))
.foregroundStyle(by: .value("type", $0.type))
}
}
VStack {
Divider()
Text("區(qū)間條形圖").font(.footnote)
// 用條狀圖來(lái)標(biāo)識(shí)一個(gè)區(qū)間扛点,特別是時(shí)間區(qū)間哥遮,如甘特圖。
Chart(foods) { food in
BarMark(x: .value("name", food.name),
yStart: .value("minGi", food.minGiValue),
yEnd: .value("maxGi", food.maxGiValue)
)
}
}
VStack {
Divider()
Text("橫向區(qū)間條形圖").font(.footnote)
Chart(foods) { food in
BarMark(xStart: .value("minGi", food.minGiValue), xEnd: .value("maxGi", food.maxGiValue),
y: .value("color", food.color))
.foregroundStyle(by: .value("type", food.type))
}
}
}
}
}
總結(jié)如下:
- BarChart中文名稱(chēng)為條狀圖陵究,柱狀圖等眠饮,是圖形化少量數(shù)據(jù)的有效方法。
- BarChart可以在多種維度上展示數(shù)據(jù)铜邮。
- BarChart可以用圖例更詳細(xì)的演示堆疊在一個(gè)Bar上的多種數(shù)據(jù)仪召。
- BarChart可以以一維的方式展示數(shù)據(jù)。
- BarChart可以展示數(shù)據(jù)區(qū)間松蒜。