rename some functions

This commit is contained in:
Daniel Schwabeneder 2020-04-02 20:12:03 +02:00
parent 6e3c218d89
commit f86b324200
8 changed files with 212 additions and 165 deletions

View File

@ -44,7 +44,7 @@ PlotThemes = "1"
PlotUtils = "0.6.5" PlotUtils = "0.6.5"
RecipesBase = "0.8" RecipesBase = "0.8"
Reexport = "0.2" Reexport = "0.2"
Requires = "0.5, 1.0" Requires = "0.5, 1"
Showoff = "0.3.1" Showoff = "0.3.1"
StatsBase = "0.32" StatsBase = "0.32"
julia = "1" julia = "1"

View File

@ -222,7 +222,7 @@ end
const CURRENT_BACKEND = CurrentBackend(:none) const CURRENT_BACKEND = CurrentBackend(:none)
include("precompile.jl") # include("precompile.jl")
_precompile_() # _precompile_()
end # module end # module

View File

@ -682,7 +682,7 @@ end
function default(; kw...) function default(; kw...)
kw = KW(kw) kw = KW(kw)
preprocessArgs!(kw) preprocess_attributes!(kw)
for (k,v) in kw for (k,v) in kw
default(k, v) default(k, v)
end end
@ -935,7 +935,7 @@ function _add_markershape(plotattributes::AKW)
end end
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases." "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
function preprocessArgs!(plotattributes::AKW) function preprocess_attributes!(plotattributes::AKW)
replaceAliases!(plotattributes, _keyAliases) replaceAliases!(plotattributes, _keyAliases)
# handle axis args common to all axis # handle axis args common to all axis
@ -1122,7 +1122,7 @@ end
# this is when given a vector-type of values to group by # this is when given a vector-type of values to group by
function extractGroupArgs(v::AVec, args...; legendEntry = string) function _extract_group_attributes(v::AVec, args...; legendEntry = string)
groupLabels = sort(collect(unique(v))) groupLabels = sort(collect(unique(v)))
n = length(groupLabels) n = length(groupLabels)
if n > 100 if n > 100
@ -1135,24 +1135,24 @@ end
legendEntryFromTuple(ns::Tuple) = join(ns, ' ') legendEntryFromTuple(ns::Tuple) = join(ns, ' ')
# this is when given a tuple of vectors of values to group by # this is when given a tuple of vectors of values to group by
function extractGroupArgs(vs::Tuple, args...) function _extract_group_attributes(vs::Tuple, args...)
isempty(vs) && return GroupBy([""], [axes(args[1],1)]) isempty(vs) && return GroupBy([""], [axes(args[1],1)])
v = map(tuple, vs...) v = map(tuple, vs...)
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple) _extract_group_attributes(v, args...; legendEntry = legendEntryFromTuple)
end end
# allow passing NamedTuples for a named legend entry # allow passing NamedTuples for a named legend entry
legendEntryFromTuple(ns::NamedTuple) = legendEntryFromTuple(ns::NamedTuple) =
join(["$k = $v" for (k, v) in pairs(ns)], ", ") join(["$k = $v" for (k, v) in pairs(ns)], ", ")
function extractGroupArgs(vs::NamedTuple, args...) function _extract_group_attributes(vs::NamedTuple, args...)
isempty(vs) && return GroupBy([""], [axes(args[1],1)]) isempty(vs) && return GroupBy([""], [axes(args[1],1)])
v = map(NamedTuple{keys(vs)}tuple, values(vs)...) v = map(NamedTuple{keys(vs)}tuple, values(vs)...)
extractGroupArgs(v, args...; legendEntry = legendEntryFromTuple) _extract_group_attributes(v, args...; legendEntry = legendEntryFromTuple)
end end
# expecting a mapping of "group label" to "group indices" # expecting a mapping of "group label" to "group indices"
function extractGroupArgs(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}} function _extract_group_attributes(idxmap::Dict{T,V}, args...) where {T, V<:AVec{Int}}
groupLabels = sortedkeys(idxmap) groupLabels = sortedkeys(idxmap)
groupIds = Vector{Int}[collect(idxmap[k]) for k in groupLabels] groupIds = Vector{Int}[collect(idxmap[k]) for k in groupLabels]
GroupBy(groupLabels, groupIds) GroupBy(groupLabels, groupIds)
@ -1180,7 +1180,7 @@ end
const _already_warned = Dict{Symbol,Set{Symbol}}() const _already_warned = Dict{Symbol,Set{Symbol}}()
const _to_warn = Set{Symbol}() const _to_warn = Set{Symbol}()
function warnOnUnsupported_args(pkg::AbstractBackend, plotattributes) function warn_on_unsupported_args(pkg::AbstractBackend, plotattributes)
empty!(_to_warn) empty!(_to_warn)
bend = backend_name(pkg) bend = backend_name(pkg)
already_warned = get!(_already_warned, bend, Set{Symbol}()) already_warned = get!(_already_warned, bend, Set{Symbol}())
@ -1204,7 +1204,7 @@ end
# _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg) # _markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
# _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes]) # _markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
function warnOnUnsupported(pkg::AbstractBackend, plotattributes) function warn_on_unsupported(pkg::AbstractBackend, plotattributes)
if !is_seriestype_supported(pkg, plotattributes[:seriestype]) if !is_seriestype_supported(pkg, plotattributes[:seriestype])
@warn("seriestype $(plotattributes[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))") @warn("seriestype $(plotattributes[:seriestype]) is unsupported with $pkg. Choose from: $(supported_seriestypes(pkg))")
end end
@ -1216,7 +1216,7 @@ function warnOnUnsupported(pkg::AbstractBackend, plotattributes)
end end
end end
function warnOnUnsupported_scales(pkg::AbstractBackend, plotattributes::AKW) function warn_on_unsupported_scales(pkg::AbstractBackend, plotattributes::AKW)
for k in (:xscale, :yscale, :zscale, :scale) for k in (:xscale, :yscale, :zscale, :scale)
if haskey(plotattributes, k) if haskey(plotattributes, k)
v = plotattributes[k] v = plotattributes[k]

View File

@ -85,7 +85,7 @@ function attr!(axis::Axis, args...; kw...)
end end
# then preprocess keyword arguments # then preprocess keyword arguments
preprocessArgs!(KW(kw)) preprocess_attributes!(KW(kw))
# then override for any keywords... only those keywords that already exists in plotattributes # then override for any keywords... only those keywords that already exists in plotattributes
for (k,v) in kw for (k,v) in kw

View File

@ -52,7 +52,7 @@ function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{Re
# we simply add the GroupBy object to the front of the args list to allow # we simply add the GroupBy object to the front of the args list to allow
# the recipe to be applied # the recipe to be applied
if haskey(plotattributes, :group) if haskey(plotattributes, :group)
args = (extractGroupArgs(plotattributes[:group], args...), args...) args = (_extract_group_attributes(plotattributes[:group], args...), args...)
end end
# if we were passed a vector/matrix of seriestypes and there's more than one row, # if we were passed a vector/matrix of seriestypes and there's more than one row,
@ -61,7 +61,8 @@ function _preprocess_args(plotattributes::AKW, args, still_to_process::Vector{Re
append!(still_to_process, _expand_seriestype_array(plotattributes, args)) append!(still_to_process, _expand_seriestype_array(plotattributes, args))
end end
# remove subplot and axis args from plotattributes... they will be passed through in the kw_list # remove subplot and axis args from plotattributes...
# they will be passed through in the kw_list
if !isempty(args) if !isempty(args)
for (k, v) in plotattributes for (k, v) in plotattributes
if k in _all_subplot_args || k in _all_axis_args if k in _all_subplot_args || k in _all_axis_args
@ -90,20 +91,19 @@ function _process_userrecipes(plt::Plot, plotattributes::AKW, args)
kw_list = KW[] kw_list = KW[]
while !isempty(still_to_process) while !isempty(still_to_process)
# grab the first in line to be processed and either add it to the kw_list or # grab the first in line to be processed and either add it to the kw_list or
# pass it through apply_recipe to generate a list of RecipeData objects (data + attributes) # pass it through apply_recipe to generate a list of RecipeData objects
# for further processing. # (data + attributes) for further processing.
next_series = popfirst!(still_to_process) next_series = popfirst!(still_to_process)
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes # recipedata should be of type RecipeData.
# if it's not then the inputs must not have been fully processed by recipes
if !(typeof(next_series) <: RecipeData) if !(typeof(next_series) <: RecipeData)
error("Inputs couldn't be processed... expected RecipeData but got: $next_series") error("Inputs couldn't be processed... expected RecipeData but got: $next_series")
end end
if isempty(next_series.args) if isempty(next_series.args)
_process_userrecipe(plt, kw_list, next_series) _process_userrecipe(plt, kw_list, next_series)
else else
rd_list = RecipesBase.apply_recipe( rd_list =
next_series.plotattributes, RecipesBase.apply_recipe(next_series.plotattributes, next_series.args...)
next_series.args...
)
warn_on_recipe_aliases!(rd_list, :user, next_series.args...) warn_on_recipe_aliases!(rd_list, :user, next_series.args...)
prepend!(still_to_process, rd_list) prepend!(still_to_process, rd_list)
end end
@ -118,9 +118,9 @@ function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeD
# when the arg tuple is empty, that means there's nothing left to recursively # when the arg tuple is empty, that means there's nothing left to recursively
# process... finish up and add to the kw_list # process... finish up and add to the kw_list
kw = recipedata.plotattributes kw = recipedata.plotattributes
preprocessArgs!(kw) preprocess_attributes!(kw)
_preprocess_userrecipe(kw) _preprocess_userrecipe(kw)
warnOnUnsupported_scales(plt.backend, kw) warn_on_unsupported_scales(plt.backend, kw)
# add the plot index # add the plot index
plt.n += 1 plt.n += 1
@ -141,12 +141,14 @@ function _preprocess_userrecipe(kw::AKW)
# map marker_z if it's a Function # map marker_z if it's a Function
if isa(get(kw, :marker_z, nothing), Function) if isa(get(kw, :marker_z, nothing), Function)
# TODO: should this take y and/or z as arguments? # TODO: should this take y and/or z as arguments?
kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) : map(kw[:marker_z], kw[:x], kw[:y], kw[:z]) kw[:marker_z] = isa(kw[:z], Nothing) ? map(kw[:marker_z], kw[:x], kw[:y]) :
map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
end end
# map line_z if it's a Function # map line_z if it's a Function
if isa(get(kw, :line_z, nothing), Function) if isa(get(kw, :line_z, nothing), Function)
kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) : map(kw[:line_z], kw[:x], kw[:y], kw[:z]) kw[:line_z] = isa(kw[:z], Nothing) ? map(kw[:line_z], kw[:x], kw[:y]) :
map(kw[:line_z], kw[:x], kw[:y], kw[:z])
end end
# convert a ribbon into a fillrange # convert a ribbon into a fillrange
@ -178,14 +180,20 @@ function _add_smooth_kw(kw_list::Vector{KW}, kw::AKW)
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y) β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
sx = [ignorenan_minimum(x), ignorenan_maximum(x)] sx = [ignorenan_minimum(x), ignorenan_maximum(x)]
sy = β .* sx .+ α sy = β .* sx .+ α
push!(kw_list, merge(copy(kw), KW( push!(
kw_list,
merge(
copy(kw),
KW(
:seriestype => :path, :seriestype => :path,
:x => sx, :x => sx,
:y => sy, :y => sy,
:fillrange => nothing, :fillrange => nothing,
:label => "", :label => "",
:primary => false, :primary => false,
))) ),
),
)
end end
end end
@ -196,7 +204,12 @@ end
# to generate a list of RecipeData objects (data + attributes). # to generate a list of RecipeData objects (data + attributes).
# If we applied a "plot recipe" without error, then add the returned datalist's KWs, # If we applied a "plot recipe" without error, then add the returned datalist's KWs,
# otherwise we just add the original KW. # otherwise we just add the original KW.
function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_process::Vector{KW}) function _process_plotrecipe(
plt::Plot,
kw::AKW,
kw_list::Vector{KW},
still_to_process::Vector{KW},
)
if !isa(get(kw, :seriestype, nothing), Symbol) 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 # seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
push!(kw_list, kw) push!(kw_list, kw)
@ -208,7 +221,7 @@ function _process_plotrecipe(plt::Plot, kw::AKW, kw_list::Vector{KW}, still_to_p
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt) datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
warn_on_recipe_aliases!(datalist, :plot, st) warn_on_recipe_aliases!(datalist, :plot, st)
for data in datalist for data in datalist
preprocessArgs!(data.plotattributes) preprocess_attributes!(data.plotattributes)
if data.plotattributes[:seriestype] == st if data.plotattributes[:seriestype] == st
error("Plot recipe $st returned the same seriestype: $(data.plotattributes)") error("Plot recipe $st returned the same seriestype: $(data.plotattributes)")
end end
@ -286,7 +299,13 @@ function _subplot_setup(plt::Plot, plotattributes::AKW, kw_list::Vector{KW})
for kw in kw_list for kw in kw_list
# get the Subplot object to which the series belongs. # get the Subplot object to which the series belongs.
sps = get(kw, :subplot, :auto) sps = get(kw, :subplot, :auto)
sp = get_subplot(plt, _cycle(sps == :auto ? plt.subplots : plt.subplots[sps], series_idx(kw_list,kw))) sp = get_subplot(
plt,
_cycle(
sps == :auto ? plt.subplots : plt.subplots[sps],
series_idx(kw_list, kw),
),
)
kw[:subplot] = sp kw[:subplot] = sp
# extract subplot/axis attributes from kw and add to sp_attr # extract subplot/axis attributes from kw and add to sp_attr
@ -328,7 +347,7 @@ end
# getting ready to add the series... last update to subplot from anything # getting ready to add the series... last update to subplot from anything
# that might have been added during series recipes # that might have been added during series recipes
function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where T function _prepare_subplot(plt::Plot{T}, plotattributes::AKW) where {T}
st::Symbol = plotattributes[:seriestype] st::Symbol = plotattributes[:seriestype]
sp::Subplot{T} = plotattributes[:subplot] sp::Subplot{T} = plotattributes[:subplot]
sp_idx = get_subplot_index(plt, sp) sp_idx = get_subplot_index(plt, sp)
@ -356,7 +375,8 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol)
# do we want to override the series type? # do we want to override the series type?
if !is3d(st) && !(st in (:contour, :contour3d)) if !is3d(st) && !(st in (:contour, :contour3d))
z = plotattributes[:z] z = plotattributes[:z]
if !isa(z, Nothing) && (size(plotattributes[:x]) == size(plotattributes[:y]) == size(z)) if !isa(z, Nothing) &&
(size(plotattributes[:x]) == size(plotattributes[:y]) == size(z))
st = (st == :scatter ? :scatter3d : :path3d) st = (st == :scatter ? :scatter3d : :path3d)
plotattributes[:seriestype] = st plotattributes[:seriestype] = st
end end
@ -364,27 +384,11 @@ function _override_seriestype_check(plotattributes::AKW, st::Symbol)
st st
end end
function _prepare_annotations(sp::Subplot, plotattributes::AKW)
# 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])
# series_anns = annotations(pop!(plotattributes, :series_annotations, []))
# if isa(series_anns, SeriesAnnotations)
# series_anns.x = plotattributes[:x]
# series_anns.y = plotattributes[:y]
# elseif length(series_anns) > 0
# x, y = plotattributes[:x], plotattributes[:y]
# nx, ny, na = map(length, (x,y,series_anns))
# n = max(nx, ny, na)
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
# end
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
end
function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol) function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol)
# adjust extrema and discrete info # adjust extrema and discrete info
if st == :image if st == :image
xmin, xmax = ignorenan_extrema(plotattributes[:x]); ymin, ymax = ignorenan_extrema(plotattributes[:y]) xmin, xmax = ignorenan_extrema(plotattributes[:x])
ymin, ymax = ignorenan_extrema(plotattributes[:y])
expand_extrema!(sp[:xaxis], (xmin, xmax)) expand_extrema!(sp[:xaxis], (xmin, xmax))
expand_extrema!(sp[:yaxis], (ymin, ymax)) expand_extrema!(sp[:yaxis], (ymin, ymax))
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d)) elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
@ -398,8 +402,8 @@ function _expand_subplot_extrema(sp::Subplot, plotattributes::AKW, st::Symbol)
end end
function _add_the_series(plt, sp, plotattributes) function _add_the_series(plt, sp, plotattributes)
warnOnUnsupported_args(plt.backend, plotattributes) warn_on_unsupported_args(plt.backend, plotattributes)
warnOnUnsupported(plt.backend, plotattributes) warn_on_unsupported(plt.backend, plotattributes)
series = Series(plotattributes) series = Series(plotattributes)
push!(plt.series_list, series) push!(plt.series_list, series)
push!(sp.series_list, series) push!(sp.series_list, series)
@ -411,7 +415,6 @@ end
# this method recursively applies series recipes when the seriestype is not supported # this method recursively applies series recipes when the seriestype is not supported
# natively by the backend # natively by the backend
function _process_seriesrecipe(plt::Plot, plotattributes::AKW) function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
#println("process $(typeof(plotattributes))")
# replace seriestype aliases # replace seriestype aliases
st = Symbol(plotattributes[:seriestype]) st = Symbol(plotattributes[:seriestype])
st = plotattributes[:seriestype] = get(_typeAliases, st, st) st = plotattributes[:seriestype] = get(_typeAliases, st, st)
@ -421,10 +424,10 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
plotattributes[:fillrange] = nothing plotattributes[:fillrange] = nothing
end end
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse # if it's natively supported, finalize processing and pass along to the backend,
# otherwise recurse
if is_seriestype_supported(st) if is_seriestype_supported(st)
sp = _prepare_subplot(plt, plotattributes) sp = _prepare_subplot(plt, plotattributes)
_prepare_annotations(sp, plotattributes)
_expand_subplot_extrema(sp, plotattributes, st) _expand_subplot_extrema(sp, plotattributes, st)
_update_series_attributes!(plotattributes, plt, sp) _update_series_attributes!(plotattributes, plt, sp)
_add_the_series(plt, sp, plotattributes) _add_the_series(plt, sp, plotattributes)
@ -438,7 +441,7 @@ function _process_seriesrecipe(plt::Plot, plotattributes::AKW)
# assuming there was no error, recursively apply the series recipes # assuming there was no error, recursively apply the series recipes
for data in datalist for data in datalist
if isa(data, RecipeData) if isa(data, RecipeData)
preprocessArgs!(data.plotattributes) preprocess_attributes!(data.plotattributes)
if data.plotattributes[:seriestype] == st if data.plotattributes[:seriestype] == st
error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.") error("The seriestype didn't change in series recipe $st. This will cause a StackOverflow.")
end end

View File

@ -49,7 +49,7 @@ as a String to look up its docstring; e.g. `plotattr("seriestype")`.
function plot(args...; kw...) function plot(args...; kw...)
# this creates a new plot with args/kw and sets it to be the current plot # this creates a new plot with args/kw and sets it to be the current plot
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocess_attributes!(plotattributes)
# create an empty Plot then process # create an empty Plot then process
plt = Plot() plt = Plot()
@ -61,7 +61,7 @@ end
# note: we split into plt1 and plts_tail so we can dispatch correctly # note: we split into plt1 and plts_tail so we can dispatch correctly
function plot(plt1::Plot, plts_tail::Plot...; kw...) function plot(plt1::Plot, plts_tail::Plot...; kw...)
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocess_attributes!(plotattributes)
# build our plot vector from the args # build our plot vector from the args
n = length(plts_tail) + 1 n = length(plts_tail) + 1
@ -153,7 +153,7 @@ end
# this adds to a specific plot... most plot commands will flow through here # this adds to a specific plot... most plot commands will flow through here
function plot!(plt::Plot, args...; kw...) function plot!(plt::Plot, args...; kw...)
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocess_attributes!(plotattributes)
# merge!(plt.user_attr, plotattributes) # merge!(plt.user_attr, plotattributes)
_plot!(plt, plotattributes, args) _plot!(plt, plotattributes, args)
end end

View File

@ -11,29 +11,32 @@ const MaybeNumber = Union{Number, Missing}
const MaybeString = Union{AbstractString, Missing} const MaybeString = Union{AbstractString, Missing}
const DataPoint = Union{MaybeNumber, MaybeString} const DataPoint = Union{MaybeNumber, MaybeString}
prepareSeriesData(x) = error("Cannot convert $(typeof(x)) to series data for plotting") prepare_series_data(x) = error("Cannot convert $(typeof(x)) to series data for plotting")
prepareSeriesData(::Nothing) = nothing prepare_series_data(::Nothing) = nothing
prepareSeriesData(t::Tuple{T, T}) where {T<:Number} = t prepare_series_data(t::Tuple{T, T}) where {T <: Number} = t
prepareSeriesData(f::Function) = f prepare_series_data(f::Function) = f
prepareSeriesData(ar::AbstractRange{<:Number}) = ar prepare_series_data(ar::AbstractRange{<:Number}) = ar
function prepareSeriesData(a::AbstractArray{<:MaybeNumber}) function prepare_series_data(a::AbstractArray{<:MaybeNumber})
f = isimmutable(a) ? replace : replace! f = isimmutable(a) ? replace : replace!
a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a)) a = f(x -> ismissing(x) || isinf(x) ? NaN : x, map(float, a))
end end
prepareSeriesData(a::AbstractArray{<:Missing}) = fill(NaN, axes(a)) prepare_series_data(a::AbstractArray{<:Missing}) = fill(NaN, axes(a))
prepareSeriesData(a::AbstractArray{<:MaybeString}) = replace(x -> ismissing(x) ? "" : x, a) prepare_series_data(a::AbstractArray{<:MaybeString}) =
prepareSeriesData(s::Surface{<:AMat{<:MaybeNumber}}) = Surface(prepareSeriesData(s.surf)) replace(x -> ismissing(x) ? "" : x, a)
prepareSeriesData(s::Surface) = s # non-numeric Surface, such as an image prepare_series_data(s::Surface{<:AMat{<:MaybeNumber}}) =
prepareSeriesData(v::Volume) = Volume(prepareSeriesData(v.v), v.x_extents, v.y_extents, v.z_extents) Surface(prepare_series_data(s.surf))
prepare_series_data(s::Surface) = s # non-numeric Surface, such as an image
prepare_series_data(v::Volume) =
Volume(prepare_series_data(v.v), v.x_extents, v.y_extents, v.z_extents)
# default: assume x represents a single series # default: assume x represents a single series
series_vector(x, plotattributes) = [prepareSeriesData(x)] series_vector(x, plotattributes) = [prepare_series_data(x)]
# fixed number of blank series # fixed number of blank series
series_vector(n::Integer, plotattributes) = [zeros(0) for i in 1:n] series_vector(n::Integer, plotattributes) = [zeros(0) for i in 1:n]
# vector of data points is a single series # vector of data points is a single series
series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepareSeriesData(v)] series_vector(v::AVec{<:DataPoint}, plotattributes) = [prepare_series_data(v)]
# list of things (maybe other vectors, functions, or something else) # list of things (maybe other vectors, functions, or something else)
function series_vector(v::AVec, plotattributes) function series_vector(v::AVec, plotattributes)
@ -49,9 +52,9 @@ end
# Matrix is split into columns # Matrix is split into columns
function series_vector(v::AMat{<:DataPoint}, plotattributes) function series_vector(v::AMat{<:DataPoint}, plotattributes)
if all3D(plotattributes) if all3D(plotattributes)
[prepareSeriesData(Surface(v))] [prepare_series_data(Surface(v))]
else else
[prepareSeriesData(v[:, i]) for i in axes(v, 2)] [prepare_series_data(v[:, i]) for i in axes(v, 2)]
end end
end end
@ -99,8 +102,10 @@ nobigs(v) = v
end end
# not allowed # not allowed
compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F<:Function} = error("If you want to plot the function `$y`, you need to define the x values!") compute_xyz(x::Nothing, y::FuncOrFuncs{F}, z) where {F <: Function} =
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F<:Function} = error("If you want to plot the function `$z`, you need to define x and y values!") error("If you want to plot the function `$y`, you need to define the x values!")
compute_xyz(x::Nothing, y::Nothing, z::FuncOrFuncs{F}) where {F <: Function} =
error("If you want to plot the function `$z`, you need to define x and y values!")
compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!") compute_xyz(x::Nothing, y::Nothing, z::Nothing) = error("x/y/z are all nothing!")
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -176,10 +181,11 @@ end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# this is the default "type recipe"... just pass the object through # this is the default "type recipe"... just pass the object through
@recipe f(::Type{T}, v::T) where T = v @recipe f(::Type{T}, v::T) where {T} = v
# this should catch unhandled "series recipes" and error with a nice message # this should catch unhandled "series recipes" and error with a nice message
@recipe f(::Type{V}, x, y, z) where {V<:Val} = error("The backend must not support the series type $V, and there isn't a series recipe defined.") @recipe f(::Type{V}, x, y, z) where {V <: Val} =
error("The backend must not support the series type $V, and there isn't a series recipe defined.")
function _apply_type_recipe(plotattributes, v, letter) function _apply_type_recipe(plotattributes, v, letter)
_preprocess_axis_args!(plotattributes, letter) _preprocess_axis_args!(plotattributes, letter)
@ -309,13 +315,16 @@ end
# so we'll apply_type_recipe to all of them # so we'll apply_type_recipe to all of them
@recipe function f(v1, v2, v3, v4, vrest...) @recipe function f(v1, v2, v3, v4, vrest...)
did_replace = false did_replace = false
newargs = map(v -> begin newargs = map(
v -> begin
newv = _apply_type_recipe(plotattributes, v, :unknown) newv = _apply_type_recipe(plotattributes, v, :unknown)
if newv !== v if newv !== v
did_replace = true did_replace = true
end end
newv newv
end, (v1, v2, v3, v4, vrest...)) end,
(v1, v2, v3, v4, vrest...),
)
if !did_replace if !did_replace
error("Couldn't process recipe args: $(map(typeof, (v1, v2, v3, v4, vrest...)))") error("Couldn't process recipe args: $(map(typeof, (v1, v2, v3, v4, vrest...)))")
end end
@ -344,7 +353,8 @@ end
# 1 argument # 1 argument
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@recipe f(n::Integer) = is3d(get(plotattributes,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing) @recipe f(n::Integer) = is3d(get(plotattributes, :seriestype, :path)) ? (SliceIt, n, n, n) :
(SliceIt, n, n, nothing)
all3D(plotattributes) = trueOrAllTrue( all3D(plotattributes) = trueOrAllTrue(
st -> st in ( st -> st in (
@ -408,12 +418,12 @@ end
yflip --> true yflip --> true
cbar --> false cbar --> false
fillcolor --> ColorGradient([:black, :white]) fillcolor --> ColorGradient([:black, :white])
SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0., 1.)) SliceIt, m, n, Surface(clamp!(convert(Matrix{Float64}, mat), 0.0, 1.0))
end end
end end
# images - colors # images - colors
@recipe function f(mat::AMat{T}) where T<:Colorant @recipe function f(mat::AMat{T}) where {T <: Colorant}
n, m = axes(mat) n, m = axes(mat)
if is_seriestype_supported(:image) if is_seriestype_supported(:image)
@ -455,7 +465,7 @@ end
# function without range... use the current range of the x-axis # function without range... use the current range of the x-axis
@recipe function f(f::FuncOrFuncs{F}) where F<:Function @recipe function f(f::FuncOrFuncs{F}) where {F <: Function}
plt = plotattributes[:plot_object] plt = plotattributes[:plot_object]
xmin, xmax = if haskey(plotattributes, :xlims) xmin, xmax = if haskey(plotattributes, :xlims)
plotattributes[:xlims] plotattributes[:xlims]
@ -480,7 +490,7 @@ end
# if functions come first, just swap the order (not to be confused with parametric # if functions come first, just swap the order (not to be confused with parametric
# functions... as there would be more than one function passed in) # functions... as there would be more than one function passed in)
@recipe function f(f::FuncOrFuncs{F}, x) where F<:Function @recipe function f(f::FuncOrFuncs{F}, x) where {F <: Function}
F2 = typeof(x) F2 = typeof(x)
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here @assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
x, f x, f
@ -505,7 +515,7 @@ end
end end
# images - grays # images - grays
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Gray @recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Gray}
if is_seriestype_supported(:image) if is_seriestype_supported(:image)
seriestype := :image seriestype := :image
yflip --> true yflip --> true
@ -520,7 +530,7 @@ end
end end
# images - colors # images - colors
@recipe function f(x::AVec, y::AVec, mat::AMat{T}) where T<:Colorant @recipe function f(x::AVec, y::AVec, mat::AMat{T}) where {T <: Colorant}
if is_seriestype_supported(:image) if is_seriestype_supported(:image)
seriestype := :image seriestype := :image
yflip --> true yflip --> true
@ -541,15 +551,25 @@ end
# special handling... xmin/xmax with parametric function(s) # special handling... xmin/xmax with parametric function(s)
@recipe function f(f::Function, xmin::Number, xmax::Number) @recipe function f(f::Function, xmin::Number, xmax::Number)
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)] xscale, yscale = [get(plotattributes, sym, :identity) for sym in (:xscale, :yscale)]
_scaled_adapted_grid(f, xscale, yscale, xmin, xmax) _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
end end
@recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where F<:Function @recipe function f(fs::AbstractArray{F}, xmin::Number, xmax::Number) where {F <: Function}
xscale, yscale = [get(plotattributes, sym, :identity) for sym=(:xscale,:yscale)] xscale, yscale = [get(plotattributes, sym, :identity) for sym in (:xscale, :yscale)]
unzip(_scaled_adapted_grid.(fs, xscale, yscale, xmin, xmax)) unzip(_scaled_adapted_grid.(fs, xscale, yscale, xmin, xmax))
end end
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) where {F<:Function,G<:Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u) @recipe f(
@recipe f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) where {F<:Function,G<:Function} = fx, fy, range(umin, stop = umax, length = n) fx::FuncOrFuncs{F},
fy::FuncOrFuncs{G},
u::AVec,
) where {F <: Function, G <: Function} = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
@recipe f(
fx::FuncOrFuncs{F},
fy::FuncOrFuncs{G},
umin::Number,
umax::Number,
n = 200,
) where {F <: Function, G <: Function} = fx, fy, range(umin, stop = umax, length = n)
function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax) function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
(xf, xinv), (yf, yinv) = ((scalefunc(s), invscalefunc(s)) for s in (xscale, yscale)) (xf, xinv), (yf, yinv) = ((scalefunc(s), invscalefunc(s)) for s in (xscale, yscale))
@ -558,10 +578,22 @@ function _scaled_adapted_grid(f, xscale, yscale, xmin, xmax)
end end
# special handling... 3D parametric function(s) # special handling... 3D parametric function(s)
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, u::AVec) where {F<:Function,G<:Function,H<:Function} @recipe function f(
fx::FuncOrFuncs{F},
fy::FuncOrFuncs{G},
fz::FuncOrFuncs{H},
u::AVec,
) where {F <: Function, G <: Function, H <: Function}
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u) mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
end end
@recipe function f(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200) where {F<:Function,G<:Function,H<:Function} @recipe function f(
fx::FuncOrFuncs{F},
fy::FuncOrFuncs{G},
fz::FuncOrFuncs{H},
umin::Number,
umax::Number,
numPoints = 200,
) where {F <: Function, G <: Function, H <: Function}
fx, fy, fz, range(umin, stop = umax, length = numPoints) fx, fy, fz, range(umin, stop = umax, length = numPoints)
end end
@ -576,7 +608,9 @@ end
@recipe f(p::GeometryTypes.Point) = [p] @recipe f(p::GeometryTypes.Point) = [p]
# Special case for 4-tuples in :ohlc series # Special case for 4-tuples in :ohlc series
@recipe f(xyuv::AVec{<:Tuple{R1,R2,R3,R4}}) where {R1,R2,R3,R4} = get(plotattributes,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv) @recipe f(xyuv::AVec{<:Tuple{R1, R2, R3, R4}}) where {R1, R2, R3, R4} =
get(plotattributes, :seriestype, :path) == :ohlc ? OHLC[OHLC(t...) for t in xyuv] :
unzip(xyuv)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -584,9 +618,11 @@ end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
splittable_kw(key, val, lengthGroup) = false splittable_kw(key, val, lengthGroup) = false
splittable_kw(key, val::AbstractArray, lengthGroup) = !(key in (:group, :color_palette)) && length(axes(val,1)) == lengthGroup splittable_kw(key, val::AbstractArray, lengthGroup) =
!(key in (:group, :color_palette)) && length(axes(val, 1)) == lengthGroup
splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup)) splittable_kw(key, val::Tuple, lengthGroup) = all(splittable_kw.(key, val, lengthGroup))
splittable_kw(key, val::SeriesAnnotations, lengthGroup) = splittable_kw(key, val.strs, lengthGroup) splittable_kw(key, val::SeriesAnnotations, lengthGroup) =
splittable_kw(key, val.strs, lengthGroup)
split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val) - 1)...] split_kw(key, val::AbstractArray, indices) = val[indices, fill(Colon(), ndims(val) - 1)...]
split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val) split_kw(key, val::Tuple, indices) = Tuple(split_kw(key, v, indices) for v in val)
@ -596,7 +632,11 @@ function split_kw(key, val::SeriesAnnotations, indices)
end end
function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1]) function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
y_mat = Array{promote_type(eltype(y), typeof(def_val))}(undef, length(keys(x_ind)), length(groupby.groupLabels)) y_mat = Array{promote_type(eltype(y), typeof(def_val))}(
undef,
length(keys(x_ind)),
length(groupby.groupLabels),
)
fill!(y_mat, def_val) fill!(y_mat, def_val)
for i in eachindex(groupby.groupLabels) for i in eachindex(groupby.groupLabels)
xi = x[groupby.groupIds[i]] xi = x[groupby.groupIds[i]]
@ -606,7 +646,8 @@ function groupedvec2mat(x_ind, x, y::AbstractArray, groupby, def_val = y[1])
return y_mat return y_mat
end end
groupedvec2mat(x_ind, x, y::Tuple, groupby) = Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y) groupedvec2mat(x_ind, x, y::Tuple, groupby) =
Tuple(groupedvec2mat(x_ind, x, v, groupby) for v in y)
group_as_matrix(t) = false group_as_matrix(t) = false
@ -646,6 +687,9 @@ group_as_matrix(t) = false
end end
end end
label --> reshape(groupby.groupLabels, 1, :) label --> reshape(groupby.groupLabels, 1, :)
typeof(g)((x_u, (groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)...)) typeof(g)((
x_u,
(groupedvec2mat(x_ind, x, arg, groupby, NaN) for arg in last_args)...,
))
end end
end end

View File

@ -838,7 +838,7 @@ end
function attr!(series::Series; kw...) function attr!(series::Series; kw...)
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocess_attributes!(plotattributes)
for (k,v) in plotattributes for (k,v) in plotattributes
if haskey(_series_defaults, k) if haskey(_series_defaults, k)
series[k] = v series[k] = v
@ -852,7 +852,7 @@ end
function attr!(sp::Subplot; kw...) function attr!(sp::Subplot; kw...)
plotattributes = KW(kw) plotattributes = KW(kw)
preprocessArgs!(plotattributes) preprocess_attributes!(plotattributes)
for (k,v) in plotattributes for (k,v) in plotattributes
if haskey(_subplot_defaults, k) if haskey(_subplot_defaults, k)
sp[k] = v sp[k] = v