Skip to content

Support for downsampling #722

@viktaur

Description

@viktaur

Provide a ChartContext method to downsample the representation of the source data to a custom bucket size (not sure if something like this exists already, but could not find it).

Sometimes we are dealing with data sampled very frequently over long periods of time, which can make our chart look too cluttered.

Image

To fix this in one of my projects, I wrote the following function for DateTime data (x axis).

fn downsample(
    data: &[(DateTime<Utc>, f64)],
    bucket_size: Duration,
) -> Vec<(DateTime<Utc>, f64)> {
    if data.is_empty() {
        return vec![];
    }

    let mut buckets: Vec<(DateTime<Utc>, f64)> = Vec::new();
    let mut bucket_start = data[0].0;
    let mut sum = 0.0;
    let mut count = 0u32;

    for &(dt, val) in data {
        if dt >= bucket_start + bucket_size {
            if count > 0 {
                let mid = bucket_start + bucket_size / 2;
                buckets.push((mid, sum / count as f64));
            }
            bucket_start = dt;
            sum = 0.0;
            count = 0;
        }
        sum += val;
        count += 1;
    }

    // Last bucket
    if count > 0 {
        let mid = bucket_start + bucket_size / 2;
        buckets.push((mid, sum / count as f64));
    }

    buckets
}

Then you just pass the new bucket size, how often you want a sample to be displayed.

let data = downsample(&data, Duration::hours(12));

chart
    .draw_series(LineSeries::new(data, color.stroke_width(2)))?
    .label(position.to_string())
    .legend(move |(x, y)| {
        PathElement::new(vec![(x, y), (x + 20, y)], color.stroke_width(2))
    });

Final result:

Image

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions