LoginSignup
0
3

More than 1 year has passed since last update.

Swift Charts で箱ひげ図を描く

Posted at

概要

Swift Charts には6つの mark があります。Mark とは、図を構成する単位のことです。
WWDC では、これらの mark を組み合わせることで様々なグラフを作ることができると言っていました。

Screen Shot 2022-06-30 at 9.53.52.png
WWDC 2022 Swift Charts: Raise the bar

今回は、WWDC の動画で例に挙がっていた箱ひげ図を書いてみます。スクリーンショットは載せられないのですが、以下の箱ひげ図とほぼ同じものができます。

Screen Shot 2022-06-30 at 9.54.49.png
WWDC 2022 Swift Charts: Raise the bar

コード

Swift Charts を使うには Charts パッケージを import します。

import Charts

まず、データの準備です。今回は箱ひげ図用の BoxPlotData と外れ値用の PointData という2つの struct を定義しました。

struct PointData: Identifiable {
    var value: Double
    var id = UUID()
}

struct BoxplotData: Identifiable {
    var name: String
    var maximum: Double
    var thirdQuantile: Double
    var median: Double
    var firstQuantile: Double
    var minimum: Double
    var outliers: [PointData]
    var id = UUID()
}

let type1Data: [BoxplotData] = [
    .init(name: "A", maximum: 180, thirdQuantile: 160, median: 150, firstQuantile: 140, minimum: 90, outliers: [.init(value: 190), .init(value: 195), .init(value: 210), .init(value: 80), .init(value: 78)]),
    .init(name: "B", maximum: 180, thirdQuantile: 160, median: 150, firstQuantile: 140, minimum: 90, outliers: [.init(value: 190), .init(value: 195), .init(value: 210), .init(value: 80), .init(value: 78)])
]

let type2Data: [BoxplotData] = [
    .init(name: "A", maximum: 160, thirdQuantile: 140, median: 130, firstQuantile: 120, minimum: 60, outliers: [.init(value: 170), .init(value: 175), .init(value: 180), .init(value: 60), .init(value: 58)]),
    .init(name: "B", maximum: 160, thirdQuantile: 140, median: 130, firstQuantile: 120, minimum: 60, outliers: [.init(value: 170), .init(value: 175), .init(value: 180), .init(value: 60), .init(value: 58)])
]

let allData: [(type: String, data: [BoxplotData])] = [
    (type: "Type 1", data: type1Data),
    (type: "Type 2", data: type2Data)
]

では、実際に書いていきます。

struct ContentView: View {
    var body: some View {
        Chart {
            ForEach(allData, id: \.type) { typeData in
                ForEach(typeData.data, id: \.name) { dat in

                    // 箱
                    BarMark(
                        x: .value("Name", dat.name),
                        yStart: .value("First Quantile", dat.firstQuantile),
                        yEnd: .value("Third Quantile", dat.thirdQuantile)
                    )

                    // 縦線
                    RuleMark(
                        x: .value("Name", dat.name),
                        yStart: .value("Minimum", dat.minimum),
                        yEnd: .value("Maximum", dat.maximum)
                    )

                    // 最小値の横線
                    RectangleMark(
                        x: .value("Name", dat.name),
                        y: .value("Minimum", dat.minimum),
                        width: .ratio(0.2),
                        height: 2
                    )

                    // 最大値の横線
                    RectangleMark(
                        x: .value("Name", dat.name),
                        y: .value("Maximum", dat.maximum),
                        width: .ratio(0.2),
                        height: 2
                    )

                    // 中央値の横線
                    RectangleMark(
                        x: .value("Name", dat.name),
                        y: .value("Median", dat.median),
                        height: 2
                    )
                    .foregroundStyle(.black)

                    // 外れ値
                    ForEach(dat.outliers) { outlier in
                        PointMark(
                            x: .value("Name", dat.name),
                            y: .value("Value", outlier.value)
                        )
                        .opacity(0.6)
                    }
                }
                .foregroundStyle(by: .value("Type", typeData.type))
                .position(by: .value("Type", typeData.type))
            }
        }
        .padding(20)
    }
}

箱のプロットに BarMark を使っていますが、これは RectangleMark に置き換えても同じ結果になります:

                    RectangleMark(
                        x: .value("Name", dat.name),
                        yStart: .value("First Quantile", dat.firstQuantile),
                        yEnd: .value("Third Quantile", dat.thirdQuantile)
                    )

まとめ

今回は Bar、Rule、Rectangle、Point の4つの mark を使って箱ひげ図が作れることが分かりました。このように、複数の mark を使って様々なグラフを作ることができそうです。

参考

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3