Bar Plots

Plotting vertical bars

Bar Plots are also 2-D plots and are therefore plotted within an XYGraph parent. They are typically used with discrete data, and accordingly all of the examples on this page will use CategoryAxisModel for the x-axis. The y-axis can be of any data type, but the examples will all use LinearAxisModel.

Single Vertical Bar Plot

The simplest type of vertical bar plot has a single series of data, and there is a VerticalBarPlot overload to make this use case easy when using Float values for the y-axis. The below example demonstrates this to plot the population of each New York City burrough.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
    val boroughs = listOf("Bronx", "Brooklyn", "Manhattan", "Queens", "Staten Island")
    val population = listOf(1.446788f, 2.648452f, 1.638281f, 2.330295f, 0.487155f)

    XYGraph(
        xAxisModel = remember { CategoryAxisModel(boroughs) },
        yAxisModel = rememberFloatLinearAxisModel(0f..3f, minorTickCount = 0),
        yAxisTitle = "Population (Millions)"
    ) {
        VerticalBarPlot(
            xData = boroughs,
            yData = population,
            bar = {
                DefaultVerticalBar(SolidColor(Color.Blue))
            }
        )
    }
/examples/src/jvmMain/kotlin/io/github/koalaplot/example/VerticalBar1.kt

Vertical bars

In the above example, all bars were automatically extended to the origin of the y-axis, 0. In some cases it is desirable to plot vertical bars with starting positions that are not at 0. To support this use case, there is a second form of VerticalBarPlot that allows specifying the starting and ending coordinates of each bar. The below example demonstrates using this flexibility to create a waterfall chart:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@OptIn(ExperimentalKoalaPlotApi::class)
fun main() = singleWindowApplication {
    val categories = listOf("Initial Cash", "Q1", "Q2", "Q3", "Q4", "Final Cash")
    val colors = listOf(
        Color.DarkGray, Color(0xFF00498F), Color(0xFFED7D31),
        Color(0xFF00498F), Color(0xFF00498F), Color.DarkGray,
    )
    val data = listOf(
        verticalBarPlotEntry(categories[0], 0f, 100f),
        verticalBarPlotEntry(categories[1], 100f, 120f),
        verticalBarPlotEntry(categories[2], 120f, 90f),
        verticalBarPlotEntry(categories[3], 90f, 110f),
        verticalBarPlotEntry(categories[4], 110f, 130f),
        verticalBarPlotEntry(categories[5], 0f, 130f),
    )

    XYGraph(
        xAxisModel = remember { CategoryAxisModel(categories) },
        yAxisModel = rememberFloatLinearAxisModel(0f..150f, minorTickCount = 0),
    ) {
        VerticalBarPlot(
            data,
            bar = { DefaultVerticalBar(SolidColor(colors[it])) }
/examples/src/jvmMain/kotlin/io/github/koalaplot/example/VerticalBar2.kt

Waterfall

There is also a builder DSL for vertical bar plots, that slightly improves on the syntax. The previous example can also be implemented as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fun main() = singleWindowApplication {
    val categories = listOf("Initial Cash", "Q1", "Q2", "Q3", "Q4", "Final Cash")

    XYGraph(
        xAxisModel = remember { CategoryAxisModel(categories) },
        yAxisModel = rememberFloatLinearAxisModel(0f..150f, minorTickCount = 0),
    ) {
        VerticalBarPlot {
            item("Initial Cash", 0f, 100f, solidBar(Color.DarkGray))
            item("Q1", 100f, 120f, solidBar(Color(0xFF00498F)))
            item("Q2", 120f, 90f, solidBar(Color(0xFFED7D31)))
            item("Q3", 90f, 110f, solidBar(Color(0xFF00498F)))
            item("Q4", 110f, 130f, solidBar(Color(0xFF00498F)))
            item("Final Cash", 0f, 130f, solidBar(Color.DarkGray))
        }

Stacked Bars

A stacked bar chart consists of multiple series of data, each of which have data points at the same set of x-axis values. The bars for the data at each x-axis value accumulate and stack on top of each other to form a total. The below example demonstrates a stacked bar plot showing the population for each of the 5 New York City boroughs by year.

 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
    val boroughs = listOf("Bronx", "Brooklyn", "Manhattan", "Queens", "Staten Island")
    val years = listOf(1980, 1990, 2000, 2010, 2020)
    val colors =
        listOf(
            Color.hsv(0f, 1f, 0.7f),
            Color.hsv(72f, 1f, 0.7f),
            Color.hsv(144f, 1f, 0.7f),
            Color.hsv(216f, 1f, 0.7f),
            Color.hsv(288f, 1f, 0.7f)
        ) // 1 color per borough

    // Population data is in order of borough, then year
    val population = listOf(
        listOf(1168972, 1203789, 1332650, 1385108, 1446788), // Bronx
        listOf(2230936, 2300664, 2465326, 2552911, 2648452), // Brooklyn
        listOf(1428285, 1487536, 1537195, 1585873, 1638281), // Manhattan
        listOf(1891325, 1951598, 2229379, 2250002, 2330295), // Queens
        listOf(352121, 378977, 443728, 468730, 487155), // Staten Island
    )

    XYGraph(
        xAxisModel = remember { CategoryAxisModel(years) },
        yAxisModel = rememberFloatLinearAxisModel(0f..10f, minorTickCount = 0),
        yAxisTitle = "Population (Millions)"
    ) {
        StackedVerticalBarPlot {
            boroughs.forEachIndexed { index, _ ->
                series(solidBar(colors[index])) {
                    years.forEachIndexed { yearIndex, year ->
                        item(year, population[index][yearIndex].toFloat() / 1E6f)
                    }
                }
            }
        }
    }
/examples/src/jvmMain/kotlin/io/github/koalaplot/example/StackedVerticalBar1.kt

Stacked bars

This example uses a StackedVerticalBarPlot overload that initiates a builder api for specifying the data series and items within each series. This builder will automatically accumulate the values in a stack, and is currently implemented only for vertical axes that use Float values. An alternative is to use the other variant of StackedVerticalBarPlot, which is lower level but directly receives a List of the data regardless of types used, and requires the user to pre-accumulate the stack values.

Grouped Bars

Like stacked bars, grouped bars also plot multiple series of data with a common set of x-axis values. However in grouped bars, each series is plotted side by side in a group centered on the common x-axis value for the group. The below example demonstrates creating a grouped vertical bar plot with Koala Plot.

 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
    val boroughs = listOf("Bronx", "Brooklyn", "Manhattan", "Queens", "Staten Island")
    val years = listOf(1980, 1990, 2000, 2010, 2020)
    val colors =
        listOf(
            Color.hsv(0f, 1f, 0.7f),
            Color.hsv(72f, 1f, 0.7f),
            Color.hsv(144f, 1f, 0.7f),
            Color.hsv(216f, 1f, 0.7f),
            Color.hsv(288f, 1f, 0.7f)
        ) // 1 color per year

    // Population data is in order of borough, then year
    val population = listOf(
        listOf(1168972, 1203789, 1332650, 1385108, 1446788), // Bronx
        listOf(2230936, 2300664, 2465326, 2552911, 2648452), // Brooklyn
        listOf(1428285, 1487536, 1537195, 1585873, 1638281), // Manhattan
        listOf(1891325, 1951598, 2229379, 2250002, 2330295), // Queens
        listOf(352121, 378977, 443728, 468730, 487155), // Staten Island
    )

    XYGraph(
        xAxisModel = remember { CategoryAxisModel(boroughs) },
        yAxisModel = rememberFloatLinearAxisModel(0f..3f, minorTickCount = 0),
        yAxisTitle = "Population (Millions)"
    ) {
        GroupedVerticalBarPlot {
            years.indices.forEach {
                series(solidBar(colors[it])) {
                    boroughs.forEachIndexed { index, borough ->
                        item(borough, 0f, population[index][it].toFloat() / 1E6f)
                    }
                }
            }
        }
    }
/examples/src/jvmMain/kotlin/io/github/koalaplot/example/GroupedVerticalBar1.kt

Grouped bars

Most of this example is establishing the data and the colors to use for each data series. The data series are the years, and the x-axis data values are the boroughs that make up the category axis. The key call is GroupedVerticalBarPlot which initiates a builder API for the plot. Within that api, series is used to add a new data series, which for this data are the years, and item is used to add an item to the series at a specific x-axis value and with minimum and maximum y-axis values for the corresponding bar, in this case the population data.

In this example, a unique Composable to generate a bar is provided for each series. It is also possible to specify a bar Composable for the individual item as well, if it should be rendered differently than the other bars in the series.


Last modified November 25, 2023: Add bar plots section (51c53bb)