Skip to content

Commit

Permalink
adds PlottableData struct, introduces the idea of transform functions…
Browse files Browse the repository at this point in the history
… to eventually support custom scales
  • Loading branch information
rdboyes committed Mar 26, 2024
1 parent f6659f1 commit 97f6af9
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 24 deletions.
9 changes: 5 additions & 4 deletions src/draw.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ function Makie.SpecApi.Axis(plot::GGPlot)
# inherit any aes specified at the ggplot level
aes_dict = merge(plot.default_aes, geom.aes)

# check to make sure all required aesthetics are available
#check_aes(geom.required_aes, aes_dict, geom.args["geom_name"])

# apply function if required to edit the aes/args/data
aes_dict, args_dict, required_aes, plot_data =
geom.aes_function(aes_dict, geom.args, geom.required_aes, plot_data)
Expand Down Expand Up @@ -130,7 +127,11 @@ function Makie.SpecApi.Axis(plot::GGPlot)
labels = levels(cat_array)
axis_options[Symbol(req_aes * "ticks")] = (1:length(labels), labels)
else
column_data = plot_data[!, aes_dict[req_aes]]
if plot_data isa DataFrame
column_data = plot_data[!, aes_dict[req_aes]]
else
column_data = plot_data[aes_dict[req_aes]]
end
end
push!(visual_args_list, column_data)
end
Expand Down
6 changes: 4 additions & 2 deletions src/geom.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ function build_geom(
args_dict,
required_aes,
spec_api_function,
aes_function;
aes_function,
column_transformations;
special_aes = Dict())

if haskey(args_dict, "data")
Expand All @@ -28,6 +29,7 @@ function build_geom(
plot_data,
spec_api_function,
Dict(),
aes_function
aes_function,
column_transformations
)
end
20 changes: 11 additions & 9 deletions src/geoms/geom_contour.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
geom_tile = geom_template("geom_tile", ["x", "y", "z"], :Heatmap)
geom_contour = geom_template("geom_contour", ["x", "y"], :Contour; aes_function = stat_density_2d)

function stat_density_2d(aes_dict::Dict{String, Symbol},
args_dict::Dict{String, Any}, required_aes::Vector{String}, plot_data::DataFrame)
args_dict, required_aes::Vector{String}, plot_data::DataFrame)

x = plot_data[!, aes_dict["x"]]
y = plot_data[!, aes_dict["y"]]

k = kde((penguins.bill_length_mm, penguins.bill_depth_mm))
k = kde((x, y))

required_aes = ["z"]

return_data = DataFrame(
"z" => k.density
)
aes_dict["z"] = :z
delete!(aes_dict, "x")
delete!(aes_dict, "y")

return_data = Dict(:z => k.density)

return (aes_dict, args_dict, required_aes, return_data)
end
end

geom_tile = geom_template("geom_tile", ["x", "y", "z"], :Heatmap)
geom_contour = geom_template("geom_contour", ["x", "y"], :Contour; aes_function = stat_density_2d)
6 changes: 4 additions & 2 deletions src/geoms/geom_errorbar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ function geom_errorbar(args...; kwargs...)
return build_geom(aes_dict, args_dict,
["x", "ymin", "ymax"], # required aesthetics
:Rangebars, # function for visual layer
do_nothing;
do_nothing,
Dict{Symbol, Function}();
special_aes = Dict("width" => "whiskerwidth"))
end

Expand All @@ -19,7 +20,8 @@ function geom_errorbarh(args...; kwargs...)
return build_geom(aes_dict, args_dict,
["y", "xmin", "xmax"], # required aesthetics
:Rangebars, # function for visual layer
do_nothing;
do_nothing,
Dict{Symbol, Function}();
special_aes = Dict("width" => "whiskerwidth"))

end
Expand Down
4 changes: 2 additions & 2 deletions src/geoms/geom_hvline.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function geom_hline(args...; kwargs...)
return build_geom(aes_dict, args_dict,
["yintercept"], # required aesthetics
:HLines,
do_nothing) # function for visual layer
do_nothing, Dict{Symbol, Function}()) # function for visual layer
end

function geom_vline(args...; kwargs...)
Expand All @@ -27,7 +27,7 @@ function geom_vline(args...; kwargs...)
return build_geom(aes_dict, args_dict,
["xintercept"], # required aesthetics
:VLines,
do_nothing) # function for visual layer
do_nothing, Dict{Symbol, Function}()) # function for visual layer
end

function geom_hline(plot::GGPlot, args...; kwargs...)
Expand Down
9 changes: 6 additions & 3 deletions src/geoms/geom_smooth.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@ function geom_smooth(args...; kwargs...)
args_dict,
["x", "y"],
:Lines,
stat_linear),
stat_linear,
Dict{Symbol, Function}()),
build_geom(aes_dict,
args_dict,
["x", "lower", "upper"],
:Band,
stat_linear)]
stat_linear,
Dict{Symbol, Function}())]
end
end

return build_geom(aes_dict,
args_dict,
["x", "y"],
:Lines,
stat_loess)
stat_loess,
Dict{Symbol, Function}())
end

function stat_loess(aes_dict::Dict{String, Symbol},
Expand Down
6 changes: 4 additions & 2 deletions src/geoms/geom_template.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
function geom_template(name::AbstractString,
required_aes::AbstractArray,
spec_api_function::Symbol;
aes_function::Function = do_nothing,
aes_function::Function = do_nothing,
column_transformations::Dict{Symbol, Function} = Dict{Symbol, Function}(),
extra_args::Dict = Dict())

extract_geom_aes = make_aes_extractor(required_aes)
Expand All @@ -14,7 +15,8 @@ function geom_template(name::AbstractString,
return build_geom(aes_dict, args_dict,
required_aes,
spec_api_function,
aes_function)
aes_function,
column_transformations)
end

function geom_function(plot::GGPlot, args...; kwargs...)
Expand Down
1 change: 1 addition & 0 deletions src/structs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ struct Geom
visual::Symbol
axis_options::Dict
aes_function::Function
column_transformations::Dict
end

struct GGPlot
Expand Down
66 changes: 66 additions & 0 deletions src/transforms.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# transformation functions for aes specification
# they all should return a Dict that maps a aes symbol
# to a PlottableData struct which contains

# - position: the data
# - label_target: where should the labels go?
# - label_function: what should be done to data in "position" to display it
# - makie_function: what should be done before data goes to the Makie arg

struct PlottableData
position::Any
makie_function::Function
label_target::Union{Symbol, Nothing}
label_function::Any
end

# simplest one is as_is, which just gets the column
# exactly as it is in the DataFrame

function as_is(target::Symbol, data::DataFrame)
return Dict{Symbol, PlottableData}(
target => PlottableData(data[!, target],
identity,
Symbol(String(target) * "ticks"),
Makie.automatic
)
)
end

# categorical array handling options for String columns

function cat_inorder(target::Symbol, data::DataFrame)
cat_column = data[!, target]
cat_array = CategoricalArray(cat_column,
levels = unique(cat_column),
ordered = true)
return Dict{Symbol, PlottableData}(
target => PlottableData(
levelcode.(cat_array),
x -> levels(x)
)
)
end

function cat_inseq(target::Symbol, data::DataFrame)
cat_array = CategoricalArray(data[!, target])
return Dict{Symbol, PlottableData}(
target => PlottableData(
levelcode.(cat_array),
x -> levels(x)
)
)
end

# kernel density estimation for geom_contour

function kernel_density_2d(x::Symbol, y::Symbol, data::DataFrame)

k = kde((data[!, x], data[!, y]))

return Dict{Symbol, PlottableData}(
:x => PlottableData(k.x, identity),
:y => PlottableData(k.y, identity),
:z => PlottableData(k.density, identity)
)
end

0 comments on commit 97f6af9

Please sign in to comment.