Skip to content

Conversation

@lenianiva
Copy link

@lenianiva lenianiva commented Oct 18, 2025

Description

This PR adds a multi-histogram plotting function multihist! like seaborn's stacked histogram. Below is an example of stacked vs dodged histograms.

Vide https://discourse.julialang.org/t/stacked-histogram/133248

comparison

using CairoMakie

data1 = rand(100) .* 2.0 .- 1.0
data2 = rand(100) .* 2.0

fig = Figure(size=(600,300))

multihist!(
    Axis(fig[1, 1]),
    [data1, data2];
    colormap = :Set3_10,
    label = ["red", "blue"],
    positioning = :stack,
)
multihist!(
    Axis(fig[1, 2]),
    [data1, data2];
    colormap = :Set3_10,
    label = ["red", "blue"],
    positioning = :dodge,
)
save("/tmp/stacked.svg", fig)

Type of change

Delete options that do not apply:

  • New feature (non-breaking change which adds functionality)

Checklist

  • Added an entry in CHANGELOG.md (for new features and breaking changes)
  • Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
  • Added reference image tests for new plotting functions, recipes, visual options, etc.

@github-project-automation github-project-automation bot moved this to Work in progress in PR review Oct 18, 2025
@lenianiva
Copy link
Author

I also want to add an optional dodge positioning for the histogram, but this would require conditionally setting either :stack or :dodge based on another option :positioning, and map! does not seem to be able to do this.

    map!(plot, :points, [:flatbins, :stack]) do points
        flatbins = collect(Iterators.flatten(points))
        stack = [i for (i, bin) in enumerate(points) for _ in 1:length(bin)]
        return (flatbins, stack)
    end

@lenianiva lenianiva changed the title feat: Stacked histogram plot feat: Multi-histogram plot Oct 18, 2025
@lenianiva
Copy link
Author

The weights argument should be a vector of vectors as well, but this doesn't work:

    map!(plot, [:values, :edges, :normalization, :scale_to, :weights], :points) do values, edges, normalization, scale_to, weights
        points = []
        for (i, dataset) in enumerate(values)
            if weights === automatic
                wgts = automatic
            else
                wgts = weights[i]
            end
            centers, weights = _hist_center_weights(dataset, edges, normalization, scale_to, wgts)
            push!(points, Point2.(centers, weights))
        end
        return points
    end

Giving the error

Triggered by update of:
  arg1, scale_to, weights, normalization or bins
Due to ERROR: MethodError: no method matching weights(::Float64)
The function `weights` exists, but no method is defined for this combination of argument types.

@ffreyer
Copy link
Collaborator

ffreyer commented Oct 27, 2025

I'd say this should be a change to hist instead, and follow the syntax we already have for barplot, violin, boxplot and crossbar

@lenianiva
Copy link
Author

I'd say this should be a change to hist instead, and follow the syntax we already have for barplot, violin, boxplot and crossbar

so with an extra categories variable?

@jkrumbiegel
Copy link
Member

so with an extra categories variable?

No, barplot for example has a dodge and a stack attribute and these take Vector{Int}s that assign groups for each.

@ffreyer
Copy link
Collaborator

ffreyer commented Nov 20, 2025

I can also refactor this if you're not already working on it

@ffreyer
Copy link
Collaborator

ffreyer commented Nov 25, 2025

I think this should be done now.

You can now do either:

# established syntax:
hist(rand(200), stack = vcat(fill(1, 100), fill(2, 100))

# vector of vectors syntax:
hist([rand(100), rand(100)], stack = [1, 2])

where stack can be replaced with dodge. weights work if they match the syntax used for the argument. color can be :stack or :dodge to use those indices as color values, a per-group color vector (one color per stacked/dodged histogram), the existing :values (uses heights as color values) or the existing per-bin color vector. All of these work with both input styles.

@lenianiva
Copy link
Author

I think this should be done now.

You can now do either:

# established syntax:
hist(rand(200), stack = vcat(fill(1, 100), fill(2, 100))

# vector of vectors syntax:
hist([rand(100), rand(100)], stack = [1, 2])

where stack can be replaced with dodge. weights work if they match the syntax used for the argument. color can be :stack or :dodge to use those indices as color values, a per-group color vector (one color per stacked/dodged histogram), the existing :values (uses heights as color values) or the existing per-bin color vector. All of these work with both input styles.

Thanks! Sorry I didn't have time to work on this.

@ffreyer ffreyer moved this from Work in progress to Ready to review in PR review Nov 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready to review

Development

Successfully merging this pull request may close these issues.

3 participants