boxplot, violin, quiver, and line series recipes; plus some fixes
This commit is contained in:
parent
26779cbf4a
commit
8ec3d18339
@ -222,8 +222,8 @@ plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
|
||||
scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d)
|
||||
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)
|
||||
boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
|
||||
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
|
||||
boxplot(args...; kw...) = plot(args...; kw..., linetype = :boxplot)
|
||||
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :boxplot)
|
||||
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
|
||||
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
|
||||
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
|
||||
|
||||
@ -11,7 +11,7 @@ const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe, :contour3d]
|
||||
const _allTypes = vcat([
|
||||
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
|
||||
:heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, :ohlc,
|
||||
:contour, :pie, :shape, :box, :violin, :quiver, :image
|
||||
:contour, :pie, :shape, :image #, :boxplot, :violin, :quiver,
|
||||
], _3dTypes)
|
||||
@compat const _typeAliases = KW(
|
||||
:n => :none,
|
||||
@ -38,7 +38,7 @@ const _allTypes = vcat([
|
||||
:shapes => :shape,
|
||||
:poly => :shape,
|
||||
:polygon => :shape,
|
||||
:boxplot => :box,
|
||||
:box => :boxplot,
|
||||
:velocity => :quiver,
|
||||
:gradient => :quiver,
|
||||
:img => :image,
|
||||
|
||||
@ -28,7 +28,7 @@ supportedAxes(::GadflyBackend) = [:auto, :left]
|
||||
supportedTypes(::GadflyBackend) = [
|
||||
:none, :line, :path, :steppre, :steppost, :sticks,
|
||||
:scatter, :hist2d, :hexbin, :hist,
|
||||
:bar, :box, :violin, :quiver,
|
||||
:bar, #:box, :violin, :quiver,
|
||||
:hline, :vline, :contour, :shape
|
||||
]
|
||||
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
|
||||
@ -40,7 +40,7 @@ supportedAxes(::PyPlotBackend) = _allAxes
|
||||
supportedTypes(::PyPlotBackend) = [
|
||||
:none, :line, :path, :steppre, :steppost, :shape,
|
||||
:scatter, :hist2d, :hexbin, :hist, :density,
|
||||
:bar, :sticks, :box, :violin, :quiver,
|
||||
:bar, :sticks, #:box, :violin, :quiver,
|
||||
:hline, :vline, :heatmap, :pie, :image,
|
||||
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
]
|
||||
|
||||
22
src/plot.jl
22
src/plot.jl
@ -86,12 +86,15 @@ end
|
||||
# natively by the backend
|
||||
function _apply_series_recipe(plt::Plot, d::KW)
|
||||
lt = d[:linetype]
|
||||
# dumpdict(d, "apply_series_recipe", true)
|
||||
if lt in supportedTypes()
|
||||
# println("adding series!!")
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
_add_series(plt.backend, plt, d)
|
||||
else
|
||||
# get a sub list of series for this linetype
|
||||
try
|
||||
series_list = RecipesBase.apply_recipe(Val{lt}, d[:x], d[:y], d[:z])
|
||||
series_list = try
|
||||
RecipesBase.apply_recipe(d, Val{lt}, d[:x], d[:y], d[:z])
|
||||
catch
|
||||
warn("Exception during apply_recipe(Val{$lt}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
|
||||
rethrow()
|
||||
@ -142,12 +145,25 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
next_series = pop!(still_to_process)
|
||||
series_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
for series in series_list
|
||||
# @show series
|
||||
if isempty(series.args)
|
||||
# finish processing and add to the kw_list
|
||||
_add_markershape(series.d)
|
||||
_filter_input_data!(series.d)
|
||||
warnOnUnsupportedArgs(plt.backend, series.d)
|
||||
warnOnUnsupportedScales(plt.backend, series.d)
|
||||
|
||||
# TODO:
|
||||
# # map functions to vectors
|
||||
# if isa(d[:marker_z], Function)
|
||||
# d[:marker_z] = map(d[:marker_z], d[:x])
|
||||
# end
|
||||
# # handle ribbons
|
||||
# if get(d, :ribbon, nothing) != nothing
|
||||
# rib = d[:ribbon]
|
||||
# d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
# end
|
||||
|
||||
push!(kw_list, series.d)
|
||||
else
|
||||
push!(still_to_process, series)
|
||||
@ -229,7 +245,7 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
|
||||
# todo: while the linetype is not supported, try to apply a recipe of f(Val{lt}, x,y,z) recursively
|
||||
# the default should throw an error because we can't handle that linetype
|
||||
_apply_series_recipe(kw)
|
||||
_apply_series_recipe(plt, kw)
|
||||
|
||||
|
||||
# _add_series(plt.backend, plt, kw)
|
||||
|
||||
@ -204,28 +204,42 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
`apply_series_recipe` should take a processed series KW dict and break it up
|
||||
into component parts. For example, a box plot is made up of `shape` for the
|
||||
boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
# """
|
||||
# `apply_series_recipe` should take a processed series KW dict and break it up
|
||||
# into component parts. For example, a box plot is made up of `shape` for the
|
||||
# boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
#
|
||||
# Returns a Vector{KW}.
|
||||
# """
|
||||
# apply_series_recipe(d::KW, lt) = KW[d]
|
||||
|
||||
Returns a Vector{KW}.
|
||||
"""
|
||||
apply_series_recipe(d::KW, lt) = KW[d]
|
||||
|
||||
# for linetype `line`, need to sort by x values
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
# order by x
|
||||
indices = sortperm(d[:x])
|
||||
d[:x] = d[:x][indices]
|
||||
d[:y] = d[:y][indices]
|
||||
if typeof(d[:z]) <: AVec
|
||||
d[:z] = d[:z][indices]
|
||||
end
|
||||
d[:linetype] = :path
|
||||
()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Box Plot
|
||||
|
||||
const _box_halfwidth = 0.4
|
||||
|
||||
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)
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
groupby = extractGroupArgs(x)
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
@ -249,10 +263,16 @@ function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
KW[d]
|
||||
d[:linetype] = :shape
|
||||
xticklabels --> groupby.groupLabels
|
||||
|
||||
# we want to set the fields directly inside series recipes... args are ignored
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
() # expects a tuple returned
|
||||
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -260,17 +280,13 @@ end
|
||||
|
||||
# if the user has KernelDensity installed, use this for violin plots.
|
||||
# otherwise, just use a histogram
|
||||
try
|
||||
Pkg.installed("KernelDensity")
|
||||
import KernelDensity
|
||||
|
||||
# warn("using KD for violin")
|
||||
if is_installed("KernelDensity")
|
||||
@eval import KernelDensity
|
||||
@eval function violin_coords(y)
|
||||
kd = KernelDensity.kde(y, npoints = 30)
|
||||
kd.density, kd.x
|
||||
end
|
||||
catch
|
||||
# warn("using hist for violin")
|
||||
else
|
||||
@eval function violin_coords(y)
|
||||
edges, widths = hist(y, 20)
|
||||
centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
||||
@ -280,13 +296,13 @@ catch
|
||||
end
|
||||
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
@recipe function f(::Type{Val{:violin}}, x, y, z)
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
@ -304,10 +320,14 @@ function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
d[:linetype] = :shape
|
||||
xticklabels --> groupby.groupLabels
|
||||
|
||||
KW[d]
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
()
|
||||
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -353,18 +373,22 @@ end
|
||||
|
||||
# we will create a series of path segments, where each point represents one
|
||||
# side of an errorbar
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
d[:markershape] = :hline
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
|
||||
KW[d]
|
||||
# KW[d]
|
||||
()
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
d[:markershape] = :vline
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
|
||||
KW[d]
|
||||
# KW[d]
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
@ -407,7 +431,7 @@ function quiver_using_arrows(d::KW)
|
||||
end
|
||||
|
||||
d[:x], d[:y] = x, y
|
||||
KW[d]
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
@ -453,15 +477,17 @@ function quiver_using_hack(d::KW)
|
||||
end
|
||||
|
||||
d[:x], d[:y] = Plots.unzip(pts[2:end])
|
||||
KW[d]
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
@recipe function f(::Type{Val{:quiver}}, x, y, z)
|
||||
if :arrow in supportedArgs()
|
||||
quiver_using_arrows(d)
|
||||
else
|
||||
quiver_using_hack(d)
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -77,7 +77,7 @@ function _add_defaults!(d::KW, plt::Plot, commandIndex::Int)
|
||||
end
|
||||
d[:label] = label
|
||||
|
||||
warnOnUnsupported(pkg, d)
|
||||
# warnOnUnsupported(pkg, d)
|
||||
|
||||
d
|
||||
end
|
||||
@ -329,8 +329,8 @@ end
|
||||
# end
|
||||
|
||||
@recipe function f{X,Y}(x::AVec{X}, y::AVec{Y}, zf::Function)
|
||||
x = TX <: Number ? sort(x) : x
|
||||
y = TY <: Number ? sort(y) : y
|
||||
x = X <: Number ? sort(x) : x
|
||||
y = Y <: Number ? sort(y) : y
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user