This section explores how to visualize data variability, confidence intervals, and statistical trends. By leveraging Charton's Layered Chart system and Polars expressions, you can easily combine summary statistics (like bars or lines) with their associated error ranges.

Basic Bar with Error Bars

Error bars are essential for communicating the precision of your data. This example shows the most common use case: a bar chart where each bar is accompanied by a vertical error bar calculated from pre-defined standard deviation values.

Key Concept: We use transform_calculate to dynamically create value_min and value_max columns within the chart pipeline.

use charton::prelude::*;
use polars::prelude::*;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Create sample data with x and y values
    let df = df! [
        "type" => ["a", "b", "c", "d"],
        "value" => [4.9, 5.3, 5.5, 6.5],
        "value_std" => [0.3, 0.39, 0.34, 0.20]
    ]?;

    // Create error bar chart using transform_calculate to add min/max values
    let errorbar = Chart::build(&df)?
        // Use transform_calculate to create ymin and ymax columns based on fixed std values
        .transform_calculate(
            (col("value") - col("value_std")).alias("value_min"), // ymin = y - std
            (col("value") + col("value_std")).alias("value_max"), // ymax = y + std
        )?
        .mark_errorbar()?
        .encode((x("type"), y("value_min"), y2("value_max")))?;
    let bar = Chart::build(&df)?
        .mark_bar()?
        .encode((x("type"), y("value")))?;

    // Create a layered chart and add the errorbar chart as a layer
    errorbar
        .and(bar)
        .with_y_label("value")
        .save("docs/src/images/bar_with_errorbar.svg")?;

    Ok(())
}

Grouped Bar with ErrorBar

When multiple groups are present (mapped to color), Charton automatically applies "dodge" logic to ensure that both the bars and the error bars are aligned side-by-side for each category.

use charton::prelude::*;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let df = load_dataset("penguins")?;
    println!("{:?}", df);

    // 2. Build the Error Bar layer
    let errorbar = Chart::build(&df)?
        .mark_errorbar()?
        // Mapping 'Sex' to color triggers the dodge logic
        .encode((x("Species"), y("Body Mass (g)"), color("Sex")))?;

    // 3. Add a Bar layer to see the alignment
    let bar = Chart::build(&df)?.mark_bar()?.encode((
        x("Species"),
        y("Body Mass (g)").with_aggregate("mean"),
        color("Sex"),
    ))?;

    // 4. Create the multiple layered Chart
    errorbar
        .and(bar)
        .with_size(600, 400)
        .with_title("Grouped Error Bars with Mean & Std Dev")
        .save("docs/src/images/grouped_bar_with_errorbar_1.svg")?;

    Ok(())
}

As an alternative approach, we demonstrate how to create a grouped error bar chart by manually defining the error boundaries using transform_calculate. While the previous one use automatic statistical aggregations, this method shows that the data generated through Charton's internal transformation pipeline is fully compatible across different layers. By calculating value_min and value_max within the errorbar layer, we ensure that the resulting dataset structure remains consistent with the mark_bar layer.

use charton::prelude::*;
use polars::prelude::*;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Create sample data with x and y values
    let df = df! [
        "type" => ["a", "a", "a", "a", "b", "b", "b", "b", "c", "c", "c", "c"],
        "value" => [4.1, 5.3, 5.5, 6.5, 4.2, 5.1, 5.7, 5.5, 4.3, 5.5, 5.1, 6.8],
        "value_std" => [0.22, 0.26, 0.14, 0.23, 0.2, 0.23, 0.12, 0.25, 0.21, 0.20, 0.16, 0.25],
        "group" => ["E", "F", "G", "H", "E", "F", "G", "H", "E", "F", "G", "H"]
    ]?;

    // Create error bar chart using transform_calculate to add min/max values
    let errorbar = Chart::build(&df)?
        // Use transform_calculate to create ymin and ymax columns based on fixed std values
        .transform_calculate(
            (col("value") - col("value_std")).alias("value_min"), // ymin = y - std
            (col("value") + col("value_std")).alias("value_max"), // ymax = y + std
        )?
        .mark_errorbar()?
        .encode((x("type"), y("value_min"), y2("value_max"), color("group")))?;

    // Create a bar chart
    let bar = Chart::build(&df)?
        .mark_bar()?
        .encode((x("type"), y("value"), color("group")))?;

    // Create a layered chart
    errorbar
        .and(bar)
        .save("docs/src/images/grouped_bar_with_errorbar_2.svg")?;

    Ok(())
}