forked from GiovineItalia/Gadfly.jl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
boxplot.jl
125 lines (101 loc) · 4.19 KB
/
boxplot.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
immutable BoxplotGeometry <: Gadfly.GeometryElement
end
const boxplot = BoxplotGeometry
element_aesthetics(::BoxplotGeometry) = [:x, :y, :color,
:middle,
:upper_fence, :lower_fence,
:upper_hinge, :lower_hinge]
default_statistic(::BoxplotGeometry) = Gadfly.Stat.boxplot()
function render(geom::BoxplotGeometry, theme::Gadfly.Theme,
aes::Gadfly.Aesthetics, data::Gadfly.Data,
scales::Dict{Symbol, ScaleElement})
Gadfly.assert_aesthetics_defined("Geom.bar", aes,
:lower_fence, :lower_hinge,
:upper_hinge, :upper_fence,)
Gadfly.assert_aesthetics_equal_length("Geom.bar", aes,
:lower_fence, :lower_hinge, :middle,
:upper_hinge, :upper_fence, :outliers)
default_aes = Gadfly.Aesthetics()
default_aes.color = PooledDataArray(RGB{Float32}[theme.default_color])
default_aes.x = Float64[0.5]
aes = inherit(aes, default_aes)
n = length(aes.lower_hinge)
bw = 1w / n - theme.boxplot_spacing # boxplot width
fw = 2/3 * bw # fence width
xs = [Measure(cx=x) for x in takestrict(cycle(aes.x), n)]
cs = takestrict(cycle(aes.color), n)
# We allow lower_hinge > upper_hinge, and lower_fence > upper_fence. So we
# need to organize them for drawing here
lower_hinge = similar(aes.lower_hinge)
upper_hinge = similar(aes.upper_hinge)
lower_fence = similar(aes.lower_fence)
upper_fence = similar(aes.upper_fence)
for (i, (lh, uh, lf, uf)) in enumerate(zip(aes.lower_hinge, aes.upper_hinge,
aes.lower_fence, aes.upper_fence))
if uh > lh
lower_hinge[i] = aes.lower_hinge[i]
upper_hinge[i] = aes.upper_hinge[i]
else
lower_hinge[i] = aes.upper_hinge[i]
upper_hinge[i] = aes.lower_hinge[i]
end
if uf > lf
lower_fence[i] = aes.lower_fence[i]
upper_fence[i] = aes.upper_fence[i]
else
lower_fence[i] = aes.upper_fence[i]
upper_fence[i] = aes.lower_fence[i]
end
end
ctx = compose!(
context(),
fill(collect(cs)),
stroke(collect(cs)),
linewidth(theme.line_width),
{
context(),
# Box
rectangle(
[x - bw/2 for x in xs],
lower_hinge, [bw],
[uh - lh for (lh, uh) in zip(lower_hinge, upper_hinge)]),
{
context(),
# Whiskers
Compose.line([[(x, lh), (x, lf)]
for (x, lh, lf) in zip(xs, lower_hinge, lower_fence)]),
Compose.line([[(x, uh), (x, uf)]
for (x, uh, uf) in zip(xs, upper_hinge, upper_fence)]),
# Fences
Compose.line([[(x - fw/2, lf), (x + fw/2, lf)]
for (x, lf) in zip(xs, lower_fence)]),
Compose.line([[(x - fw/2, uf), (x + fw/2, uf)]
for (x, uf) in zip(xs, upper_fence)]),
stroke(collect(cs))
},
},
svgclass("geometry"))
# Outliers
if aes.outliers != nothing && !isempty(aes.outliers)
xys = collect(chain([zip(cycle([x]), ys, cycle([c]))
for (x, ys, c) in zip(xs, aes.outliers, cs)]...))
compose!(ctx,
(context(),
circle([x for (x, y, c) in xys],
[y for (x, y, c) in xys],
[theme.default_point_size]),
stroke([theme.discrete_highlight_color(c) for (x, y, c) in xys]),
fill([c for (x, y, c) in xys])))
end
# Middle
if aes.middle != nothing
compose!(ctx, {
context(order=1),
Compose.line([[(x - fw/2, mid), (x + fw/2, mid)]
for (x, mid) in zip(xs, aes.middle)]),
linewidth(theme.middle_width),
stroke([theme.middle_color(c) for c in cs])
})
end
return ctx
end