Skip to content

Commit

Permalink
Allow width to be set per box in boxplot (#4447)
Browse files Browse the repository at this point in the history
* allow width per category in BoxPlot

* fix boxplot when range is zero

* update CHANGELOG

* add test for variable width

---------

Co-authored-by: Simon <sdanisch@protonmail.com>
Co-authored-by: Frederic Freyer <frederic481994@hotmail.de>
  • Loading branch information
3 people authored Oct 21, 2024
1 parent 6b26779 commit 49b29b6
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [Unreleased]

- Allow `width` to be set per box in `boxplot` [#4447](https://github.com/MakieOrg/Makie.jl/pull/4447).
- For `Textbox`es in which a fixed width is specified, the text is now scrolled
if the width is exceeded [#4293](https://github.com/MakieOrg/Makie.jl/pull/4293)
- Changed image, heatmap and surface picking indices to correctly index the relevant matrix arguments. [#4459](https://github.com/MakieOrg/Makie.jl/pull/4459)
Expand Down
13 changes: 6 additions & 7 deletions ReferenceTests/src/tests/examples2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1613,8 +1613,8 @@ end
boxplot(fig[1, 1], categories, values)

dodge = RNG.rand(1:2, 900)
boxplot(fig[1, 2], categories, values, dodge = dodge, show_notch = true,
color = map(d->d==1 ? :blue : :red, dodge),
boxplot(fig[1, 2], categories, values, dodge = dodge, show_notch = true,
color = map(d->d==1 ? :blue : :red, dodge),
outliercolor = RNG.rand([:red, :green, :blue, :black, :orange], 900)
)

Expand All @@ -1631,16 +1631,15 @@ end

weights = 1.0 ./ (1.0 .+ abs.(values))
boxplot!(ax_vert, categories, values, orientation=:vertical, weights = weights,
gap = 0.5,
show_notch = true, notchwidth = 0.75,
gap = 0.5,
show_notch = true, notchwidth = 0.75,
markersize = 5, strokewidth = 2.0, strokecolor = :black,
medianlinewidth = 5, mediancolor = :orange,
whiskerwidth = 1.0, whiskerlinewidth = 3, whiskercolor = :green,
outlierstrokewidth = 1.0, outlierstrokecolor = :red,
width = 1.5,

width = 1.5,
)
boxplot!(ax_horiz, categories, values; orientation=:horizontal)
boxplot!(ax_horiz, categories, values; orientation=:horizontal, width = categories ./ 3)

fig
end
Expand Down
20 changes: 11 additions & 9 deletions src/stats/boxplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,10 @@ function Makie.plot!(plot::BoxPlot)
plot[:color],
args...,
) do x, y, color, weights, width, range, show_outliers, whiskerwidth, show_notch, orientation, gap, dodge, n_dodge, dodge_gap
x̂, boxwidth = compute_x_and_width(x, width, gap, dodge, n_dodge, dodge_gap)
x̂, widths = compute_x_and_width(x, width, gap, dodge, n_dodge, dodge_gap)
if !(whiskerwidth === :match || whiskerwidth >= 0)
error("whiskerwidth must be :match or a positive number. Found: $whiskerwidth")
end
ww = whiskerwidth === :match ? boxwidth : whiskerwidth * boxwidth
outlier_points = Point2f[]
centers = Float32[]
medians = Float32[]
Expand All @@ -99,8 +98,10 @@ function Makie.plot!(plot::BoxPlot)
notchmax = Float32[]
t_segments = Point2f[]
outlier_indices = Int[]
T = color isa AbstractVector ? eltype(color) : typeof(color)
boxcolor = T[]
CT = color isa AbstractVector ? eltype(color) : typeof(color)
boxcolor = CT[]
WT = widths isa AbstractVector ? eltype(widths) : typeof(widths)
boxwidth = WT[]
for (i, (center, idxs)) in enumerate(StructArrays.finduniquesorted(x̂))
values = view(y, idxs)

Expand All @@ -117,7 +118,7 @@ function Makie.plot!(plot::BoxPlot)
end

# outliers
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
if !iszero(range) # if the range is 0, the whiskers will extend to the data
limit = range * (q4 - q2)
inside = Float64[]
for (value, idx) in zip(values,idxs)
Expand All @@ -134,18 +135,19 @@ function Makie.plot!(plot::BoxPlot)
# change q1 and q5 to show outliers
# using maximum and minimum values inside the limits
q1, q5 = extrema_nan(inside)
# register boxcolor
push!(boxcolor, getuniquevalue(color, idxs))
end

# whiskers
HW = 0.5 * _cycle(ww, i) # Whisker width
lw, rw = center - HW, center + HW
bw = getuniquevalue(widths, idxs) # Box width
ww = whiskerwidth === :match ? bw : whiskerwidth * bw # Whisker width
lw, rw = center - ww/2, center + ww/2
push!(t_segments, (center, q2), (center, q1), (lw, q1), (rw, q1)) # lower T
push!(t_segments, (center, q4), (center, q5), (rw, q5), (lw, q5)) # upper T

# box
push!(boxcolor, getuniquevalue(color, idxs))
push!(centers, center)
push!(boxwidth, bw)
push!(boxmin, q2)
push!(medians, q3)
push!(boxmax, q4)
Expand Down

0 comments on commit 49b29b6

Please sign in to comment.