Add notch and outliers to boxplot
This commit is contained in:
parent
d64d3ef101
commit
71d2af07da
@ -429,30 +429,68 @@ end
|
|||||||
|
|
||||||
const _box_halfwidth = 0.4
|
const _box_halfwidth = 0.4
|
||||||
|
|
||||||
|
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||||
|
|
||||||
# function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
# function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||||
@recipe function f(::Type{Val{:boxplot}}, x, y, z)
|
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||||
# dumpdict(d, "box before", true)
|
# Plots.dumpdict(d, "box before", true)
|
||||||
# TODO: add scatter series with outliers
|
|
||||||
|
|
||||||
# create a list of shapes, where each shape is a single boxplot
|
# create a list of shapes, where each shape is a single boxplot
|
||||||
shapes = Shape[]
|
shapes = Shape[]
|
||||||
groupby = extractGroupArgs(x)
|
groupby = extractGroupArgs(x)
|
||||||
|
outliers_y = Float64[]
|
||||||
|
outliers_x = Float64[]
|
||||||
|
|
||||||
|
warning = false
|
||||||
|
|
||||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||||
|
|
||||||
# filter y values, then compute quantiles
|
# filter y values
|
||||||
q1,q2,q3,q4,q5 = quantile(d[:y][groupby.groupIds[i]], linspace(0,1,5))
|
values = d[:y][groupby.groupIds[i]]
|
||||||
|
# then compute quantiles
|
||||||
|
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||||
|
# notch
|
||||||
|
n = notch_width(q2, q4, length(values))
|
||||||
|
|
||||||
|
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||||
|
warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||||
|
warning = true # Show the warning only one time
|
||||||
|
end
|
||||||
|
|
||||||
# make the shape
|
# make the shape
|
||||||
center = i - 0.5
|
center = i - 0.5
|
||||||
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
||||||
xcoords = [
|
# internal nodes for notches
|
||||||
|
L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
|
||||||
|
# outliers
|
||||||
|
limit = range*(q4-q2)
|
||||||
|
for value in values
|
||||||
|
if (value < (q2 - limit)) || (value > (q4 + limit))
|
||||||
|
push!(outliers_y, value)
|
||||||
|
push!(outliers_x, center)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# change q1 and q5 to use the limit in order to show outliers
|
||||||
|
q1 = q2 - limit
|
||||||
|
q5 = q4 + limit
|
||||||
|
# Box
|
||||||
|
xcoords = notch::Bool ? [
|
||||||
|
m, l, r, m, m, NaN, # lower T
|
||||||
|
l, l, L, R, r, r, l, NaN, # lower box
|
||||||
|
l, l, L, R, r, r, l, NaN, # upper box
|
||||||
|
m, l, r, m, m, NaN, # upper T
|
||||||
|
] : [
|
||||||
m, l, r, m, m, NaN, # lower T
|
m, l, r, m, m, NaN, # lower T
|
||||||
l, l, r, r, l, NaN, # lower box
|
l, l, r, r, l, NaN, # lower box
|
||||||
l, l, r, r, l, NaN, # upper box
|
l, l, r, r, l, NaN, # upper box
|
||||||
m, l, r, m, m, NaN, # upper T
|
m, l, r, m, m, NaN, # upper T
|
||||||
]
|
]
|
||||||
ycoords = [
|
ycoords = notch::Bool ? [
|
||||||
|
q1, q1, q1, q1, q2, NaN, # lower T
|
||||||
|
q2, q3-n, q3, q3, q3-n, q2, q2, NaN, # lower box
|
||||||
|
q4, q3+n, q3, q3, q3+n, q4, q4, NaN, # upper box
|
||||||
|
q5, q5, q5, q5, q4, NaN, # upper T
|
||||||
|
] : [
|
||||||
q1, q1, q1, q1, q2, NaN, # lower T
|
q1, q1, q1, q1, q2, NaN, # lower T
|
||||||
q2, q3, q3, q2, q2, NaN, # lower box
|
q2, q3, q3, q2, q2, NaN, # lower box
|
||||||
q4, q3, q3, q4, q4, NaN, # upper box
|
q4, q3, q3, q4, q4, NaN, # upper box
|
||||||
@ -467,8 +505,24 @@ const _box_halfwidth = 0.4
|
|||||||
n = length(groupby.groupLabels)
|
n = length(groupby.groupLabels)
|
||||||
xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||||
|
|
||||||
|
# clean d
|
||||||
|
pop!(d, :notch)
|
||||||
|
pop!(d, :range)
|
||||||
|
|
||||||
# we want to set the fields directly inside series recipes... args are ignored
|
# we want to set the fields directly inside series recipes... args are ignored
|
||||||
d[:x], d[:y] = shape_coords(shapes)
|
d[:x], d[:y] = Plots.shape_coords(shapes)
|
||||||
|
|
||||||
|
# Outliers
|
||||||
|
@series begin
|
||||||
|
seriestype := :scatter
|
||||||
|
markershape := :ellipse
|
||||||
|
x := outliers_x
|
||||||
|
y := outliers_y
|
||||||
|
label := ""
|
||||||
|
primary := false
|
||||||
|
()
|
||||||
|
end
|
||||||
|
|
||||||
() # expects a tuple returned
|
() # expects a tuple returned
|
||||||
|
|
||||||
# KW[d]
|
# KW[d]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user