Skip to content

Commit

Permalink
Merge pull request #1386 from Mattriks/scale_size_radius
Browse files Browse the repository at this point in the history
Size revamp3: ContinuousSizeScale
  • Loading branch information
bjarthur authored Jan 26, 2020
2 parents 32c12cb + d3c7723 commit f1e5474
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 15 deletions.
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. Color values can also be specified by most names common to CSS or X11, e.g. `"oldlace"` or `"aliceblue"`. The [full list of valid color names](http://juliagraphics.github.io/Colors.jl/stable/namedcolors/) is defined in the [Colors.jl library](http://juliagraphics.github.io/Colors.jl/stable/).


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.sizenothing])

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)

0 comments on commit f1e5474

Please sign in to comment.