Skip to content

Commit

Permalink
scales dict (#1468)
Browse files Browse the repository at this point in the history
* Scale some aesthetics by mapping not default
  • Loading branch information
Mattriks authored Aug 8, 2020
1 parent 81783d5 commit d507b20
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 87 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Each release typically has a number of minor bug fixes beyond what is listed her

# Version 1.x

* Better define what scales are included in the (internal) `scales` dict (#1468)
* Support one-length aesthetics for `Geom.segment` (#1465)
* Support one-length aesthetics e.g. `color=[colorant"red"]` for `Geom.line` (#1459)

Expand Down
6 changes: 3 additions & 3 deletions docs/src/gallery/geometries.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ p3 = plot(z=volcano, x=collect(0.0:10:860.0), y=collect(0.0:10:600.0),
Geom.contour(levels=2))
Mv = volcano[1:4:end, 1:4:end]
Dv = vcat([DataFrame(x=[1:size(Mv,1);], y=j, z=Mv[:,j]) for j in 1:size(Mv,2)]...)
p4 = plot(Dv, x=:x, y=:y, z=:z, color=:z,
p4 = plot(Dv, x=:x, y=:y, z=:z,
Coord.cartesian(xmin=1, xmax=22, ymin=1, ymax=16),
Geom.point, Geom.contour(levels=10),
style(line_width=0.5mm, point_size=0.2mm) )
layer(Geom.point, color=:z), Geom.contour(levels=10),
Theme(line_width=0.5mm, point_size=1pt) )
gridstack([p1 p2; p3 p4])
```

Expand Down
109 changes: 46 additions & 63 deletions src/Gadfly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -419,17 +419,19 @@ function render_prepare(plot::Plot)
# plot layers. This is the only way scales, etc, can be consistently
# applied.
subplot_datas = Data[]
local geom_aes::Vector{Symbol}
for (layer, layer_data) in zip(plot.layers, datas)
if isa(layer.geom, Geom.SubplotGeometry)
for subplot_layer in layers(layer.geom)
subplot_data = Data()
if subplot_layer.data_source === nothing
subplot_layer.data_source = layer.data_source
end

subplot_layer.data_source===nothing && (subplot_layer.data_source = layer.data_source)

if isempty(subplot_layer.mapping)
subplot_layer.mapping = layer.mapping
end
geom_aes = vcat(element_aesthetics(subplot_layer.geom), [:xgroup,:ygroup], input_aesthetics(default_statistic(subplot_layer.geom)))
!isempty(subplot_layer.statistics) && append!(geom_aes, input_aesthetics(subplot_layer.statistics[1]))
geom_aesthetics = intersect(geom_aes, plot_aesthetics)
geom_dict = filter(x->in(x.first, geom_aesthetics), plot.mapping)
subplot_layer.mapping = merge(geom_dict, subplot_layer.mapping)

evalmapping!(subplot_layer.mapping, subplot_layer.data_source, subplot_data)
push!(subplot_datas, subplot_data)
Expand All @@ -451,8 +453,12 @@ function render_prepare(plot::Plot)
# Add default statistics for geometries.
layer_stats = Array{Vector{StatisticElement}}(undef, length(plot.layers))
for (i, layer) in enumerate(plot.layers)
layer_stats[i] = isempty(layer.statistics) ? ( isa(layer.geom, Geom.SubplotGeometry) ?
default_statistic(layer.geom) : [default_statistic(layer.geom)] ) : layer.statistics
if isa(layer.geom, Geom.SubplotGeometry)
layer_stats[i] = [isempty(subplot_layer.statistics) ? default_statistic(subplot_layer.geom) : subplot_layer.statistics[1]
for subplot_layer in layers(layer.geom)]
else
layer_stats[i] = isempty(layer.statistics) ? [default_statistic(layer.geom)] : layer.statistics
end
end

# auto-enumeration: add Stat.x/y_enumerate when x and y is needed but only
Expand Down Expand Up @@ -485,8 +491,15 @@ function render_prepare(plot::Plot)
end

mapped_aesthetics = Set(keys(plot.mapping))
facet_plot = false
for layer in plot.layers
union!(mapped_aesthetics, keys(layer.mapping))
if isa(layer.geom, Geom.SubplotGeometry)
facet_plot = true
for subplot_layer in layers(layer.geom)
union!(mapped_aesthetics, keys(subplot_layer.mapping))
end
end
end

defined_unused_aesthetics = setdiff(mapped_aesthetics, used_aesthetics)
Expand Down Expand Up @@ -517,30 +530,26 @@ function render_prepare(plot::Plot)
map(s->(s, _theme(l, plot)), collect(stats))
end

for element in Iterators.flatten(([(s, plot.theme) for s in plot.statistics],
# sbm_aesthetics: are scaled by mapping below, not by default_scales
sbm_aesthetics = intersect(mapped_aesthetics, Set([:color]))
# Aesthetics scaled by default_scales
elements = Iterators.flatten(([(s, plot.theme) for s in plot.statistics],
[(l.geom, _theme(plot, l)) for l in plot.layers],
layer_stats_with_theme...))

scalev = reduce(vcat, [default_scales(element...) for element in elements])
scale_aes = [intersect(element_aesthetics(scale), unscaled_aesthetics) for scale in scalev]
default_scale_dict = Dict(var=>scale for (vars, scale) in zip(scale_aes, scalev)
for var in vars if !in(var, sbm_aesthetics))
merge!(scales, default_scale_dict)
setdiff!(unscaled_aesthetics, keys(scales))

for scale in default_scales(element...)
# Use the statistics default scale only when it covers some
# aesthetic that is not already scaled.
scale_aes = Set(element_aesthetics(scale))
if !isempty(intersect(scale_aes, unscaled_aesthetics))
for var in scale_aes
scales[var] = scale
end
setdiff!(unscaled_aesthetics, scale_aes)
end
end
end

# Assign scales to mapped aesthetics first.
for var in unscaled_aesthetics
in(var, mapped_aesthetics) || continue
# Now assign scales to Aesthetics based on mapping
for var in intersect(unscaled_aesthetics, mapped_aesthetics)

var_data = getfield(plot.data, var)
if var_data == nothing
for data in datas
for data in vcat(datas, subplot_datas)
var_layer_data = getfield(data, var)
if var_layer_data != nothing
var_data = var_layer_data
Expand All @@ -561,25 +570,7 @@ function render_prepare(plot::Plot)
end
end

for var in unscaled_aesthetics
(haskey(plot.mapping, var) || haskey(scales, var)) && continue

t = :categorical
for data in Iterators.flatten((datas, subplot_datas))
val = getfield(data, var)
if val != nothing && val != :categorical
t = classify_data(val)
end
end

if scale_exists(t, var)
scale = get_scale(t, var, plot.theme)
scale_aes = Set(element_aesthetics(scale))
for var in scale_aes
scales[var] = scale
end
end
end

# Avoid clobbering user-defined guides with default guides (e.g.
# in the case of labels.)
Expand All @@ -595,14 +586,6 @@ function render_prepare(plot::Plot)
end

# Default guides and statistics
facet_plot = true
for layer in plot.layers
if typeof(layer.geom) != Geom.subplot_grid
facet_plot = false
break
end
end

if !facet_plot
in(Guide.PanelBackground, explicit_guide_types) || push!(guides, Guide.background())
in(Guide.QuestionMark, explicit_guide_types) || push!(guides, Guide.questionmark())
Expand Down Expand Up @@ -713,15 +696,17 @@ function render_prepare(plot::Plot)
end
end

# Scale.color_none maybe deprecated, this conditional can be removed then
haskey(scales, :color) && isa(scales[:color], Scale.color_none) && (supress_colorkey = true)

if supress_colorkey
deleteat!(keytypes, 1)
deleteat!(keyvars, 1)
end

if !supress_keys
for (KT, kv) in zip(keytypes, keyvars)
fflag = !all([getfield(aes, kv)==nothing for aes in [plot_aes, layer_aess...]])
fflag && !in(KT, explicit_guide_types) && push!(guides, KT())
haskey(scales, kv) && !in(KT, explicit_guide_types) && push!(guides, KT())
end
end

Expand Down Expand Up @@ -816,17 +801,16 @@ function render_prepared(plot::Plot,
# IV. Geometries
themes = Theme[layer.theme === nothing ? plot.theme : layer.theme
for layer in plot.layers]
zips = trim_zip(plot.layers, layer_aess,
layer_subplot_aess,
layer_subplot_datas,
themes)
zips = trim_zip(plot.layers, layer_aess, layer_subplot_aess, layer_subplot_datas, themes)

compose!(plot_context,
[compose(context(order=layer.order), render(layer.geom, theme, aes,
subplot_aes, subplot_data,
scales))
[compose(context(order=layer.order),
render(layer.geom, theme, aes, subplot_aes, subplot_data,scales))
for (layer, aes, subplot_aes, subplot_data, theme) in zips]...)

facet_plot = any(isa.([layer.geom for layer in plot.layers], Geom.subplot_grid))
facet_plot && (plot_aes = Gadfly.concat(layer_aess...))

# V. Guides
guide_contexts = Any[]
for guide in guides
Expand All @@ -836,8 +820,7 @@ function render_prepared(plot::Plot,
end
end

tbl = Guide.layout_guides(plot_context, coord,
plot.theme, guide_contexts...)
tbl = Guide.layout_guides(plot_context, coord, plot.theme, guide_contexts...)
if table_only
return tbl
end
Expand Down
17 changes: 1 addition & 16 deletions src/geom/subplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,6 @@ function render(geom::SubplotGrid, theme::Gadfly.Theme,
subplot_layer_datas::Vector{Gadfly.Data},
scales::Dict{Symbol, Gadfly.ScaleElement})

# inherit aesthetics from the plot when needed but not provided
for (layer, layer_aes) in zip(geom.layers, subplot_layer_aess)
inherited_aes = element_aesthetics(layer.geom)
push!(inherited_aes, :xgroup, :ygroup)
for var in inherited_aes
if getfield(layer_aes, var) === nothing
setfield!(layer_aes, var, getfield(superplot_aes, var))
end
end
end
# z
for (layer_data, layer_aes) in zip(subplot_layer_datas, subplot_layer_aess)
z = getfield(layer_data, :z)
(z != nothing) && setfield!(layer_aes, :z, z)
end

# work out the grid size
m = 1
n = 1
Expand Down Expand Up @@ -192,6 +176,7 @@ function render(geom::SubplotGrid, theme::Gadfly.Theme,
geom_aes = Gadfly.concat([layer_aes_grid[k][i,j]
for i in 1:n, j in 1:m, k in 1:length(geom.layers)]...)
geom_stats = Gadfly.StatisticElement[]
Gadfly.inherit!(superplot_aes, geom_aes)

has_stat_xticks = false
has_stat_yticks = false
Expand Down
13 changes: 9 additions & 4 deletions src/statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,8 @@ FunctionStatistic(; num_samples=250) = FunctionStatistic(num_samples)

input_aesthetics(::FunctionStatistic) = [:y, :xmin, :xmax]
output_aesthetics(::FunctionStatistic) = [:x, :y, :group]
default_scales(::FunctionStatistic) = [Gadfly.Scale.x_continuous(), Gadfly.Scale.y_continuous()]
default_scales(::FunctionStatistic, t::Gadfly.Theme=Gadfly.current_theme()) =
[Scale.x_continuous(), Scale.y_continuous(), t.discrete_color_scale]

"""
Stat.func[(; num_samples=250)]
Expand Down Expand Up @@ -1422,7 +1423,7 @@ function apply_statistic(stat::FunctionStatistic,
groups[1+(i-1)*stat.num_samples:i*stat.num_samples] .= i
end
aes.group = discretize_make_ia(groups)
elseif length(aes.y) > 1 && haskey(scales, :color)
elseif length(aes.y) > 1
data = Gadfly.Data()
data.color = Array{AbstractString}(undef, length(aes.y) * stat.num_samples)
groups = Array{Union{Missing,Int}}(undef, length(aes.y) * stat.num_samples)
Expand All @@ -1433,6 +1434,8 @@ function apply_statistic(stat::FunctionStatistic,
end
Scale.apply_scale(scales[:color], [aes], data)
aes.group = discretize_make_ia(groups)
else
aes.color_key_colors = Dict()
end

data = Gadfly.Data()
Expand Down Expand Up @@ -1532,8 +1535,10 @@ function apply_statistic(stat::ContourStatistic,
end

aes.group = groups
color_scale = get(scales, :color, Gadfly.Scale.color_continuous_gradient())
Scale.apply_scale(color_scale, [aes], Gadfly.Data(color=levels))
if aes.color===nothing
color_scale = get(scales, :color, Scale.color_continuous_gradient())
Scale.apply_scale(color_scale, [aes], Gadfly.Data(color=levels))
end
x_scale = scales[:x].trans.f==identity ? scales[:x] : Scale.x_continuous()
y_scale = scales[:y].trans.f==identity ? scales[:y] : Scale.y_continuous()
Scale.apply_scale(x_scale, [aes], Gadfly.Data(x=contour_xs))
Expand Down
2 changes: 1 addition & 1 deletion test/testscripts/contour_dataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ a = expand_grid(6.0:10, 1.0:4, 3)
D= DataFrame(x= a[:,1], y=a[:,2], z=a[:,3].*a[:,1].*a[:,2], g = string.(floor.(Int, a[:,3])) )
coord = Coord.cartesian(xmin=6, xmax=10, ymin=1, ymax=4)

plot(D, xgroup=:g, x=:x, y=:y, color=:z,
plot(D, xgroup=:g, x=:x, y=:y,
Geom.subplot_grid(coord, layer(z=:z, Geom.contour(levels=7))),
Scale.color_continuous(minvalue=0, maxvalue=120)
)

0 comments on commit d507b20

Please sign in to comment.