diff --git a/src/Plots.jl b/src/Plots.jl index 838d328e..d2fefc3a 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -141,7 +141,7 @@ include("plot.jl") include("series.jl") include("layouts.jl") include("subplots.jl") -include("recipes.jl") +# include("recipes.jl") include("animation.jl") include("output.jl") include("examples.jl") diff --git a/src/args.jl b/src/args.jl index c72fe01f..943ef8b3 100644 --- a/src/args.jl +++ b/src/args.jl @@ -1,6 +1,6 @@ -const _keyAliases = KW() +const _keyAliases = Dict{Symbol,Symbol}() function add_aliases(sym::Symbol, aliases::Symbol...) for alias in aliases @@ -11,7 +11,7 @@ function add_aliases(sym::Symbol, aliases::Symbol...) end end -function add_non_underscore_aliases!(aliases::KW) +function add_non_underscore_aliases!(aliases::Dict{Symbol,Symbol}) for (k,v) in aliases s = string(k) if '_' in s @@ -24,7 +24,7 @@ end # ------------------------------------------------------------ const _allAxes = [:auto, :left, :right] -const _axesAliases = KW( +const _axesAliases = Dict{Symbol,Symbol}( :a => :auto, :l => :left, :r => :right @@ -39,7 +39,7 @@ const _allTypes = vcat([ :contour, :pie, :shape, :image ], _3dTypes) -@compat const _typeAliases = KW( +@compat const _typeAliases = Dict{Symbol,Symbol}( :n => :none, :no => :none, :l => :line, @@ -83,7 +83,7 @@ like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contou is3d(seriestype::Symbol) = seriestype in _3dTypes is3d(series::Series) = is3d(series.d) -is3d(d::KW) = trueOrAllTrue(is3d, d[:seriestype]) +is3d(d::KW) = trueOrAllTrue(is3d, Symbol(d[:seriestype])) is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d" ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar" @@ -92,7 +92,7 @@ ispolar(series::Series) = ispolar(series.d[:subplot]) # ------------------------------------------------------------ const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] -@compat const _styleAliases = KW( +@compat const _styleAliases = Dict{Symbol,Symbol}( :a => :auto, :s => :solid, :d => :dash, @@ -101,7 +101,7 @@ const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] ) const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes)))) -@compat const _markerAliases = KW( +@compat const _markerAliases = Dict{Symbol,Symbol}( :n => :none, :no => :none, :a => :auto, @@ -143,7 +143,7 @@ const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes)) ) const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] -@compat const _scaleAliases = KW( +@compat const _scaleAliases = Dict{Symbol,Symbol}( :none => :identity, :log => :log10, ) @@ -330,7 +330,7 @@ autopick(notarr, idx::Integer) = notarr autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx) autopick_ignore_none_auto(notarr, idx::Integer) = notarr -function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int) +function aliasesAndAutopick(d::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int) if d[sym] == :auto d[sym] = autopick_ignore_none_auto(options, plotIndex) elseif haskey(aliases, d[sym]) @@ -338,7 +338,7 @@ function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plot end end -function aliases(aliasMap::KW, val) +function aliases(aliasMap::Dict{Symbol,Symbol}, val) sortedkeys(filter((k,v)-> v==val, aliasMap)) end @@ -687,22 +687,7 @@ function preprocessArgs!(d::KW) if haskey(d, :colorbar) d[:colorbar] = convertLegendValue(d[:colorbar]) end - - # # handle subplot links - # if haskey(d, :link) - # l = d[:link] - # if isa(l, Bool) - # d[:linkx] = l - # d[:linky] = l - # elseif isa(l, Function) - # d[:linkx] = true - # d[:linky] = true - # d[:linkfunc] = l - # else - # warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)") - # end - # delete!(d, :link) - # end + return end # ----------------------------------------------------------------------------- diff --git a/src/plot.jl b/src/plot.jl index 99381d9a..03e94be7 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -43,13 +43,14 @@ When you pass in matrices, it splits by columns. See the documentation for more # this creates a new plot with args/kw and sets it to be the current plot function plot(args...; kw...) + info("started to plot") d = KW(kw) preprocessArgs!(d) # create an empty Plot then process plt = Plot() # plt.user_attr = d - _plot!(plt, d, args...) + _plot!(plt, d, args) end # build a new plot from existing plots @@ -105,22 +106,11 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...) end end - # # just in case the backend needs to set up the plot (make it current or something) - # _prepare_plot_object(plt) - # first apply any args for the subplots for (idx,sp) in enumerate(plt.subplots) _update_subplot_args(plt, sp, d, idx, remove_pair = false) end - # # now we can get rid of the axis keys without a letter - # for k in keys(_axis_defaults) - # delete!(d, k) - # for letter in (:x,:y,:z) - # delete!(d, Symbol(letter,k)) - # end - # end - # do we need to link any axes together? link_axes!(plt.layout, plt[:link]) @@ -150,7 +140,7 @@ function plot!(plt::Plot, args...; kw...) d = KW(kw) preprocessArgs!(d) # merge!(plt.user_attr, d) - _plot!(plt, d, args...) + _plot!(plt, d, args) end function strip_first_letter(s::Symbol) @@ -158,104 +148,120 @@ function strip_first_letter(s::Symbol) str[1:1], Symbol(str[2:end]) end +# ------------------------------------------------------------------------------- + +# getting ready to add the series... last update to subplot from anything +# that might have been added during series recipes +function _prepare_subplot(plt::Plot, d::KW) + st = d[:seriestype] + sp = d[:subplot] + sp_idx = get_subplot_index(plt, sp) + _update_subplot_args(plt, sp, d, sp_idx) + + # do we want to override the series type? + if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z])) + st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d) + end + + # change to a 3d projection for this subplot? + if is3d(st) + sp.attr[:projection] = "3d" + end + + # initialize now that we know the first series type + if !haskey(sp.attr, :init) + _initialize_subplot(plt, sp) + sp.attr[:init] = true + end + sp::Subplot +end + +function _prepare_annotations(sp::Subplot, d::KW) + # strip out series annotations (those which are based on series x/y coords) + # and add them to the subplot attr + sp_anns = annotations(sp[:annotations]) + anns = annotations(pop!(d, :series_annotations, [])) + if length(anns) > 0 + x, y = d[:x], d[:y] + nx, ny, na = map(length, (x,y,anns)) + n = max(nx, ny, na) + anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n] + end + sp.attr[:annotations] = vcat(sp_anns, anns) +end + +function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol) + # adjust extrema and discrete info + if st == :image + w, h = size(d[:z]) + expand_extrema!(sp[:xaxis], (0,w)) + expand_extrema!(sp[:yaxis], (0,h)) + sp[:yaxis].d[:flip] = true + elseif !(st in (:pie, :histogram, :histogram2d)) + expand_extrema!(sp, d) + end +end + +function _add_the_series(plt, d) + warnOnUnsupported_args(plt.backend, d) + warnOnUnsupported(plt.backend, d) + series = Series(d) + push!(plt.series_list, series) + _series_added(plt, series) +end + +# ------------------------------------------------------------------------------- # this method recursively applies series recipes when the seriestype is not supported # natively by the backend -function _apply_series_recipe(plt::Plot, d::KW) +function _process_seriesrecipe(plt::Plot, d::KW) # replace seriestype aliases - st = d[:seriestype] + st = Symbol(d[:seriestype]) st = d[:seriestype] = get(_typeAliases, st, st) # if it's natively supported, finalize processing and pass along to the backend, otherwise recurse if st in supported_types() - - # getting ready to add the series... last update to subplot from anything - # that might have been added during series recipes - sp = d[:subplot] - sp_idx = get_subplot_index(plt, sp) - _update_subplot_args(plt, sp, d, sp_idx) - - # do we want to override the series type? - if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z])) - st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d) - end - - # change to a 3d projection for this subplot? - if is3d(st) - sp.attr[:projection] = "3d" - end - - # initialize now that we know the first series type - if !haskey(sp.attr, :init) - _initialize_subplot(plt, sp) - sp.attr[:init] = true - end - - # strip out series annotations (those which are based on series x/y coords) - # and add them to the subplot attr - sp_anns = annotations(sp[:annotations]) - anns = annotations(pop!(d, :series_annotations, [])) - if length(anns) > 0 - x, y = d[:x], d[:y] - nx, ny, na = map(length, (x,y,anns)) - n = max(nx, ny, na) - anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n] - end - sp.attr[:annotations] = vcat(sp_anns, anns) - - # adjust extrema and discrete info - if st == :image - w, h = size(d[:z]) - expand_extrema!(sp[:xaxis], (0,w)) - expand_extrema!(sp[:yaxis], (0,h)) - sp[:yaxis].d[:flip] = true - elseif !(st in (:pie, :histogram, :histogram2d)) - expand_extrema!(sp, d) - end - - - # add the series! - warnOnUnsupported_args(plt.backend, d) - warnOnUnsupported(plt.backend, d) - series = Series(d) - push!(plt.series_list, series) - # @show series - - _series_added(plt, series) + sp = _prepare_subplot(plt, d) + _prepare_annotations(sp, d) + _expand_subplot_extrema(sp, d, st) + _add_the_series(plt, d) else # get a sub list of series for this seriestype datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z]) - # datalist = try - # RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z]) - # catch - # warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))") - # rethrow() - # end # assuming there was no error, recursively apply the series recipes for data in datalist if isa(data, RecipeData) - _apply_series_recipe(plt, data.d) + _process_seriesrecipe(plt, data.d) else warn("Unhandled recipe: $(data)") break end end end + nothing end function command_idx(kw_list::AVec{KW}, kw::KW) kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1 end +function _expand_seriestype_array(d::KW, args) + sts = get(d, :seriestype, :path) + if typeof(sts) <: AbstractArray + delete!(d, :seriestype) + RecipeData[begin + dc = copy(d) + dc[:seriestype] = sts[r,:] + RecipeData(dc, args) + end for r=1:size(sts,1)] + else + RecipeData[RecipeData(copy(d), args)] + end +end -# this is the core plotting function. recursively apply recipes to build -# a list of series KW dicts. -# note: at entry, we only have those preprocessed args which were passed in... no default values yet -function _plot!(plt::Plot, d::KW, args...) - d[:plot_object] = plt - +function _preprocess_args(d::KW, args, still_to_process::Vector{RecipeData}) # the grouping mechanism is a recipe on a GroupBy object # we simply add the GroupBy object to the front of the args list to allow # the recipe to be applied @@ -265,21 +271,8 @@ function _plot!(plt::Plot, d::KW, args...) # if we were passed a vector/matrix of seriestypes and there's more than one row, # we want to duplicate the inputs, once for each seriestype row. - kw_list = KW[] - still_to_process = if isempty(args) - [] - else - sts = get(d, :seriestype, :path) - if typeof(sts) <: AbstractArray - delete!(d, :seriestype) - [begin - dc = copy(d) - dc[:seriestype] = sts[r,:] - RecipeData(dc, args) - end for r=1:size(sts,1)] - else - [RecipeData(copy(d), args)] - end + if !isempty(args) + append!(still_to_process, _expand_seriestype_array(d, args)) end # remove subplot and axis args from d... they will be passed through in the kw_list @@ -295,9 +288,70 @@ function _plot!(plt::Plot, d::KW, args...) end end - # -------------------------------- - # "USER RECIPES" - # -------------------------------- + args +end + + +function _preprocess_userrecipe(kw::KW) + _add_markershape(kw) + + # if there was a grouping, filter the data here + _filter_input_data!(kw) + + # map marker_z if it's a Function + if isa(get(kw, :marker_z, nothing), Function) + # TODO: should this take y and/or z as arguments? + kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) + end + + # map line_z if it's a Function + if isa(get(kw, :line_z, nothing), Function) + kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z]) + end + + # convert a ribbon into a fillrange + if get(kw, :ribbon, nothing) != nothing + make_fillrange_from_ribbon(kw) + end + return +end + +function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW) + # handle error bars by creating new recipedata data... these will have + # the same recipedata index as the recipedata they are copied from + for esym in (:xerror, :yerror) + if get(kw, esym, nothing) != nothing + # we make a copy of the KW and apply an errorbar recipe + errkw = copy(kw) + errkw[:seriestype] = esym + errkw[:label] = "" + errkw[:primary] = false + push!(kw_list, errkw) + end + end +end + +function _add_smooth_kw(kw_list::Vector{KW}, kw::KW) + # handle smoothing by adding a new series + if get(kw, :smooth, false) + x, y = kw[:x], kw[:y] + β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y) + sx = [minimum(x), maximum(x)] + sy = β * sx + α + push!(kw_list, merge(copy(kw), KW( + :seriestype => :path, + :x => sx, + :y => sy, + :fillrange => nothing, + :label => "", + :primary => false, + ))) + end +end + +function _process_userrecipes(plt::Plot, d::KW, args) + still_to_process = RecipeData[] + args = _preprocess_args(d, args, still_to_process) # for plotting recipes, swap out the args and update the parameter dictionary # we are keeping a queue of series that still need to be processed. @@ -305,80 +359,20 @@ function _plot!(plt::Plot, d::KW, args...) # the recipe will return a list a Series objects... the ones that are # finished (no more args) get added to the kw_list, and the rest go into the queue # for processing. + kw_list = KW[] while !isempty(still_to_process) - # grab the first in line to be processed and pass it through apply_recipe # to generate a list of RecipeData objects (data + attributes) next_series = shift!(still_to_process) - for recipedata in RecipesBase.apply_recipe(next_series.d, next_series.args...) - + rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...) + for recipedata in rd_list # recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes if !(typeof(recipedata) <: RecipeData) error("Inputs couldn't be processed... expected RecipeData but got: $recipedata") end if isempty(recipedata.args) - # when the arg tuple is empty, that means there's nothing left to recursively - # process... finish up and add to the kw_list - kw = recipedata.d - _add_markershape(kw) - - # if there was a grouping, filter the data here - _filter_input_data!(kw) - - # map marker_z if it's a Function - if isa(get(kw, :marker_z, nothing), Function) - # TODO: should this take y and/or z as arguments? - kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) - end - - # map line_z if it's a Function - if isa(get(kw, :line_z, nothing), Function) - kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z]) - end - - # convert a ribbon into a fillrange - if get(kw, :ribbon, nothing) != nothing - make_fillrange_from_ribbon(kw) - end - - # add the plot index - plt.n += 1 - kw[:series_plotindex] = plt.n - - # check that the backend will support the command and add it to the list - warnOnUnsupported_scales(plt.backend, kw) - push!(kw_list, kw) - - # handle error bars by creating new recipedata data... these will have - # the same recipedata index as the recipedata they are copied from - for esym in (:xerror, :yerror) - if get(d, esym, nothing) != nothing - # we make a copy of the KW and apply an errorbar recipe - errkw = copy(kw) - errkw[:seriestype] = esym - errkw[:label] = "" - errkw[:primary] = false - push!(kw_list, errkw) - end - end - - # handle smoothing by adding a new series - if get(d, :smooth, false) - x, y = kw[:x], kw[:y] - β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y) - sx = [minimum(x), maximum(x)] - sy = β * sx + α - push!(kw_list, merge(copy(kw), KW( - :seriestype => :path, - :x => sx, - :y => sy, - :fillrange => nothing, - :label => "", - :primary => false, - ))) - end - + _process_userrecipe(plt, kw_list, recipedata) else # args are non-empty, so there's still processing to do... add it back to the queue push!(still_to_process, recipedata) @@ -388,51 +382,57 @@ function _plot!(plt::Plot, d::KW, args...) # don't allow something else to handle it d[:smooth] = false + kw_list +end - # -------------------------------- - # "PLOT RECIPES" - # -------------------------------- +function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData) + # when the arg tuple is empty, that means there's nothing left to recursively + # process... finish up and add to the kw_list + kw = recipedata.d + _preprocess_userrecipe(kw) + warnOnUnsupported_scales(plt.backend, kw) - # "plot recipe", which acts like a series type, and is processed before - # the plot layout is created, which allows for setting layouts and other plot-wide attributes. - # we get inputs which have been fully processed by "user recipes" and "type recipes", - # so we can expect standard vectors, surfaces, etc. No defaults have been set yet. - still_to_process = kw_list - kw_list = KW[] - while !isempty(still_to_process) - # Grab the first in line to be processed and pass it through apply_recipe - # to generate a list of RecipeData objects (data + attributes). - # If we applied a "plot recipe" without error, then add the returned datalist's KWs, - # otherwise we just add the original KW. - next_kw = shift!(still_to_process) - if !isa(get(next_kw, :seriestype, nothing), Symbol) - # seriestype was never set, or it's not a Symbol, so it can't be a plot recipe - push!(kw_list, next_kw) - continue + # add the plot index + plt.n += 1 + kw[:series_plotindex] = plt.n + + push!(kw_list, kw) + _add_errorbar_kw(kw_list, kw) + _add_smooth_kw(kw_list, kw) + return +end + +# Grab the first in line to be processed and pass it through apply_recipe +# to generate a list of RecipeData objects (data + attributes). +# If we applied a "plot recipe" without error, then add the returned datalist's KWs, +# otherwise we just add the original KW. +function _process_plotrecipe(kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW}) + if !isa(get(kw, :seriestype, nothing), Symbol) + # seriestype was never set, or it's not a Symbol, so it can't be a plot recipe + push!(kw_list, kw) + return + end + try + st = kw[:seriestype] + st = kw[:seriestype] = get(_typeAliases, st, st) + datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) + for data in datalist + if data.d[:seriestype] == st + error("Plot recipe $st returned the same seriestype: $(data.d)") + end + push!(still_to_process, data.d) end - try - st = next_kw[:seriestype] - st = next_kw[:seriestype] = get(_typeAliases, st, st) - datalist = RecipesBase.apply_recipe(next_kw, Val{st}, plt) - for data in datalist - if data.d[:seriestype] == st - error("Plot recipe $st returned the same seriestype: $(data.d)") - end - push!(still_to_process, data.d) - end - catch err - if isa(err, MethodError) - push!(kw_list, next_kw) - else - rethrow() - end + catch err + if isa(err, MethodError) + push!(kw_list, kw) + else + rethrow() end end + return +end - # -------------------------------- - # Plot/Subplot/Layout setup - # -------------------------------- - +function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW}) # merge in anything meant for the Plot for kw in kw_list, (k,v) in kw haskey(_plot_defaults, k) && (d[k] = pop!(kw, k)) @@ -478,7 +478,9 @@ function _plot!(plt::Plot, d::KW, args...) push!(plt.inset_subplots, sp) end end +end +function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW}) # we'll keep a map of subplot to an attribute override dict. # Subplot/Axis attributes set by a user/series recipe apply only to the # Subplot object which they belong to. @@ -514,6 +516,41 @@ function _plot!(plt::Plot, d::KW, args...) # do we need to link any axes together? link_axes!(plt.layout, plt[:link]) +end + +# this is the core plotting function. recursively apply recipes to build +# a list of series KW dicts. +# note: at entry, we only have those preprocessed args which were passed in... no default values yet +function _plot!(plt::Plot, d::KW, args::Tuple) + # d[:plot_object] = plt + + # -------------------------------- + # "USER RECIPES" + # -------------------------------- + + kw_list = _process_userrecipes(plt, d, args) + + + # -------------------------------- + # "PLOT RECIPES" + # -------------------------------- + + # "plot recipe", which acts like a series type, and is processed before + # the plot layout is created, which allows for setting layouts and other plot-wide attributes. + # we get inputs which have been fully processed by "user recipes" and "type recipes", + # so we can expect standard vectors, surfaces, etc. No defaults have been set yet. + still_to_process = kw_list + kw_list = KW[] + while !isempty(still_to_process) + next_kw = shift!(still_to_process) + _process_plotrecipe(next_kw, kw_list, still_to_process) + end + + # -------------------------------- + # Plot/Subplot/Layout setup + # -------------------------------- + _plot_setup(plt, d, kw_list) + _subplot_setup(plt, d, kw_list) # !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!! # !!! The next step is to recursively apply series recipes until the backend supports that series type !!! @@ -538,7 +575,7 @@ function _plot!(plt::Plot, d::KW, args...) # For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot, # and a step plot is really just a path. So any backend that supports drawing a path will implicitly # be able to support step, bar, and histogram plots (and any recipes that use those components). - _apply_series_recipe(plt, kw) + _process_seriesrecipe(plt, kw) end # -------------------------------- diff --git a/src/recipes.jl b/src/recipes.jl index 729f1e2f..01220490 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -106,47 +106,47 @@ num_series(x) = 1 RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T")) +# # TODO: remove when StatPlots is ready +# if is_installed("DataFrames") +# @eval begin +# import DataFrames -if is_installed("DataFrames") - @eval begin - import DataFrames +# # if it's one symbol, set the guide and return the column +# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol) +# get!(d, Symbol(letter * "guide"), string(sym)) +# collect(df[sym]) +# end - # if it's one symbol, set the guide and return the column - function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol) - get!(d, Symbol(letter * "guide"), string(sym)) - collect(df[sym]) - end +# # 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}) +# get!(d, :label, reshape(syms, 1, length(syms))) +# Any[collect(df[s]) for s in syms] +# end - # 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}) - get!(d, :label, reshape(syms, 1, length(syms))) - Any[collect(df[s]) for s in syms] - end +# # for anything else, no-op +# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything) +# anything +# end - # for anything else, no-op - function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything) - anything - end +# # handle grouping by DataFrame column +# function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...) +# extractGroupArgs(collect(df[group])) +# end - # handle grouping by DataFrame column - function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...) - extractGroupArgs(collect(df[group])) - end +# # if a DataFrame is the first arg, lets swap symbols out for columns +# @recipe function f(df::DataFrames.AbstractDataFrame, args...) +# # 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) +# if haskey(d, k) && isa(d[k], Symbol) +# d[k] = collect(df[d[k]]) +# end +# end - # if a DataFrame is the first arg, lets swap symbols out for columns - @recipe function f(df::DataFrames.AbstractDataFrame, args...) - # 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) - if haskey(d, k) && isa(d[k], Symbol) - d[k] = collect(df[d[k]]) - end - end - - # 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)]...) - end - end -end +# # 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)]...) +# end +# end +# end # --------------------------------------------------------------------------- @@ -553,6 +553,8 @@ end # 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 + # --------------------------------------------------------------------------- # Box Plot @@ -794,6 +796,8 @@ end @deps xerror path +# TODO: move quiver to PlotRecipes + # --------------------------------------------------------------------------- # quiver @@ -943,7 +947,8 @@ end # ------------------------------------------------- -# TODO: this should really be in another package... +# TODO: move OHLC to PlotRecipes finance.jl + type OHLC{T<:Real} open::T high::T diff --git a/src/series.jl b/src/series.jl index df2d8d28..156f5cdc 100644 --- a/src/series.jl +++ b/src/series.jl @@ -277,10 +277,10 @@ end # # # function without range... use the current range of the x-axis -@recipe function f(f::FuncOrFuncs) - plt = d[:plot_object] - f, xmin(plt), xmax(plt) -end +# @recipe function f(f::FuncOrFuncs) +# plt = d[:plot_object] +# f, xmin(plt), xmax(plt) +# end # # # -------------------------------------------------------------------- diff --git a/src/utils.jl b/src/utils.jl index 97f0ea94..434ee17f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -284,20 +284,16 @@ function replaceType(vec, val) push!(vec, val) end -function replaceAlias!(d::KW, k::Symbol, aliases::KW) +function replaceAlias!(d::KW, k::Symbol, aliases::Dict{Symbol,Symbol}) if haskey(aliases, k) d[aliases[k]] = pop!(d, k) end end -function replaceAliases!(d::KW, aliases::KW) +function replaceAliases!(d::KW, aliases::Dict{Symbol,Symbol}) ks = collect(keys(d)) for k in ks replaceAlias!(d, k, aliases) - # if haskey(aliases, k) - # d[aliases[k]] = d[k] - # delete!(d, k) - # end end end