accept vectors of tuples; implement ohlc for gadfly with an example

This commit is contained in:
Thomas Breloff 2015-09-17 14:44:29 -04:00
parent 01cb036976
commit 51021c799d
7 changed files with 60 additions and 58 deletions

View File

@ -83,6 +83,9 @@ const examples = PlotExample[
PlotExample("",
"",
[:(subplot!(randn(100,3)))]),
PlotExample("Open/High/Low/Close",
"Pass in a vector of 4-tuples as your `y` argument. Adjust the tick width with arg `markersize`.",
[:(n=10; hgt=rand(n)+1; bot=randn(n); openpct=rand(n); closepct=rand(n);), :(y = [(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n];), :(ohlc(y; markersize=10))]),
]

View File

@ -32,6 +32,8 @@ export
hline!,
vline,
vline!,
ohlc,
ohlc!,
savepng,
@ -76,6 +78,8 @@ hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
# ---------------------------------------------------------

View File

@ -16,6 +16,7 @@ const TYPES = [:line,
:bar,
:hline,
:vline,
:ohlc,
]
const STYLES = [:solid, :dash, :dot, :dashdot, :dashdotdot]
const MARKERS = [:ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon]

View File

@ -9,7 +9,7 @@ gadfly!() = plotter!(:gadfly)
supportedArgs(::GadflyPackage) = setdiff(ARGS, [:heatmap_c, :fillto, :pos])
supportedAxes(::GadflyPackage) = setdiff(ALL_AXES, [:right])
supportedTypes(::GadflyPackage) = [:none, :line, :step, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
supportedTypes(::GadflyPackage) = [:none, :line, :step, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc]
supportedStyles(::GadflyPackage) = [:auto, :solid]
supportedMarkers(::GadflyPackage) = [:none, :auto, :rect, :ellipse, :diamond, :cross]
@ -45,79 +45,32 @@ function getLineGeoms(d::Dict)
lt = d[:linetype]
lt in (:heatmap,:hexbin) && return [Gadfly.Geom.hexbin(xbincount = d[:nbins], ybincount = d[:nbins])]
lt == :hist && return [Gadfly.Geom.histogram(bincount = d[:nbins])]
lt == :none && return [Gadfly.Geom.path]
# lt == :none && return [Gadfly.Geom.path]
lt == :line && return [Gadfly.Geom.path]
lt == :scatter && return [Gadfly.Geom.point]
lt == :bar && return [Gadfly.Geom.bar]
lt == :step && return [Gadfly.Geom.step]
# NOTE: we won't actually show this (we'll set width to 0 later), but we need a geom so that Gadfly doesn't complain
if lt in (:none, :ohlc)
return [Gadfly.Geom.path]
end
# lt == :sticks && return [Gadfly.Geom.bar]
error("linetype $lt not currently supported with Gadfly")
end
# function createGadflyAnnotation(d::Dict)
# if d[:marker] == :rect
# # get the width/height of the square (both are sz)
# sz = d[:markersize] * Gadfly.px
# halfsz = sz/2
# # remap x/y to the corner position of the squares
# xs = map(z -> Gadfly.Compose.Measure(;cx=z) - halfsz, float(d[:x]))
# ys = map(z -> Gadfly.Compose.Measure(;cy=z) + halfsz, float(d[:y]))
# # return an Annotation which will add those shapes to each point in the series
# return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.rectangle(xs,ys,[sz],[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
# else
# # make circles
# sz = 0.5 * d[:markersize] * Gadfly.px
# xs = collect(float(d[:x]))
# ys = collect(float(d[:y]))
# # return an Annotation which will add those shapes to each point in the series
# return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.circle(xs,ys,[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))
# end
# end
# serious hack (I think?) to draw my own shapes as annotations... will it work? who knows...
function getMarkerGeomsAndGuides(d::Dict)
marker = d[:marker]
if marker == :none
if marker == :none && d[:linetype] != :ohlc
return [],[]
end
return [], [createGadflyAnnotation(d)]
end
# # special handling for other marker shapes... gotta create Compose contexts and map them to series points using Guide.Annotation
# elseif marker == :rect
# # get the width/height of the square (both are sz)
# sz = d[:markersize] * Gadfly.px
# halfsz = sz/2
# # remap x/y to the corner position of the squares
# xs = map(z -> Gadfly.Compose.Measure(;cx=z) - halfsz, float(d[:x]))
# ys = map(z -> Gadfly.Compose.Measure(;cy=z) + halfsz, float(d[:y]))
# # return an Annotation which will add those shapes to each point in the series
# return [], [Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.rectangle(xs,ys,[sz],[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))]
# else
# # make circles
# sz = 0.5 * d[:markersize] * Gadfly.px
# xs = collect(float(d[:x]))
# ys = collect(float(d[:y]))
# # return an Annotation which will add those shapes to each point in the series
# return [], [Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), Gadfly.circle(xs,ys,[sz]), Gadfly.fill(d[:markercolor]), Gadfly.stroke(nothing)))]
# end
# otherwise just return a Geom.point
# [Gadfly.Geom.point], []
# end
function addGadflyFixedLines!(gplt, d::Dict)
@ -153,6 +106,7 @@ function addGadflySeries!(gplt, d::Dict)
elseif d[:linetype] in (:hline, :vline)
addGadflyFixedLines!(gplt, d)
return
end
gfargs = []
@ -161,7 +115,9 @@ function addGadflySeries!(gplt, d::Dict)
append!(gfargs, getLineGeoms(d))
# handle markers
# @show d[:y]
geoms, guides = getMarkerGeomsAndGuides(d)
# @show d[:y]
append!(gfargs, geoms)
append!(gplt.guides, guides)

View File

@ -10,7 +10,13 @@ function createGadflyAnnotation(d::Dict)
x, y = d[:x], d[:y]
marker = d[:marker]
if marker == :rect
if d[:linetype] == :ohlc
shape = ohlcshape(x, y, d[:markersize])
d[:y] = Float64[z[1] for z in y]
d[:linetype] = :none
return Gadfly.Guide.annotation(Gadfly.compose(Gadfly.context(), shape, Gadfly.fill(nothing), Gadfly.stroke(d[:color])))
elseif marker == :rect
shape = square(x, y, sz)
elseif marker == :diamond
@ -86,3 +92,24 @@ function cross(xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
return Gadfly.polygon(polys)
end
# Base.isfinite{T<:Real}(x::Tuple{T,T,T,T}) = isfinite(x[1]) && isfinite(x[2]) && isfinite(x[3]) && isfinite(x[4])
function ohlcshape{T}(xs::AVec, ys::AVec{Tuple{T,T,T,T}}, tickwidth::Real)
@assert length(xs) == length(ys)
n = length(xs)
u = tickwidth * Compose.px
polys = Vector{Vector{Tuple{Compose.Measure, Compose.Measure}}}(n)
for i in 1:n
x = Compose.x_measure(xs[i])
o,h,l,c = map(Compose.y_measure, ys[i])
polys[i] = Tuple{Compose.Measure, Compose.Measure}[
(x, o), (x - u, o), (x, o), # open tick
(x, l), (x, h), (x, c), # high/low bar
(x + u, c), (x, c) # close tick
]
end
return Gadfly.polygon(polys)
end

View File

@ -362,6 +362,17 @@ function createKWargsList(plt::PlottingObject, n::Integer; kw...)
ret
end
# vector of tuples... to be used for things like OHLC
createKWargsList{T<:Tuple}(plt::PlottingObject, y::AVec{T}; kw...) = createKWargsList(plt, 1:length(y), y; kw...)
function createKWargsList{S<:Real, T<:Tuple}(plt::PlottingObject, x::AVec{S}, y::AVec{T}; kw...)
d = getPlotKeywordArgs(plt.plotter, kw, 1, plt.n + 1)
d[:x] = x
d[:y] = y
[d]
end
# TODO: handle DataFrames (might have NAs!)
# -------------------------

View File

@ -89,7 +89,7 @@ function plotter()
if !(currentBackendSymbol in INITIALIZED_BACKENDS)
# initialize
println("[Plots.jl] Initializing package: ", CURRENT_BACKEND.sym)
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
if currentBackendSymbol == :qwt
try
@eval import Qwt