ohlc recipe and related fixes; removed ohlc-specific code from gadfly and gr

This commit is contained in:
Thomas Breloff 2016-05-20 10:06:15 -04:00
parent 89feb68b7c
commit 333c2765fe
9 changed files with 107 additions and 41 deletions

View File

@ -10,7 +10,7 @@ const _allAxes = [:auto, :left, :right]
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,
:heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, #:ohlc,
:contour, :pie, :shape, :image #, :boxplot, :violin, :quiver,
], _3dTypes)
@compat const _typeAliases = KW(

View File

@ -298,9 +298,9 @@ function addGadflySeries!(plt::Plot, d::KW)
# special handling for ohlc and scatter
st = d[:seriestype]
if st == :ohlc
error("Haven't re-implemented after refactoring")
elseif st in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
# if st == :ohlc
# error("Haven't re-implemented after refactoring")
if st in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
elseif st == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse

View File

@ -40,7 +40,7 @@ supportedTypes(::GRBackend) = [
:none, :line, :path, :steppre, :steppost,
:scatter, :hist2d, :hexbin, :hist, :density,
:bar, :sticks,
:hline, :vline, :heatmap, :pie, :image, :ohlc,
:hline, :vline, :heatmap, :pie, :image, #:ohlc,
:contour, :path3d, :scatter3d, :surface, :wireframe
]
supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@ -261,8 +261,8 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
end
if st == :bar
x, y = 1:length(p[:y]), p[:y]
elseif st == :ohlc
x, y = 1:size(p[:y], 1), p[:y]
# elseif st == :ohlc
# x, y = 1:size(p[:y], 1), p[:y]
elseif st in [:hist, :density]
x, y = Base.hist(p[:y], p[:bins])
elseif st in [:hist2d, :hexbin]
@ -295,15 +295,15 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
if !(st in [:pie, :polar])
xmin = min(minimum(x), xmin)
xmax = max(maximum(x), xmax)
if st == :ohlc
for val in y
ymin = min(val.open, val.high, val.low, val.close, ymin)
ymax = max(val.open, val.high, val.low, val.close, ymax)
end
else
# if st == :ohlc
# for val in y
# ymin = min(val.open, val.high, val.low, val.close, ymin)
# ymax = max(val.open, val.high, val.low, val.close, ymax)
# end
# else
ymin = min(minimum(y), ymin)
ymax = max(maximum(y), ymax)
end
# end
if p[:xerror] != nothing || p[:yerror] != nothing
dx = xmax - xmin
xmin -= 0.02 * dx
@ -460,7 +460,7 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
GR.savestate()
xmin, xmax, ymin, ymax = extrema[gr_getaxisind(p),:]
GR.setwindow(xmin, xmax, ymin, ymax)
if st in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc, :polar]
if st in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :polar] # :ohlc, :polar]
GR.setlinetype(gr_linetype[p[:linestyle]])
GR.setlinewidth(p[:linewidth])
GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
@ -675,15 +675,15 @@ function gr_display(plt::Plot{GRBackend}, clear=true, update=true,
GR.setcharheight(charheight)
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
elseif st == :ohlc
y = p[:y]
n = size(y, 1)
ticksize = 0.5 * (xmax - xmin) / n
for i in 1:n
GR.polyline([i-ticksize, i], [y[i].open, y[i].open])
GR.polyline([i, i], [y[i].low, y[i].high])
GR.polyline([i, i+ticksize], [y[i].close, y[i].close])
end
# elseif st == :ohlc
# y = p[:y]
# n = size(y, 1)
# ticksize = 0.5 * (xmax - xmin) / n
# for i in 1:n
# GR.polyline([i-ticksize, i], [y[i].open, y[i].open])
# GR.polyline([i, i], [y[i].low, y[i].high])
# GR.polyline([i, i+ticksize], [y[i].close, y[i].close])
# end
elseif st == :pie
GR.selntran(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)

View File

@ -374,15 +374,6 @@ end
# -----------------------------------------------------------------------
type OHLC{T<:Real}
open::T
high::T
low::T
close::T
end
# -----------------------------------------------------------------------
# style is :open or :closed (for now)
immutable Arrow
style::Symbol

View File

@ -197,6 +197,12 @@ 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
# series should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
if !(typeof(series) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $series")
end
# @show series
if isempty(series.args)
# when the arg tuple is empty, that means there's nothing left to recursively

View File

@ -20,7 +20,7 @@ num_series(x) = 1
# _apply_recipe(d::KW, kw::KW) = ()
# if it's not a recipe, just do nothing and return the args
function RecipesBase.apply_recipe(d::KW, kw::KW, args...; issubplot=false)
function RecipesBase.apply_recipe(d::KW, args...; issubplot=false)
if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout)
# put in a sensible default
d[:n] = maximum(map(num_series, args))
@ -540,7 +540,65 @@ function getRecipeArgs(ep::EllipseRecipe)
[(:line, (3, [:dot :solid], [:red :blue], :path))]
end
# # -------------------------------------------------
# -------------------------------------------------
# TODO: this should really be in another package...
type OHLC{T<:Real}
open::T
high::T
low::T
close::T
end
tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
# get one OHLC path
function get_xy(o::OHLC, x, xdiff)
xl, xm, xr = x-xdiff, x, x+xdiff
ox = [xl, xm, NaN,
xm, xm, NaN,
xm, xr]
oy = [o.open, o.open, NaN,
o.low, o.high, NaN,
o.close, o.close]
ox, oy
end
# get the joined vector
function get_xy(v::AVec{OHLC}, x = 1:length(v))
xdiff = 0.3mean(abs(diff(x)))
x_out, y_out = zeros(0), zeros(0)
for (i,ohlc) in enumerate(v)
ox,oy = get_xy(ohlc, x[i], xdiff)
nanappend!(x_out, ox)
nanappend!(y_out, oy)
end
x_out, y_out
end
# these are for passing in a vector of OHLC objects
# TODO: when I allow `@recipe f(::Type{T}, v::T) = ...` definitions to replace convertToAnyVector,
# then I should replace these with one definition to convert to a vector of 4-tuples
# to squash ambiguity warnings...
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) = error()
# this must be OHLC?
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) = x, OHLC[OHLC(t...) for t in ohlc]
@recipe function f(x::AVec, v::AVec{OHLC})
d[:seriestype] = :path
get_xy(v, x)
end
@recipe function f(v::AVec{OHLC})
d[:seriestype] = :path
get_xy(v)
end
# the series recipe, when passed vectors of 4-tuples
# -------------------------------------------------
"Sparsity plot... heatmap of non-zero values of a matrix"

View File

@ -44,8 +44,8 @@ convertToAnyVector(f::Function, d::KW) = Any[f], nothing
# surface
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# # vector of OHLC
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing

View File

@ -450,6 +450,11 @@ end
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz)
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]]
# these might be points+velocity, or OHLC or something else
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
#
# # 2D FixedSizeArrays
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})

View File

@ -156,13 +156,19 @@ maketuple{T,S}(x::@compat(Tuple{T,S})) = x
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
unzip{T,S}(xy::AVec{Tuple{T,S}}) = [x[1] for x in xy], [y[2] for y in xy]
unzip{T,S,R}(xyz::AVec{Tuple{T,S,R}}) = [x[1] for x in xyz], [y[2] for y in xyz], [z[3] for z in xyz]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[x[1] for x in xy], T[y[2] for y in xy]
unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[x[1] for x in xyz], T[y[2] for y in xyz], T[z[3] for z in xyz]
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
function _expand_limits(lims, x)
try