Skip to content

Commit

Permalink
color grouping for density2d (#1508)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mattriks authored Jan 6, 2021
1 parent d78d866 commit c60cc91
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 24 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
This is a log of major changes in Gadfly between releases. It is not exhaustive.
Each release typically has a number of minor bug fixes beyond what is listed here.

# Version 1.x


* Enable `color` grouping for `Geom.density2d` (#1508)

# Version 1.3.1

* Better define what scales are included in the (internal) `scales` dict (#1468)
Expand Down
30 changes: 16 additions & 14 deletions docs/src/gallery/geometries.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,21 +177,24 @@ plot(layer(x=xs, Geom.density, color=["auto"]),

```@example
using Gadfly, Distributions, RDatasets
set_default_plot_size(21cm, 8cm)
iris = dataset("datasets", "iris")
set_default_plot_size(21cm, 18cm)
geyser, iris = dataset("datasets", "faithful"), dataset("datasets", "iris")
geyser.g = geyser.Eruptions.>3
X = rand(Rayleigh(2), 1000,2)
levelf(x) = maximum(x)*0.5.^collect(1:2:8)
p1 = plot(x=X[:,1], y=X[:,2], Geom.density2d(levels=levelf),
Geom.point, Scale.color_continuous(colormap=c->colorant"red"),
Theme(key_position=:none))
p1 = plot(x=X[:,1], y=X[:,2], layer(Geom.density2d(levels=levelf), color=[colorant"red"],
linestyle=[:solid]), Geom.point)
p2 = plot(geyser, x=:Eruptions, y=:Waiting, color=:g, Geom.density2d(levels=4),
Geom.point, alpha=[0.4])
p3 = plot(iris, x=:SepalLength, y=:SepalWidth, color=:Species, Geom.density2d(levels=4),
Geom.point, alpha=[0.8])
cs = repeat(Scale.default_discrete_colors(3), inner=50)
p2 = plot(iris, x=:SepalLength, y=:SepalWidth,
layer(x=:SepalLength, y=:SepalWidth, color=cs),
p4 = plot(iris, x=:SepalLength, y=:SepalWidth, layer(color=cs, Geom.point),
layer(Geom.density2d(levels=[0.1:0.1:0.4;]), order=1),
Scale.color_continuous, Guide.colorkey(title=""),
Guide.manual_color_key("Iris", unique(iris.Species)),
Theme(point_size=3pt, line_width=1.5pt))
hstack(p1, p2)
Scale.color_continuous, Guide.colorkey(title=""),
Theme(point_size=3pt, line_width=1.5pt),
Guide.manual_color_key("Iris", unique(iris.Species)))
gridstack([p1 p2; p3 p4])
```


Expand Down Expand Up @@ -245,9 +248,8 @@ set_default_plot_size(21cm, 8cm)
salaries = dataset("car","Salaries")
salaries.Salary /= 1000.0
salaries.Discipline = ["Discipline $(x)" for x in salaries.Discipline]
fn1(x, u=mean(x), s=std(x)) = (Salary=u, ymin=u-s, ymax=u+s,
label="$(round.(Int,u))")
df = combine(:Salary=>fn1, groupby(salaries, [:Rank, :Discipline]))
fn1(x, u=mean(x), s=std(x)) = (Salary=u, ymin=u-s, ymax=u+s, label="$(round.(Int,u))")
df = combine(groupby(salaries, [:Rank, :Discipline]), :Salary=>fn1=>AsTable)
p1 = plot(df, x=:Discipline, y=:Salary, color=:Rank,
Scale.x_discrete(levels=["Discipline A", "Discipline B"]),
Expand Down
3 changes: 2 additions & 1 deletion src/geom/line.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ density(; bandwidth::Real=-Inf) =
Geom.density2d[(; bandwidth=(-Inf,-Inf), levels=15)]
Draw a set of contours showing the density estimate of the `x` and `y`
aesthetics. This geometry is equivalent to [`Geom.line`](@ref) with
aesthetics. If grouped by `color`, then contour lines are mapped to `linestyle`.
This geometry is equivalent to [`Geom.line`](@ref) with
[`Stat.density2d`](@ref); see the latter for more information.
"""
density2d(; bandwidth::Tuple{Real,Real}=(-Inf,-Inf), levels=15) =
Expand Down
53 changes: 44 additions & 9 deletions src/statistics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -513,18 +513,53 @@ is controlled by `bandwidth`. Calls [`Stat.contour`](@ref) to compute the
const density2d = Density2DStatistic

function apply_statistic(stat::Density2DStatistic,
scales::Dict{Symbol, Gadfly.ScaleElement},
coord::Gadfly.CoordinateElement,
aes::Gadfly.Aesthetics)
Gadfly.assert_aesthetics_defined("Density2DStatistic", aes, :x, :y)
scales::Dict{Symbol, Gadfly.ScaleElement},
coord::Gadfly.CoordinateElement,
aes::Gadfly.Aesthetics)

window = (stat.bw[1] <= 0.0 ? KernelDensity.default_bandwidth(aes.x) : stat.bw[1],
stat.bw[2] <= 0.0 ? KernelDensity.default_bandwidth(aes.y) : stat.bw[2])
k = KernelDensity.kde((aes.x,aes.y), bandwidth=window, npoints=stat.n)
aes.z = k.density
aes.x = collect(k.x)
aes.y = collect(k.y)
apply_statistic(ContourStatistic(levels=stat.levels), scales, coord, aes)

Dat = [aes.x aes.y]
linestyleflag = aes.linestyle nothing
colorflag = aes.color nothing
aes_color = colorflag ? aes.color : [nothing]
aes_group = (aes.group nothing) ? aes.group : [nothing]
CT, GT = eltype(aes_color), eltype(aes_group)

groups = collect(Tuple{CT, GT}, Compose.cyclezip(aes_color, aes_group))
ugroups = unique(groups)
nugroups = length(ugroups)

K, V = Tuple{CT, GT}, Matrix{eltype(Dat)}

grouped_xy = if nugroups==1
Dict{K, V}(ugroups[1]=>Dat)
elseif nugroups>1
Dict{K, V}(g=>Dat[groups.==[g],:] for g in ugroups)
end

aess = Gadfly.Aesthetics[]
for (g, data) in grouped_xy
aes1 = Gadfly.Aesthetics()
k = KernelDensity.kde(data, bandwidth=window, npoints=stat.n)
aes1.x, aes1.y, aes1.z = k.x, k.y, k.density
apply_statistic(ContourStatistic(levels=stat.levels), scales, coord, aes1)
colorflag && (aes1.color = fill(g[1], length(aes1.x)))
push!(aess, aes1)
end

aes2 = Gadfly.concat(aess...)
aes.x, aes.y, aes.color = aes2.x, aes2.y, aes2.color
colorflag && (linestyleflag ? (aes.group = aes2.group) : (aes.linestyle = aes2.group.+1))
if !colorflag
aes.group = aes2.group
aes.color_function = aes2.color_function
aes.color_label = aes2.color_label
aes.color_key_colors = aes2.color_key_colors
aes.color_key_continuous = aes2.color_key_continuous
end

end


Expand Down

0 comments on commit c60cc91

Please sign in to comment.