Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Size revamp3: ContinuousSizeScale #1386

Merged
merged 1 commit into from
Jan 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Each release typically has a number of minor bug fixes beyond what is listed her

# Version 1.x

* Add `Scale.size_radius` and `Scale.size_area` (#1386)
* Add `Guide.sizekey` (#1379)
* Add `Scale.size_discrete2` (#1376)
* Add `Geom.blank` (#1345)
Expand Down
22 changes: 22 additions & 0 deletions docs/src/gallery/scales.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,28 @@ tipsm = by(tips, [:Day, :Sex], :TotalBill=>mean, :Tip=>mean)
)
```

## [`Scale.size_radius`](@ref), [`Scale.size_area`](@ref)

```@example
using Gadfly, RDatasets
set_default_plot_size(21cm, 8cm)
aq = dropmissing(dataset("datasets","airquality"))
aq = filter(x-> 7≤x.Month≤9, aq)

sizemap(p::Float64; min=0.75mm, max=3mm) = min + p*(max-min)
labels = ["July", "August", "September"]
coord = Coord.cartesian(xmin=0, xmax=32, ymin=0, ymax=160)
plot(aq, x=:Day, y=:Ozone, color=:Month, size=:Wind,
coord, Scale.color_discrete,
Guide.colorkey(title="", labels=labels, pos=[2,240]),
Scale.size_area(sizemap, minvalue=4, maxvalue=16),
Guide.sizekey(title="Wind (mph)"), Guide.xlabel("Day of Month"),
Theme(key_swatch_shape=Shape.circle, key_swatch_color="gray",
discrete_highlight_color=identity, alphas=[0.5])
)
```


## [`Scale.size_discrete2`](@ref)

```@example
Expand Down
24 changes: 12 additions & 12 deletions docs/src/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,18 @@ colorspaces (LUV/LCHuv or LAB/LCHab), though it supports RGB, HSV, HLS, XYZ, and
converts arbitrarily between these. Of course, CSS/X11 named colors work too:
"old lace", anyone?

All aesthetics (e.g. `x`, `y`, `color`) have a Scale e.g. `Scale.x_continuous()` and some have a Guide e.g. `Guide.xticks()`. [Scales](@ref) can be continuous or discrete.
All aesthetics (e.g. `x`, `y`, `color`) have a Scale e.g. `Scale.x_continuous()` and some have a Guide e.g. `Guide.xticks()`. [Scales](@ref) can be continuous or discrete. Some Scales also have a corresponding palette in `Theme()`.

## Continuous Scales

| Aesthetic | Scale. | Guide. |
|-----------|------------------|-------|
| `x` | `x_continuous` | `xticks` |
| `y` | `y_continuous` | `yticks` |
| `color` | `color_continuous` | `colorkey` |
| `size` | `size_continuous` | sizekey (tbd) |
| `alpha` | `alpha_continuous` | alphakey (tbd) |
| Aesthetic | Scale. | Guide. | Theme palette |
|-----------|------------------|-------|----------|
| `x` | `x_continuous` | `xticks` | |
| `y` | `y_continuous` | `yticks` | |
| `color` | `color_continuous` | `colorkey` | (tbd) |
| `size` | `size_continuous` | --- | `point_size_min`, `point_size_max` |
| | `size_radius` | `sizekey` | `continuous_sizemap` |
| `alpha` | `alpha_continuous` | alphakey (tbd) | |

e.g. `Scale.x_continuous(format= , minvalue= , maxvalue= )`\
`format` can be: `:plain`, `:scientific`, `:engineering`, or `:auto`.
Expand All @@ -188,7 +189,8 @@ p2 = plot(mammals, x=:Body, y=:Brain, label=:Mammal, Geom.point, Geom.label,
hstack(p1, p2)
```

Scale transformations include: `_sqrt`, `_log`, `_log2`, `_log10`, `_asinh`.
Scale transformations include: `_sqrt`, `_log`, `_log2`, `_log10`, `_asinh` for the `x`, `y`, `color` aesthetics,
and `_area` for the `size` aesthetic.

```@example 1
using Printf
Expand All @@ -206,13 +208,11 @@ hstack(p3, p4)

## Discrete Scales

For some discrete scales, there is a corresponding palette in `Theme()`.

| Aesthetic | Scale. | Guide. | Theme palette |
|-----------|------------------|-------|-----------------|
| `x` | `x_discrete` | `xticks` | |
| `y` | `y_discrete` | `yticks` | |
| `color` | `color_discrete` | `colorkey` | |
| `color` | `color_discrete` | `colorkey` | (tbd) |
| `shape` | `shape_discrete` | `shapekey` | `point_shapes` |
| `size` | `size_discrete` | --- | `point_size_min`, `point_size_max` |
| | `size_discrete2`| `sizekey` | `discrete_sizemap` |
Expand Down
4 changes: 3 additions & 1 deletion src/scale.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ using Printf
using Base.Iterators

import Gadfly: element_aesthetics, isconcrete, concrete_length, discretize_make_ia,
aes2str, valid_aesthetics
aes2str, valid_aesthetics, Maybe
import Distributions: Distribution

include("color_misc.jl")
Expand Down Expand Up @@ -159,6 +159,8 @@ end
"""
size_continuous[(; minvalue=nothing, maxvalue=nothing, labels=nothing,
format=nothing, minticks=2, maxticks=10, scalable=true)]

To be updated in Gadfly 2.0. Now try out the new [`Scale.size_radius`](@ref) and [`Scale.size_area`](@ref).
"""
const size_continuous = continuous_scale_partial([:size], identity_transform)

Expand Down
95 changes: 95 additions & 0 deletions src/scale/scales.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,98 @@ function Scale.apply_scale(scale::DiscreteSizeScale, aess::Vector{Gadfly.Aesthet
end



area_transform = ContinuousScaleTransform(a->sqrt(a/π), r->π*r*r, identity_formatter)

struct ContinuousSizeScale <: Gadfly.ScaleElement
# A function of the form f(p) where 0 ≤ p ≤ 1, that returns a Measure.
f::Function
trans::ContinuousScaleTransform
minvalue::Maybe(Compose.MeasureOrNumber)
maxvalue::Maybe(Compose.MeasureOrNumber)
format::Union{Nothing, Symbol}
end

Scale.element_aesthetics(scale::ContinuousSizeScale) = [:size]

default_continuous_sizes(p::Float64; min=0mm, max=2mm) = min + p*(max-min)


"""
Scale.size_radius(f=default_continuous_sizes; minvalue=0.0, maxvalue=nothing)

A scale for continuous sizes. Values in the `size` aesthetic are mapped either to:
- x-axis units (if `maxvalue=nothing`), or
- `Measure` units (from Measures.jl).
If `maxvalue` is specified, the continuous sizes are converted to a proportion
(between `minvalue` and `maxvalue`), and then mapped to absolute sizes using the function `f(p)` where `0≤p≤1`.
"""
size_radius(f::Function=Gadfly.current_theme().continuous_sizemap; minvalue=0.0, maxvalue=nothing) =
ContinuousSizeScale(f, identity_transform, minvalue, maxvalue, nothing)



"""
Scale.size_area(f=default_continuous_sizes; minvalue=0.0, maxvalue=nothing)

Similar to [`Scale.size_radius`](@ref), except that the values in the `size` aesthetic are
scaled to area rather than radius, before mapping to x-axis units or `Measure` units.
"""
function size_area(f::Function=Gadfly.current_theme().continuous_sizemap; minvalue=0.0, maxvalue=nothing)
(isa(minvalue, Measure) || isa(maxvalue, Measure)) &&
throw(ArgumentError("Scale.size_area maps the size variable to absolute size via the function `f`. See `?Scale.size_radius` for more info."))
return ContinuousSizeScale(f, area_transform, minvalue, maxvalue, nothing)
end



function apply_scale(scale::ContinuousSizeScale, aess::Vector{Gadfly.Aesthetics}, datas::Gadfly.Data...)

sdata = reduce(vcat, [d.size for d in datas if d.size≠nothing])

showvals = length(sdata)>1
dmax = maximum(skipmissing(sdata))

strict_span = false
(smin, smax) =
if scale.maxvalue===nothing
promote(scale.minvalue, dmax)
else
strict_span = true
promote(scale.minvalue, scale.maxvalue)
end
ticks = Gadfly.optimize_ticks(smin, smax, strict_span=strict_span)[1]
Δ = scale.trans.f(ticks[end])-scale.trans.f(ticks[1])
labels = scale.trans.label(ticks)

# Transform ticks e.g. to areas/porportions/sizes
keyvals = if scale.maxvalue===nothing
showvals = false
scale.trans.f.(ticks.-smin)
else
p = (scale.trans.f.(ticks) .- scale.trans.f(ticks[1]))./Δ
scale.f.(p)
end

labeldict = Dict(k=>v for (k,v) in zip(keyvals, labels))
key_vals= OrderedDict(s=>i for (i,s) in enumerate(keyvals))
labeler(xs) = [labeldict[x] for x in xs]

for (aes, data) in zip(aess, datas)
data.size===nothing && continue

ds = if scale.maxvalue === nothing
scale.trans.f.(data.size.-smin)
else
p = (scale.trans.f.(data.size).-scale.trans.f(ticks[1]))./Δ
scale.f.(p)
end
aes.size = ds
showvals && (aes.size_key_vals = key_vals)
aes.size_label = labeler
end
end




3 changes: 3 additions & 0 deletions src/theme.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ $(FIELDS)
"The function `f` in [`Scale.size_discrete2`](@ref).",
discrete_sizemap, Function, Scale.default_discrete_sizes

"The function `f` in [`Scale.size_radius`](@ref) and [`Scale.size_area`](@ref).",
continuous_sizemap, Function, Scale.default_continuous_sizes

"Shapes of points in the point geometry. (Function in circle, square, diamond, cross, xcross, utriangle, dtriangle, star1, star2, hexagon, octagon, hline, vline, ltriangle, rtriangle)",
point_shapes, Vector{Function}, [Shape.circle, Shape.square, Shape.diamond, Shape.cross, Shape.xcross,
Shape.utriangle, Shape.dtriangle, Shape.star1, Shape.star2,
Expand Down
13 changes: 11 additions & 2 deletions test/testscripts/point_size_numerical.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
using Gadfly

set_default_plot_size(6inch, 6inch)
set_default_plot_size(9inch, 3inch)

plot(x=rand(100), y=rand(100), size=rand(1:8, 100)./100, Geom.point)
y, size = [0.55, 0.7, 0.9, 0.99, 0.9], [0.4, 0.5, 0.6, 0.68, 0.63]
juliaclrs = Gadfly.parse_colorant(["forestgreen", "brown3", "mediumorchid"])
theme = Theme(key_swatch_shape=Shape.circle, alphas=[0.1], discrete_highlight_color=identity)

p1 = plot(x=[2,1.13,2.87], y=[3,1.5,1.5], size=[0.75], alpha=[0.8],
color=juliaclrs, Coord.cartesian(fixed=true))
p2 = plot(x=1:5, y=y, size=10^5*size, Scale.size_radius(maxvalue=10^5), theme)
p3 = plot(x=1:5, y=y, size=10^5*size, Scale.size_area(maxvalue=10^5), theme)

hstack(p1, p2, p3)