From 58da0919f19b6fb0634b731a8c81422b41012035 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sat, 4 May 2024 17:42:20 -0400 Subject: [PATCH 1/9] grouping aes field added to geoms --- src/geom.jl | 6 ++++-- src/geoms/geom_template.jl | 6 ++++-- src/structs.jl | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/geom.jl b/src/geom.jl index 5f23bcb..100a476 100644 --- a/src/geom.jl +++ b/src/geom.jl @@ -5,7 +5,8 @@ function build_geom( spec_api_function, aes_function, column_transformations; - special_aes = Dict()) + special_aes = Dict(), + grouping_aes = Symbol[]) if haskey(args_dict, "data") if args_dict["data"] isa DataFrame @@ -30,6 +31,7 @@ function build_geom( spec_api_function, Dict(), aes_function, - column_transformations + column_transformations, + grouping_aes ) end \ No newline at end of file diff --git a/src/geoms/geom_template.jl b/src/geoms/geom_template.jl index de339a1..90cd01f 100644 --- a/src/geoms/geom_template.jl +++ b/src/geoms/geom_template.jl @@ -3,7 +3,8 @@ function geom_template(name::AbstractString, spec_api_function::Symbol; aes_function::Function = do_nothing, column_transformations::Dict{Symbol, Pair{Vector{Symbol}, AesTransform}} = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}(), - extra_args::Dict = Dict()) + extra_args::Dict = Dict(), + grouping_aes::Vector{Symbol} = Symbol[]) extract_geom_aes = make_aes_extractor(required_aes) @@ -16,7 +17,8 @@ function geom_template(name::AbstractString, required_aes, spec_api_function, aes_function, - merge(transforms, column_transformations)) + merge(transforms, column_transformations); + grouping_aes = grouping_aes) end function geom_function(plot::GGPlot, args...; kwargs...) diff --git a/src/structs.jl b/src/structs.jl index fb78ea8..87ae385 100644 --- a/src/structs.jl +++ b/src/structs.jl @@ -8,6 +8,7 @@ struct Geom axis_options::Dict aes_function::Function column_transformations::Dict + grouping_aes::Vector{Symbol} end struct GGPlot From 568883da38cc262c724796c5a36f0b97807ad7b9 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sat, 4 May 2024 17:43:16 -0400 Subject: [PATCH 2/9] add colour/color as grouping aes for geom_line --- src/geoms/geom_path.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/geoms/geom_path.jl b/src/geoms/geom_path.jl index 0a69450..f512b0a 100644 --- a/src/geoms/geom_path.jl +++ b/src/geoms/geom_path.jl @@ -40,7 +40,8 @@ geom_line = geom_template("geom_line", ["x", "y"], :Lines; column_transformations = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}( :y => [:y, :x]=>sort_by, :x => [:x, :x]=>sort_by - ) + ), + grouping_aes = [:color, :colour] ) From cc8fe2a1b05221d645e00afe144abeb49b7f950e Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sun, 5 May 2024 10:13:38 -0400 Subject: [PATCH 3/9] modify draw to split PlotSpecs by a grouping variable --- src/draw.jl | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/draw.jl b/src/draw.jl index 7de8008..cef2a56 100644 --- a/src/draw.jl +++ b/src/draw.jl @@ -71,14 +71,33 @@ function Makie.SpecApi.Axis(plot::GGPlot) end end - required_aes_data = [p.makie_function(p.raw) for p in [given_aes[a] for a in Symbol.(required_aes)]] - optional_aes_data = [a => p.makie_function(p.raw) for (a, p) in given_aes if !(String(a) in required_aes)] + if length(intersect(keys(given_aes), geom.grouping_aes)) == 0 + # if there are no grouping_aes given, we only need one PlotSpec + required_aes_data = [p.makie_function(p.raw) for p in [given_aes[a] for a in Symbol.(required_aes)]] + optional_aes_data = [a => p.makie_function(p.raw) for (a, p) in given_aes if !(String(a) in required_aes)] - args = Tuple([geom.visual, required_aes_data...]) - kwargs = merge(args_dict_makie, Dict(optional_aes_data)) + args = Tuple([geom.visual, required_aes_data...]) + kwargs = merge(args_dict_makie, Dict(optional_aes_data)) - # push completed PlotSpec (type, args, and kwargs) to the list of plots - push!(plot_list, Makie.PlotSpec(args...; kwargs...)) + # push completed PlotSpec (type, args, and kwargs) to the list of plots + push!(plot_list, Makie.PlotSpec(args...; kwargs...)) + else + # if there is a aes in the grouping_aes list given, we will need multiple PlotSpecs + # make a list of modified given_aes objects which only include the points from their subsets + subgroup_given_aes = subgroup_split(given_aes, plot_data[!, intersect(keys(given_aes), geom.grouping_aes)]) + + # push each one to the overall plot_list + for sub in subgroup_given_aes + required_aes_data = [p.makie_function(p.raw) for p in [sub[a] for a in Symbol.(required_aes)]] + optional_aes_data = [a => p.makie_function(p.raw) for (a, p) in sub if !(String(a) in required_aes)] + + args = Tuple([geom.visual, required_aes_data...]) + kwargs = merge(args_dict_makie, Dict(optional_aes_data)) + + # push completed PlotSpec (type, args, and kwargs) to the list of plots + push!(plot_list, Makie.PlotSpec(args...; kwargs...)) + end + end end # rename and correct types on all axis options From 95556c0e2b46c4fba4c128749211da41939a140e Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sun, 5 May 2024 11:45:43 -0400 Subject: [PATCH 4/9] functions for subgrouping PlottableData --- src/TidierPlots.jl | 1 + src/grouping.jl | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/grouping.jl diff --git a/src/TidierPlots.jl b/src/TidierPlots.jl index 8c9c070..391831f 100644 --- a/src/TidierPlots.jl +++ b/src/TidierPlots.jl @@ -40,6 +40,7 @@ include("extract_aes.jl") include("geom.jl") include("ggplot.jl") include("ggsave.jl") +include("grouping.jl") include("labs.jl") include("label_functions.jl") include("legend.jl") diff --git a/src/grouping.jl b/src/grouping.jl new file mode 100644 index 0000000..8272771 --- /dev/null +++ b/src/grouping.jl @@ -0,0 +1,19 @@ +function subgroup_split(given_aes, grouping_columns) + grouping_columns.index = 1:nrow(grouping_columns) + group_index_list = [df.index for df in groupby(grouping_columns, Not(:index))] + return [subset_aes(given_aes, index) for index in group_index_list] +end + +function subset_aes(given_aes, index) + subset_given_aes_dict = Dict{Symbol, PlottableData}() + for (key, value) in given_aes + subset_given_aes_item = PlottableData( + value.raw[index, :], + value.makie_function, + value.label_target, + value.label_function + ) + push!(subset_given_aes_dict, key => subset_given_aes_item) + end + return subset_given_aes_dict +end \ No newline at end of file From 66758e4948ff9305a2b657911cbfc1ea8a57f219 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sun, 5 May 2024 12:34:43 -0400 Subject: [PATCH 5/9] color needs to be simplified down --- src/draw.jl | 5 ++++- src/geoms/geom_density.jl | 2 +- src/geoms/geom_path.jl | 3 ++- src/grouping.jl | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/draw.jl b/src/draw.jl index cef2a56..bfd9cf7 100644 --- a/src/draw.jl +++ b/src/draw.jl @@ -84,7 +84,8 @@ function Makie.SpecApi.Axis(plot::GGPlot) else # if there is a aes in the grouping_aes list given, we will need multiple PlotSpecs # make a list of modified given_aes objects which only include the points from their subsets - subgroup_given_aes = subgroup_split(given_aes, plot_data[!, intersect(keys(given_aes), geom.grouping_aes)]) + grouping_columns = [aes_dict_makie[a] for a in [intersect(keys(given_aes), geom.grouping_aes)...]] + subgroup_given_aes = subgroup_split(given_aes, plot_data[!, grouping_columns]) # push each one to the overall plot_list for sub in subgroup_given_aes @@ -94,6 +95,8 @@ function Makie.SpecApi.Axis(plot::GGPlot) args = Tuple([geom.visual, required_aes_data...]) kwargs = merge(args_dict_makie, Dict(optional_aes_data)) + kwargs[:color] = first(kwargs[:color]) + # push completed PlotSpec (type, args, and kwargs) to the list of plots push!(plot_list, Makie.PlotSpec(args...; kwargs...)) end diff --git a/src/geoms/geom_density.jl b/src/geoms/geom_density.jl index 8af4e0f..47d7b2b 100644 --- a/src/geoms/geom_density.jl +++ b/src/geoms/geom_density.jl @@ -37,4 +37,4 @@ ggplot(penguins, @aes(x=bill_length_mm)) + geom_density(color = (:red, 0.3), strokecolor = :red, stroke = 2) ``` """ -geom_density = geom_template("geom_density", ["x"], :Density) +geom_density = geom_template("geom_density", ["x"], :Density; grouping_aes = [:color, :colour]) diff --git a/src/geoms/geom_path.jl b/src/geoms/geom_path.jl index f512b0a..449f348 100644 --- a/src/geoms/geom_path.jl +++ b/src/geoms/geom_path.jl @@ -127,4 +127,5 @@ ggplot(penguins, @aes(x = bill_length_mm, y = bill_depth_mm)) + geom_path() ``` """ -geom_path = geom_template("geom_path", ["x", "y"], :Lines) +geom_path = geom_template("geom_path", ["x", "y"], :Lines; + grouping_aes = [:color, :colour]) diff --git a/src/grouping.jl b/src/grouping.jl index 8272771..225a955 100644 --- a/src/grouping.jl +++ b/src/grouping.jl @@ -1,6 +1,6 @@ function subgroup_split(given_aes, grouping_columns) grouping_columns.index = 1:nrow(grouping_columns) - group_index_list = [df.index for df in groupby(grouping_columns, Not(:index))] + group_index_list = Vector{Int}.([df.index for df in groupby(grouping_columns, Not(:index))]) return [subset_aes(given_aes, index) for index in group_index_list] end @@ -8,7 +8,7 @@ function subset_aes(given_aes, index) subset_given_aes_dict = Dict{Symbol, PlottableData}() for (key, value) in given_aes subset_given_aes_item = PlottableData( - value.raw[index, :], + value.raw[index], value.makie_function, value.label_target, value.label_function From 0156a65cce4d83b8b4c5549617d1281ae188d187 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sun, 5 May 2024 12:43:04 -0400 Subject: [PATCH 6/9] grouping complete --- src/draw.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/draw.jl b/src/draw.jl index bfd9cf7..e20d9e9 100644 --- a/src/draw.jl +++ b/src/draw.jl @@ -95,7 +95,10 @@ function Makie.SpecApi.Axis(plot::GGPlot) args = Tuple([geom.visual, required_aes_data...]) kwargs = merge(args_dict_makie, Dict(optional_aes_data)) - kwargs[:color] = first(kwargs[:color]) + # if we are grouping, we only need a single value rather than a vector + for aes in [intersect(keys(given_aes), geom.grouping_aes)...] + kwargs[aes] = first(kwargs[aes]) + end # push completed PlotSpec (type, args, and kwargs) to the list of plots push!(plot_list, Makie.PlotSpec(args...; kwargs...)) From de815d326ece7a54cb6053bbf5083cf83d56d3f1 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Sun, 5 May 2024 13:02:54 -0400 Subject: [PATCH 7/9] fixing geom_line? --- src/geoms/geom_path.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/geoms/geom_path.jl b/src/geoms/geom_path.jl index 449f348..9ff7dc4 100644 --- a/src/geoms/geom_path.jl +++ b/src/geoms/geom_path.jl @@ -39,7 +39,9 @@ ggplot(df, @aes(x = x, y = y)) + geom_line() geom_line = geom_template("geom_line", ["x", "y"], :Lines; column_transformations = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}( :y => [:y, :x]=>sort_by, - :x => [:x, :x]=>sort_by + :x => [:x, :x]=>sort_by, + :color => [:color, :x]=>sort_by, + :colour => [:colour, :x]=>sort_by ), grouping_aes = [:color, :colour] ) @@ -86,7 +88,9 @@ ggplot(df, @aes(x = x, y = y)) + geom_step() geom_step = geom_template("geom_step", ["x", "y"], :Stairs; column_transformations = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}( :y => [:y, :x]=>sort_by, - :x => [:x, :x]=>sort_by + :x => [:x, :x]=>sort_by, + :color => [:color, :x]=>sort_by, + :colour => [:colour, :x]=>sort_by ) ) From 8e2107001c047222690e85b2d8ce1b682c4341e3 Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Wed, 8 May 2024 17:04:53 -0400 Subject: [PATCH 8/9] change the way sorting works for geom_line and geom_step --- src/geoms/geom_path.jl | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/geoms/geom_path.jl b/src/geoms/geom_path.jl index 9ff7dc4..279233d 100644 --- a/src/geoms/geom_path.jl +++ b/src/geoms/geom_path.jl @@ -1,3 +1,13 @@ +function stat_sort_by_x(aes_dict::Dict{String, Symbol}, + args_dict::Dict{Any, Any}, required_aes::Vector{String}, plot_data::DataFrame) + + x_column = aes_dict["x"] + + perm = sortperm(data[!, x_column]) + + return (aes_dict, args_dict, required_aes, plot_data[perm, :]) +end + """ geom_line(aes(...), ...) geom_line(plot::GGPlot, aes(...), ...) @@ -36,13 +46,8 @@ df = DataFrame(x = xs, y = sin.(xs)) ggplot(df, @aes(x = x, y = y)) + geom_line() ``` """ -geom_line = geom_template("geom_line", ["x", "y"], :Lines; - column_transformations = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}( - :y => [:y, :x]=>sort_by, - :x => [:x, :x]=>sort_by, - :color => [:color, :x]=>sort_by, - :colour => [:colour, :x]=>sort_by - ), +geom_line = geom_template("geom_line", ["x", "y"], :Lines; + aes_function = stat_sort_by_x, grouping_aes = [:color, :colour] ) @@ -86,12 +91,8 @@ ggplot(df, @aes(x = x, y = y)) + geom_step() ``` """ geom_step = geom_template("geom_step", ["x", "y"], :Stairs; - column_transformations = Dict{Symbol, Pair{Vector{Symbol}, AesTransform}}( - :y => [:y, :x]=>sort_by, - :x => [:x, :x]=>sort_by, - :color => [:color, :x]=>sort_by, - :colour => [:colour, :x]=>sort_by - ) + aes_function = stat_sort_by_x, + grouping_aes = [:color, :colour] ) From 82d490e730c7d5e25eb10436408187381c945eed Mon Sep 17 00:00:00 2001 From: Randy Boyes Date: Wed, 8 May 2024 17:18:15 -0400 Subject: [PATCH 9/9] typo --- src/geoms/geom_path.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geoms/geom_path.jl b/src/geoms/geom_path.jl index 279233d..3e2511a 100644 --- a/src/geoms/geom_path.jl +++ b/src/geoms/geom_path.jl @@ -3,7 +3,7 @@ function stat_sort_by_x(aes_dict::Dict{String, Symbol}, x_column = aes_dict["x"] - perm = sortperm(data[!, x_column]) + perm = sortperm(plot_data[!, x_column]) return (aes_dict, args_dict, required_aes, plot_data[perm, :]) end