Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ v0.11.0 (2014 Apr XX)

* Added stem() function and Stems component.

* Added quartileboxes() function and QuartileBoxes component

14 changes: 14 additions & 0 deletions doc/fun/quartileboxes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
quartileboxes
=======

.. function:: quartileboxes(x [;notch=false])

Visualize the distribution ``x``. If ``x`` is a matrix, visualize each column. The median of ``x`` is represented by a horizontal line inside a box extending from the 25th to the 75th percentile of ``x``. Vertical lines, called whiskers, span 1.5 times the inter-quartile range. Data outside of this range are represented as dots. If the keyword argument ``notch`` is set to ``true``, a notch is drawn around around the median to indicate the confidence of the median.

Examples
=======

.. winston::

X = rand((200,10))
quartileboxes(X ;notch=true)
153 changes: 152 additions & 1 deletion src/Winston.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export
plot,
plothist,
plothist2d,
quartileboxes,
savefig,
scatter,
semilogx,
Expand All @@ -37,7 +38,8 @@ export
FramedPlot,
Plot,
Table,


QuartileBoxes,
Curve,
FillAbove,
FillBelow,
Expand Down Expand Up @@ -2033,6 +2035,155 @@ function make(self::Histogram, context::PlotContext)
GroupPainter(getattr(self,:style), PathPainter(u, v))
end

type QuartileBoxes <: PlotComponent
attr::PlotAttributes
median::Float64
quartiles::(Float64,Float64)
iqr::Float64
outliers::AbstractVector
notch::Bool
width::Float64
n::Int64
position::Float64


function QuartileBoxes(median, quartiles, outliers, n,args...; kvs...)
self = new(Dict())
iniattr(self)
kw_init(self, args...; kvs...)
self.median = median
self.quartiles = quartiles
self.iqr = quartiles[2] - quartiles[1]
self.outliers = filter(x-> (x<quartiles[1]-1.5*self.iqr)|(x>quartiles[2]+1.5*self.iqr),outliers)
self.notch = get(args2dict(kvs...), :notch, false)
self.width=1.0
self.position = 1.0
self.n = n
self
end
end

function QuartileBoxes(X::Vector;kvs...)
#compute median and quartiles directly
Xs = sort(X)
m = median(X)
l = quantile(X,0.25)
h = quantile(X,0.75)
iqr = h-l
QuartileBoxes(m,(l,h),X[(X.>h+1.5*iqr)|(X.<l-1.5*iqr)],length(X);kvs...)
end

function limits(self::QuartileBoxes, window::BoundingBox)
if !isempty(self.outliers)
ymin = min(self.quartiles[1]-1.5*self.iqr,minimum(self.outliers))
ymax = max(self.quartiles[2]+1.5*self.iqr,maximum(self.outliers))
else
ymin = self.quartiles[1]-1.5*self.iqr
ymax = self.quartiles[2]+1.5*self.iqr
end
xl = self.position - 1.0*self.width
xr = self.position + 1.0*self.width
bounds_within([xl,xl,xr,xr],[ymin,ymax, ymin, ymax], window)
end

function make(self::QuartileBoxes, context::PlotContext)
objs = GroupPainter(getattr(self,:style))
xl = self.position - 0.5*self.width
xr = self.position + 0.5*self.width
xm = self.position

#draw notch
if self.notch
xln = self.position - 0.25*self.width
xrn = self.position + 0.25*self.width
ymin = self.median-1.58*self.iqr/sqrt(self.n)
ymax = self.median+1.58*self.iqr/sqrt(self.n)
#top notch
p = project(context.geom, xln, self.median)
q = project(context.geom, xl, ymax)
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
p = project(context.geom, xrn, self.median)
q = project(context.geom, xr, ymax)
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
#bottom notch
p = project(context.geom, xln, self.median)
q = project(context.geom, xl, ymin)
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
p = project(context.geom, xrn, self.median)
q = project(context.geom, xr, ymin)
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
#median
p = project(context.geom, xrn, self.median)
q = project(context.geom, xln, self.median)
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))

#change the starting points for the quartile boxes
qtop = ymax
qbottom = ymin
else
#median
p = project(context.geom, xl, self.median)
q = project(context.geom, xr, self.median)
m = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,m)
qtop = self.median
qbottom = self.median
end

#quartiles
#horizontal lines
p = project(context.geom, xl, self.quartiles[1])
q = project(context.geom, xr, self.quartiles[1])
l1 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l1)
p = project(context.geom, xl, self.quartiles[2])
q = project(context.geom, xr, self.quartiles[2])
l2 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l2)

#vertical lines
p = project(context.geom, xl, qtop)
q = project(context.geom, xl, self.quartiles[2])
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
p = project(context.geom, xl, qbottom)
q = project(context.geom, xl, self.quartiles[1])
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))

p = project(context.geom, xr, qtop)
q = project(context.geom, xr, self.quartiles[2])
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))
p = project(context.geom, xr, qbottom)
q = project(context.geom, xr, self.quartiles[1])
push!(objs,LinePainter(Point(p[1],p[2]), Point(q[1],q[2])))

#draw the whiskers
xlm = xm - 0.25*self.width
xrm = xm + 0.25*self.width
p = project(context.geom, xm, self.quartiles[1])
q = project(context.geom, xm, self.quartiles[1]-1.5*self.iqr)
l5 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l5)
p = project(context.geom, xlm, self.quartiles[1]-1.5*self.iqr)
q = project(context.geom, xrm, self.quartiles[1]-1.5*self.iqr)
l6 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l6)

p = project(context.geom, xm, self.quartiles[2])
q = project(context.geom, xm, self.quartiles[2]+1.5*self.iqr)
l7 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l7)
p = project(context.geom, xlm, self.quartiles[2]+1.5*self.iqr)
q = project(context.geom, xrm, self.quartiles[2]+1.5*self.iqr)
l8 = LinePainter(Point(p[1],p[2]), Point(q[1],q[2]))
push!(objs,l8)
#outliers
for o in self.outliers
p = project(context.geom, xm, o)
push!(objs, SymbolPainter(Point(p[1],p[2])))
end
objs
end

type LineX <: LineComponent
attr::PlotAttributes
x::Float64
Expand Down
41 changes: 41 additions & 0 deletions src/plot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,47 @@ end
plothist(p::FramedPlot, args...; kvs...) = plothist(p::FramedPlot, hist(args...); kvs...)
plothist(args...; kvs...) = plothist(ghf(), args...; kvs...)

quartileboxes(args...; kvs...) = quartileboxes(ghf(), args...; kvs...)

function quartileboxes(p::FramedPlot, h::Matrix;kvs...)
for i=1:size(h,2)
b = QuartileBoxes(h[:,i];kvs...)
b.position = 1.1*b.width*i
quartileboxes(p,b;kvs...)
end
p
end

function quartileboxes(p::FramedPlot, h::Vector;kvs...)
b = QuartileBoxes(h;kvs...)
quartileboxes(p,b;kvs...)
end

function quartileboxes(p::FramedPlot, h::(Float64,(Float64,Float64),Vector);kvs...)
b = QuartileBoxes(h...;kvs...)
quartileboxes(p,b;kvs...)
end

function quartileboxes(p::FramedPlot, b::QuartileBoxes;kvs...)
#messy...
dd = args2dict(kvs...)
if :position in keys(dd)
setattr(b,:position, dd[:position])
b.position = dd[:position]
end
add(p,b)
for (k,v) in kvs
if k in [:color,:linecolor,:linekind,:linetype,:linewidth]
style(b, k, v)
else
setattr(p, k, v)
end
end

global _pwinston = p
p
end

# 3x3 gaussian
#_default_kernel2d=[.05 .1 .05; .1 .4 .1; .05 .1 .05]

Expand Down