switched Colors dep for PlotUtils dep; removed DataFrames, boxplot, violin, density and added StatPlots to tests

This commit is contained in:
Thomas Breloff 2016-07-12 10:45:58 -04:00
parent 8d5b748b09
commit 4a2e88a81c
5 changed files with 188 additions and 185 deletions

View File

@ -1,7 +1,7 @@
julia 0.4 julia 0.4
RecipesBase RecipesBase
Colors PlotUtils
Reexport Reexport
Compat Compat
FixedSizeArrays FixedSizeArrays

View File

@ -101,47 +101,47 @@ num_series(x) = 1
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T")) RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
# TODO: remove when StatPlots is ready # # TODO: remove when StatPlots is ready
if is_installed("DataFrames") # if is_installed("DataFrames")
@eval begin # @eval begin
import DataFrames # import DataFrames
# if it's one symbol, set the guide and return the column # # if it's one symbol, set the guide and return the column
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol) # function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol)
get!(d, Symbol(letter * "guide"), string(sym)) # get!(d, Symbol(letter * "guide"), string(sym))
collect(df[sym]) # collect(df[sym])
end # end
# if it's an array of symbols, set the labels and return a Vector{Any} of columns # # if it's an array of symbols, set the labels and return a Vector{Any} of columns
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol}) # function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol})
get!(d, :label, reshape(syms, 1, length(syms))) # get!(d, :label, reshape(syms, 1, length(syms)))
Any[collect(df[s]) for s in syms] # Any[collect(df[s]) for s in syms]
end # end
# for anything else, no-op # # for anything else, no-op
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything) # function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything)
anything # anything
end # end
# handle grouping by DataFrame column # # handle grouping by DataFrame column
function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...) # function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
extractGroupArgs(collect(df[group])) # extractGroupArgs(collect(df[group]))
end # end
# if a DataFrame is the first arg, lets swap symbols out for columns # # if a DataFrame is the first arg, lets swap symbols out for columns
@recipe function f(df::DataFrames.AbstractDataFrame, args...) # @recipe function f(df::DataFrames.AbstractDataFrame, args...)
# if any of these attributes are symbols, swap out for the df column # # if any of these attributes are symbols, swap out for the df column
for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror) # for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)
if haskey(d, k) && isa(d[k], Symbol) # if haskey(d, k) && isa(d[k], Symbol)
d[k] = collect(df[d[k]]) # d[k] = collect(df[d[k]])
end # end
end # end
# return a list of new arguments # # return a list of new arguments
tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...) # tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...)
end # end
end # end
end # end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -524,179 +524,179 @@ end
# note: don't add dependencies because this really isn't a drop-in replacement # note: don't add dependencies because this really isn't a drop-in replacement
# TODO: move boxplots and violin plots to StatPlots when it's ready # # TODO: move boxplots and violin plots to StatPlots when it's ready
# --------------------------------------------------------------------------- # # ---------------------------------------------------------------------------
# Box Plot # # Box Plot
const _box_halfwidth = 0.4 # const _box_halfwidth = 0.4
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N) # notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5) # @recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
xsegs, ysegs = Segments(), Segments() # xsegs, ysegs = Segments(), Segments()
glabels = sort(collect(unique(x))) # glabels = sort(collect(unique(x)))
warning = false # warning = false
outliers_x, outliers_y = zeros(0), zeros(0) # outliers_x, outliers_y = zeros(0), zeros(0)
for (i,glabel) in enumerate(glabels) # for (i,glabel) in enumerate(glabels)
# filter y # # filter y
values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))] # values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
# compute quantiles # # compute quantiles
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5)) # q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
# notch # # notch
n = notch_width(q2, q4, length(values)) # n = notch_width(q2, q4, length(values))
# warn on inverted notches? # # warn on inverted notches?
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) ) # if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
warn("Boxplot's notch went outside hinges. Set notch to false.") # warn("Boxplot's notch went outside hinges. Set notch to false.")
warning = true # Show the warning only one time # warning = true # Show the warning only one time
end # end
# make the shape # # make the shape
center = discrete_value!(d[:subplot][:xaxis], glabel)[1] # center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
hw = d[:bar_width] == nothing ? _box_halfwidth : 0.5cycle(d[:bar_width], i) # hw = d[:bar_width] == nothing ? _box_halfwidth : 0.5cycle(d[:bar_width], i)
l, m, r = center - hw, center, center + hw # l, m, r = center - hw, center, center + hw
# internal nodes for notches # # internal nodes for notches
L, R = center - 0.5 * hw, center + 0.5 * hw # L, R = center - 0.5 * hw, center + 0.5 * hw
# outliers # # outliers
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data # if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
limit = range*(q4-q2) # limit = range*(q4-q2)
inside = Float64[] # inside = Float64[]
for value in values # for value in values
if (value < (q2 - limit)) || (value > (q4 + limit)) # if (value < (q2 - limit)) || (value > (q4 + limit))
push!(outliers_y, value) # push!(outliers_y, value)
push!(outliers_x, center) # push!(outliers_x, center)
else # else
push!(inside, value) # push!(inside, value)
end # end
end # end
# change q1 and q5 to show outliers # # change q1 and q5 to show outliers
# using maximum and minimum values inside the limits # # using maximum and minimum values inside the limits
q1, q5 = extrema(inside) # q1, q5 = extrema(inside)
end # end
# Box # # Box
if notch # if notch
push!(xsegs, m, l, r, m, m) # lower T # push!(xsegs, m, l, r, m, m) # lower T
push!(xsegs, l, l, L, R, r, r, l) # lower box # push!(xsegs, l, l, L, R, r, r, l) # lower box
push!(xsegs, l, l, L, R, r, r, l) # upper box # push!(xsegs, l, l, L, R, r, r, l) # upper box
push!(xsegs, m, l, r, m, m) # upper T # push!(xsegs, m, l, r, m, m) # upper T
push!(ysegs, q1, q1, q1, q1, q2) # lower T # push!(ysegs, q1, q1, q1, q1, q2) # lower T
push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box # push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box # push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
push!(ysegs, q5, q5, q5, q5, q4) # upper T # push!(ysegs, q5, q5, q5, q5, q4) # upper T
else # else
push!(xsegs, m, l, r, m, m) # lower T # push!(xsegs, m, l, r, m, m) # lower T
push!(xsegs, l, l, r, r, l) # lower box # push!(xsegs, l, l, r, r, l) # lower box
push!(xsegs, l, l, r, r, l) # upper box # push!(xsegs, l, l, r, r, l) # upper box
push!(xsegs, m, l, r, m, m) # upper T # push!(xsegs, m, l, r, m, m) # upper T
push!(ysegs, q1, q1, q1, q1, q2) # lower T # push!(ysegs, q1, q1, q1, q1, q2) # lower T
push!(ysegs, q2, q3, q3, q2, q2) # lower box # push!(ysegs, q2, q3, q3, q2, q2) # lower box
push!(ysegs, q4, q3, q3, q4, q4) # upper box # push!(ysegs, q4, q3, q3, q4, q4) # upper box
push!(ysegs, q5, q5, q5, q5, q4) # upper T # push!(ysegs, q5, q5, q5, q5, q4) # upper T
end # end
end # end
# Outliers # # Outliers
@series begin # @series begin
seriestype := :scatter # seriestype := :scatter
markershape := :circle # markershape := :circle
markercolor := d[:fillcolor] # markercolor := d[:fillcolor]
markeralpha := d[:fillalpha] # markeralpha := d[:fillalpha]
markerstrokecolor := d[:linecolor] # markerstrokecolor := d[:linecolor]
markerstrokealpha := d[:linealpha] # markerstrokealpha := d[:linealpha]
x := outliers_x # x := outliers_x
y := outliers_y # y := outliers_y
primary := false # primary := false
() # ()
end # end
seriestype := :shape # seriestype := :shape
x := xsegs.pts # x := xsegs.pts
y := ysegs.pts # y := ysegs.pts
() # ()
end # end
@deps boxplot shape scatter # @deps boxplot shape scatter
# --------------------------------------------------------------------------- # # ---------------------------------------------------------------------------
# Violin Plot # # Violin Plot
const _violin_warned = [false] # const _violin_warned = [false]
# if the user has KernelDensity installed, use this for violin plots. # # if the user has KernelDensity installed, use this for violin plots.
# otherwise, just use a histogram # # otherwise, just use a histogram
if is_installed("KernelDensity") # if is_installed("KernelDensity")
@eval import KernelDensity # @eval import KernelDensity
@eval function violin_coords(y; trim::Bool=false) # @eval function violin_coords(y; trim::Bool=false)
kd = KernelDensity.kde(y, npoints = 200) # kd = KernelDensity.kde(y, npoints = 200)
if trim # if trim
xmin, xmax = extrema(y) # xmin, xmax = extrema(y)
inside = Bool[ xmin <= x <= xmax for x in kd.x] # inside = Bool[ xmin <= x <= xmax for x in kd.x]
return(kd.density[inside], kd.x[inside]) # return(kd.density[inside], kd.x[inside])
end # end
kd.density, kd.x # kd.density, kd.x
end # end
else # else
@eval function violin_coords(y; trim::Bool=false) # @eval function violin_coords(y; trim::Bool=false)
if !_violin_warned[1] # if !_violin_warned[1]
warn("Install the KernelDensity package for best results.") # warn("Install the KernelDensity package for best results.")
_violin_warned[1] = true # _violin_warned[1] = true
end # end
edges, widths = my_hist(y, 10) # edges, widths = my_hist(y, 10)
centers = 0.5 * (edges[1:end-1] + edges[2:end]) # centers = 0.5 * (edges[1:end-1] + edges[2:end])
ymin, ymax = extrema(y) # ymin, ymax = extrema(y)
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax) # vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
end # end
end # end
@recipe function f(::Type{Val{:violin}}, x, y, z; trim=true) # @recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
xsegs, ysegs = Segments(), Segments() # xsegs, ysegs = Segments(), Segments()
glabels = sort(collect(unique(x))) # glabels = sort(collect(unique(x)))
for glabel in glabels # for glabel in glabels
widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim) # widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
isempty(widths) && continue # isempty(widths) && continue
# normalize # # normalize
widths = _box_halfwidth * widths / maximum(widths) # widths = _box_halfwidth * widths / maximum(widths)
# make the violin # # make the violin
xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1] # xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
xcoords = vcat(widths, -reverse(widths)) + xcenter # xcoords = vcat(widths, -reverse(widths)) + xcenter
ycoords = vcat(centers, reverse(centers)) # ycoords = vcat(centers, reverse(centers))
push!(xsegs, xcoords) # push!(xsegs, xcoords)
push!(ysegs, ycoords) # push!(ysegs, ycoords)
end # end
seriestype := :shape # seriestype := :shape
x := xsegs.pts # x := xsegs.pts
y := ysegs.pts # y := ysegs.pts
() # ()
end # end
@deps violin shape # @deps violin shape
# --------------------------------------------------------------------------- # # ---------------------------------------------------------------------------
# density # # density
@recipe function f(::Type{Val{:density}}, x, y, z; trim=false) # @recipe function f(::Type{Val{:density}}, x, y, z; trim=false)
newx, newy = violin_coords(y, trim=trim) # newx, newy = violin_coords(y, trim=trim)
if isvertical(d) # if isvertical(d)
newx, newy = newy, newx # newx, newy = newy, newx
end # end
x := newx # x := newx
y := newy # y := newy
seriestype := :path # seriestype := :path
() # ()
end # end
@deps density path # @deps density path
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# contourf - filled contours # contourf - filled contours

View File

@ -1,7 +1,8 @@
julia 0.4 julia 0.4
RecipesBase RecipesBase
Colors PlotUtils
StatPlots
Reexport Reexport
Measures Measures
FactCheck FactCheck

View File

@ -13,7 +13,9 @@ try
end end
using Plots, FactCheck using Plots
using StatPlots
using FactCheck
using Glob using Glob
default(size=(500,300)) default(size=(500,300))

View File

@ -30,7 +30,7 @@ facts("GR") do
@fact gr() --> Plots.GRBackend() @fact gr() --> Plots.GRBackend()
@fact backend() --> Plots.GRBackend() @fact backend() --> Plots.GRBackend()
@linux_only image_comparison_facts(:gr, skip=[], eps=img_eps) # @linux_only image_comparison_facts(:gr, skip=[], eps=img_eps)
end end
facts("Plotly") do facts("Plotly") do