From a0df6bfc12799fe8b58c9c7007bbe11d94599ae6 Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Thu, 17 Mar 2016 18:27:33 -0400 Subject: [PATCH] groupby overhaul --- src/plot.jl | 155 +++++++++++++++++++++++++++++++++++---------- src/series_args.jl | 85 +++++++++++++++---------- 2 files changed, 173 insertions(+), 67 deletions(-) diff --git a/src/plot.jl b/src/plot.jl index 9b348548..8f516d83 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -78,25 +78,140 @@ function plot!(plt::Plot, args...; kw...) args = _apply_recipe(d, args...; kw...) dumpdict(d, "After plot! preprocessing") + # @show groupargs map(typeof, args) warnOnUnsupportedArgs(plt.backend, d) # just in case the backend needs to set up the plot (make it current or something) _before_add_series(plt) - # grouping - groupargs = get(d, :group, nothing) == nothing ? [nothing] : [extractGroupArgs(d[:group], args...)] - # @show groupargs + # # grouping + # groupargs = get(d, :group, nothing) == nothing ? [nothing] : [extractGroupArgs(d[:group], args...)] + # # @show groupargs # TODO: get the GroupBy object (or nothing), and loop through the groups, doing the following section many times + # dumpdict(d, "before", true) + groupby = if haskey(d, :group) + extractGroupArgs(d[:group], args...) + else + nothing + end + # dumpdict(d, "after", true) + # @show groupby map(typeof, args) + _add_series(plt, d, groupby, args...) + + # + # # get the list of dictionaries, one per series + # @show groupargs map(typeof, args) + # dumpdict(d, "before process_inputs") + # process_inputs(plt, d, groupargs..., args...) + # dumpdict(d, "after process_inputs", true) + # seriesArgList, xmeta, ymeta = build_series_args(plt, d) + # # seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...) + # + # # if we were able to extract guide information from the series inputs, then update the plot + # # @show xmeta, ymeta + # updateDictWithMeta(d, plt.plotargs, xmeta, true) + # updateDictWithMeta(d, plt.plotargs, ymeta, false) + # + # # now we can plot the series + # for (i,di) in enumerate(seriesArgList) + # plt.n += 1 + # + # if !stringsSupported() + # setTicksFromStringVector(d, di, :x, :xticks) + # setTicksFromStringVector(d, di, :y, :yticks) + # end + # + # # remove plot args + # for k in keys(_plotDefaults) + # delete!(di, k) + # end + # + # dumpdict(di, "Series $i") + # + # _add_series(plt.backend, plt; di...) + # end + + _add_annotations(plt, d) + + warnOnUnsupportedScales(plt.backend, d) + + + # add title, axis labels, ticks, etc + if !haskey(d, :subplot) + merge!(plt.plotargs, d) + dumpdict(plt.plotargs, "Updating plot items") + _update_plot(plt, plt.plotargs) + end + + _update_plot_pos_size(plt, d) + + current(plt) + + # NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement + # # do we want to show it? + if haskey(d, :show) && d[:show] + gui() + end + + plt +end + +# handle the grouping +function _add_series(plt::Plot, d::KW, groupby::GroupBy, args...) + # error("ERRORRRRRRRR") + + starting_n = plt.n + for (i, glab) in enumerate(groupby.groupLabels) + tmpd = copy(d) + # d[:numUncounted] = i - 1 + tmpd[:numUncounted] = plt.n - starting_n + _add_series(plt, tmpd, nothing, args...; + idxfilter = groupby.groupIds[i], + grouplabel = string(glab)) + end + + # ret = Any[] + # # error("unfinished after series reorg") + # for (i,glab) in enumerate(groupby.groupLabels) + # # TODO: don't automatically overwrite labels + # kwlist, xmeta, ymeta = process_inputs(plt, d, args..., + # idxfilter = groupby.groupIds[i], + # label = string(glab), + # numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i + # append!(ret, kwlist) + # end + # ret, nothing, nothing # TODO: handle passing meta through +end + +filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter] +filter_data(v, idxfilter) = v + +# no grouping +function _add_series(plt::Plot, d::KW, ::Void, args...; + idxfilter = nothing, + grouplabel = "") # get the list of dictionaries, one per series - @show groupargs map(typeof, args) dumpdict(d, "before process_inputs") - process_inputs(plt, d, groupargs..., args...) - dumpdict(d, "after process_inputs", true) - seriesArgList, xmeta, ymeta = build_series_args(plt, d) + process_inputs(plt, d, args...) + dumpdict(d, "after process_inputs") + + if idxfilter != nothing + # add the group name as the label if there isn't one passed in + get!(d, :label, grouplabel) + + # filter the data + for sym in (:x, :y, :z) + # @show "before" sym, d[sym], idxfilter + d[sym] = filter_data(get(d, sym, nothing), idxfilter) + # @show "after" sym, d[sym], idxfilter + end + end + + seriesArgList, xmeta, ymeta = build_series_args(plt, d) #, idxfilter) # seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...) # if we were able to extract guide information from the series inputs, then update the plot @@ -122,32 +237,6 @@ function plot!(plt::Plot, args...; kw...) _add_series(plt.backend, plt; di...) end - - # TODO: this is the end of the groupby loop - - _add_annotations(plt, d) - - warnOnUnsupportedScales(plt.backend, d) - - - # add title, axis labels, ticks, etc - if !haskey(d, :subplot) - merge!(plt.plotargs, d) - dumpdict(plt.plotargs, "Updating plot items") - _update_plot(plt, plt.plotargs) - end - - _update_plot_pos_size(plt, d) - - current(plt) - - # NOTE: lets ignore the show param and effectively use the semicolon at the end of the REPL statement - # # do we want to show it? - if haskey(d, :show) && d[:show] - gui() - end - - plt end # -------------------------------------------------------------------- diff --git a/src/series_args.jl b/src/series_args.jl index fb13d43e..8b00869a 100644 --- a/src/series_args.jl +++ b/src/series_args.jl @@ -91,7 +91,7 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!") # create n=max(mx,my) series arguments. the shorter list is cycled through # note: everything should flow through this -function build_series_args(plt::AbstractPlot, kw::KW) +function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter) x, y, z = map(a -> pop!(kw, a, nothing), (:x, :y, :z)) if nothing == x == y == z return [], nothing, nothing @@ -101,6 +101,18 @@ function build_series_args(plt::AbstractPlot, kw::KW) ys, ymeta = convertToAnyVector(y, kw) zs, zmeta = convertToAnyVector(z, kw) + # if idxfilter != nothing + # xs = filter_data(xs, idxfilter) + # ys = filter_data(ys, idxfilter) + # zs = filter_data(zs, idxfilter) + # # # filter the data + # # for sym in (:x, :y, :z) + # # @show "before" sym, d[sym], idxfilter + # # d[sym] = filter_data(get(d, sym, nothing), idxfilter) + # # @show "after" sym, d[sym], idxfilter + # # end + # end + mx = length(xs) my = length(ys) mz = length(zs) @@ -119,15 +131,17 @@ function build_series_args(plt::AbstractPlot, kw::KW) # build the series arg dict numUncounted = pop!(d, :numUncounted, 0) - n = plt.n + i + numUncounted + commandIndex = i + numUncounted + n = plt.n + i dumpdict(d, "before getSeriesArgs") - d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, i + numUncounted, convertSeriesIndex(plt, n), n) + # @show numUncounted i n commandIndex convertSeriesIndex(plt, n) + d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n) dumpdict(d, "after getSeriesArgs") - @show map(typeof, (xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])) + # @show map(typeof, (xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])) d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]) - @show map(typeof, (d[:x], d[:y], d[:z])) + # @show map(typeof, (d[:x], d[:y], d[:z])) # # NOTE: this should be handled by the time it gets here @@ -140,11 +154,11 @@ function build_series_args(plt::AbstractPlot, kw::KW) # end # end - if haskey(d, :idxfilter) - idxfilter = pop!(d, :idxfilter) - d[:x] = d[:x][idxfilter] - d[:y] = d[:y][idxfilter] - end + # if haskey(d, :idxfilter) + # idxfilter = pop!(d, :idxfilter) + # d[:x] = d[:x][idxfilter] + # d[:y] = d[:y][idxfilter] + # end # for linetype `line`, need to sort by x values if lt == :line @@ -164,7 +178,7 @@ function build_series_args(plt::AbstractPlot, kw::KW) end # cleanup those fields that were used only for generating kw args - delete!(d, :dataframe) + # delete!(d, :dataframe) # for k in (:idxfilter, :numUncounted, :dataframe) # delete!(d, k) # end @@ -370,19 +384,19 @@ end # handle grouping # -------------------------------------------------------------------- -function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...) - ret = Any[] - error("unfinished after series reorg") - for (i,glab) in enumerate(groupby.groupLabels) - # TODO: don't automatically overwrite labels - kwlist, xmeta, ymeta = process_inputs(plt, d, args..., - idxfilter = groupby.groupIds[i], - label = string(glab), - numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i - append!(ret, kwlist) - end - ret, nothing, nothing # TODO: handle passing meta through -end +# function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...) +# ret = Any[] +# error("unfinished after series reorg") +# for (i,glab) in enumerate(groupby.groupLabels) +# # TODO: don't automatically overwrite labels +# kwlist, xmeta, ymeta = process_inputs(plt, d, args..., +# idxfilter = groupby.groupIds[i], +# label = string(glab), +# numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i +# append!(ret, kwlist) +# end +# ret, nothing, nothing # TODO: handle passing meta through +# end # -------------------------------------------------------------------- # For DataFrame support. Imports DataFrames and defines the necessary methods which support them. @@ -391,9 +405,12 @@ end function setup_dataframes() @require DataFrames begin + get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg] + get_data(df::DataFrames.AbstractDataFrame, arg) = arg + function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...) - d[:dataframe] = df - process_inputs(plt, d, args...) + # d[:dataframe] = df + process_inputs(plt, d, map(arg -> get_data(df, arg), args)...) end # expecting the column name of a dataframe that was passed in... anything else should error @@ -405,15 +422,15 @@ function setup_dataframes() end end - function getDataFrameFromKW(d::Dict) - get(d, :dataframe) do - error("Missing dataframe argument!") - end - end + # function getDataFrameFromKW(d::Dict) + # get(d, :dataframe) do + # error("Missing dataframe argument!") + # end + # end - # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes - convertToAnyVector(s::Symbol, d::Dict) = Any[getDataFrameFromKW(d)[s]], s - convertToAnyVector(v::AVec{Symbol}, d::Dict) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v + # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes + # convertToAnyVector(s::Symbol, d::Dict) = Any[getDataFrameFromKW(d)[s]], s + # convertToAnyVector(v::AVec{Symbol}, d::Dict) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v end end