JuliaRobotics / KernelDensityEstimatePlotting.jl
1
module KernelDensityEstimatePlotting
2

3
using LinearAlgebra, Statistics
4
using KernelDensityEstimate
5
using Gadfly, Colors, Cairo, Fontconfig, Compose
6

7
import Gadfly: plot
8
# KDE imports will be remove when plotting is permanently removed from the KDE package
9
# import KernelDensityEstimate: plotKDE,plotKDEContour,drawAllPairs,stackMarginals,vstackedPlots,drawHorDens,toggleYTicks
10

11
export
12
  # Gadfly plotting functions
13
  plot,
14
  getColorsByLength, # duplicated in RoMEPlotting
15
  plotKDEContour,
16
  plotKDE,
17
  drawPair,
18
  drawAllPairs,
19
  stackMarginals,
20
  vstackedPlots,
21
  drawHorDens,
22
  toggleYTicks,
23
  DOYTICKS
24

25
NothingUnion{T}   = Union{Nothing, T}
26

27
# Guide.manual_color_key("Legend", ["Points", "Line"], ["green", "deepskyblue"])
28

29
DOYTICKS=true
30

31

32 0
function toggleYTicks()
33 0
  global DOYTICKS
34 0
  DOYTICKS = DOYTICKS ? false : true
35 0
  return DOYTICKS
36
end
37

38
# function plot(p::BallTreeDensity)
39
#    plotKDE(p)
40
# end
41

42 53
function plot(darr::Union{BallTreeDensity, Vector{BallTreeDensity}};
43
      c::NothingUnion{Vector}=nothing,
44
      N::Int=200,
45
      rmax=-Inf,rmin=Inf,  # should be deprecated
46
      axis::NothingUnion{Array{Float64,2}}=nothing,
47
      dims::NothingUnion{VectorRange{Int}}=nothing,
48
      xlbl::T="X", # to be deprecated
49
      title::NothingUnion{T}=nothing,
50
      legend::NothingUnion{Vector{T}}=nothing,
51
      dimLbls::NothingUnion{Vector{T}}=nothing,
52
      levels::NothingUnion{Int}=nothing,
53
      fill=false, layers::Bool=false ) where {T <: AbstractString}
54
  #
55 140
  plotKDE(darr, c=c, N=N, rmin=rmin, rmax=rmax, axis=axis, dims=dims, xlbl=xlbl, title=title, legend=legend, dimLbls=dimLbls, levels=levels, fill=fill, layers=layers )
56
end
57

58

59

60 53
function draw1D!(bd::BallTreeDensity,
61
                 bins::AbstractArray{Float64,1},
62
                 e,
63
                 c::T="deepskyblue",
64
                 myStyle::T="";
65
                 xlbl="X",
66
                 points::Bool=true,
67
                 legend=nothing,
68
                 title::NothingUnion{T}=nothing,
69
                 fill=false, layers::Bool=false ) where {T <: AbstractString}
70
  #
71 140
  global DOYTICKS
72

73 140
  yV = evaluateDualTree(bd,bins)
74
  # clamp max y values
75 107
  yV[3.0 .< yV] .= 3.0
76 107
  if e == nothing
77 107
    ptArr = Any[]
78

79 140
    if points
80 140
      tps = getPoints(bd)
81 107
      lrug = Gadfly.layer(x=tps, y=-0.01.*ones(size(tps,2)), Geom.point)
82 107
      push!(ptArr, lrug)
83
    end
84 140
    l1 = Gadfly.layer(x=bins,y=yV,Geom.line, Gadfly.Theme(default_color=parse(Colorant,c),line_width=2pt, point_size=1pt))
85 107
    push!(ptArr, l1)
86 107
    push!(ptArr, Guide.xlabel(xlbl))
87 140
    if !DOYTICKS
88
      # e=Gadfly.plot(x=bins,y=yV,Geom.line, Gadfly.Theme(default_color=parse(Colorant,c)),Guide.xlabel(xlbl))
89
    # else
90 0
      push!(ptArr, Guide.ylabel(""))
91 0
      push!(ptArr,Guide.yticks(ticks=nothing))
92
      # e=Gadfly.plot(x=bins,y=yV,Geom.line, Gadfly.Theme(default_color=parse(Colorant,c)),Guide.xlabel(xlbl),Guide.ylabel(""),Guide.yticks(ticks=nothing))
93
    end
94 107
    if legend != nothing
95 0
      push!(ptArr, legend)
96
    end
97 107
    if title != nothing
98 0
      push!(ptArr, Guide.title(title))
99
    end
100

101 140
    e = !layers ? Gadfly.plot(ptArr...) : ptArr
102
  else
103 140
    push!(e.layers, layer(x=bins, y=yV, Geom.line, Gadfly.Theme(default_color=parse(Colorant,c),line_width=2pt))[1])
104
  end
105

106
  #mx = maximum(y)
107
  #wts = getWeights(bd)
108 140
  return e
109
end
110

111

112 53
function plotKDEContour(pp::Vector{BallTreeDensity};
113
    xmin=-Inf,xmax=Inf,ymin=-Inf,ymax=Inf,
114
    xlbl::T="x", ylbl::T="y",
115
    N::Int=200,
116
    c::NothingUnion{Vector}=nothing,
117
    legend=nothing,
118
    title::NothingUnion{T}=nothing,
119
    levels::NothingUnion{Int}=nothing,
120
    fill=false, layers::Bool=false,
121
    line_width=2pt  ) where {T <: AbstractString}
122

123 140
  rangeV = getKDERange(pp[1])
124 140
  size(rangeV,1) == 2 ? nothing : error("plotKDEContour must receive two dimensional kde, you gave $(Ndim(x))")
125 140
  xmin = xmin != -Inf ? xmin : rangeV[1,1]
126 140
  xmax = xmax != Inf ? xmax : rangeV[1,2]
127 140
  ymin = ymin != -Inf ? ymin : rangeV[2,1]
128 140
  ymax = ymax != Inf ? ymax : rangeV[2,2]
129 140
  for i in 2:length(pp)
130 140
    rangeV = getKDERange(pp[i])
131 140
    xmin = xmin <= rangeV[1,1] ? xmin : rangeV[1,1]
132 140
    xmax = xmax >= rangeV[1,2] ? xmax : rangeV[1,2]
133 140
    ymin = ymin <= rangeV[2,1] ? ymin : rangeV[2,1]
134 140
    ymax = ymax >= rangeV[2,2] ? ymax : rangeV[2,2]
135
  end
136

137 140
  PL = []
138

139
  # default options
140 107
  CO = levels == nothing ? Geom.contour : Geom.contour(levels=levels)
141 107
  if c == nothing
142 140
    c = ["deepskyblue" for i in 1:length(pp)]
143
  else
144 107
    push!(PL, Gadfly.Scale.color_none)
145
  end
146
  # Gadfly.plot(
147

148 107
  i = 0
149 140
  for p in pp
150 107
    i+=1
151 140
    push!(PL, layer(z=(x,y)->evaluateDualTree(p,([[x]';[y]']))[1],
152
    x=range(xmin,stop=xmax,length=N),
153
    y=range(ymin,stop=ymax,length=N),
154
    CO,
155
    Theme(default_color=parse(Colorant,c[i]),line_width=line_width))[1] )
156
    ## trying to get rug to work
157
    # rugpl = plot(x=getPoints(p)[1,:], y=getPoints(p)[2,:], Guide.xrug, Guide.yrug)
158
    # push!( PL, rugpl.layers[1] )
159
  end
160

161 107
  push!(PL,Coord.Cartesian(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax))
162 140
  push!(PL,Guide.xlabel(xlbl), Guide.ylabel(ylbl))
163

164
  # Might be a per layer theme
165 140
  !fill ? nothing : push!(PL, Theme(background_color=colorant"white"))
166

167 107
  if legend != nothing
168 0
    push!(PL, legend)
169
  end
170

171 107
  if title != nothing
172 0
    push!(PL, Guide.title(title))
173
  end
174 140
  if !layers
175 140
    return Gadfly.plot(PL...)
176
  else
177 0
    return PL
178
  end
179
end
180
# p1 = plot(
181
#        layer(z=ff,x=linspace(-5,5,100),y=linspace(-5,5,100), Geom.contour(levels=5),Theme(default_color=parse(Colorant,"blue"))),
182
#        layer(z=ff2,x=linspace(-5,5,100),y=linspace(-5,5,100), Geom.contour(levels=5),Theme(default_color=parse(Colorant,"red"))),
183
#        Scale.color_none)
184

185 0
function plotKDEContour(p::BallTreeDensity;
186
                        xmin=-Inf,xmax=Inf,ymin=-Inf,ymax=Inf,
187
                        xlbl::T="x", ylbl::T="y",
188
                        N::Int=200,
189
                        c::NothingUnion{Vector}=nothing,
190
                        legend=nothing,
191
                        title::NothingUnion{T}=nothing,
192
                        levels::NothingUnion{Int}=nothing,
193
                        fill=false, layers::Bool=false,
194
                        line_width=2pt  ) where {T <: AbstractString}
195

196 0
    plotKDEContour([p],
197
      xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax,
198
      xlbl=xlbl, ylbl=ylbl,
199
      N=N,
200
      c=c,
201
      legend=legend,
202
      title=title,
203
      levels=levels,
204
      fill=fill, layers=layers,
205
      line_width=line_width )
206
end
207

208 53
function drawPair(xx::Vector{BallTreeDensity},
209
                  dims::Vector{Int};
210
                  axis::NothingUnion{Array{Float64,2}}=nothing,
211
                  dimLbls::NothingUnion{Vector{T}}=nothing,
212
                  legend=nothing,
213
                  title::NothingUnion{T}=nothing,
214
                  levels::NothingUnion{Int}=nothing,
215
                  c::NothingUnion{Vector}=nothing,
216
                  fill=false,
217
                  layers::Bool=false,
218
                  overlay=nothing ) where {T <: AbstractString}
219
  #
220
  # pts = getPoints(x);
221 140
  xmin, xmax, ymin, ymax = -Inf,Inf,-Inf,Inf
222 107
  if axis != nothing
223 0
    xmin, xmax, ymin, ymax = axis[dims[1],1], axis[dims[1],2], axis[dims[2],1], axis[dims[2],2]
224
  end
225

226 140
  xlbl, ylbl = nothing, nothing
227 140
  if dimLbls!=nothing
228 107
    xlbl, ylbl = dimLbls[dims[1]], dimLbls[dims[2]]
229
  end
230

231 140
  X = BallTreeDensity[]
232 140
  for x in xx
233 140
    push!(X, marginal(x,dims))
234
  end
235 140
  plr = plotKDEContour(X,
236
    xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax,
237
    xlbl=xlbl,ylbl=ylbl,
238
    legend=legend,
239
    title=title,
240
    levels=levels,c=c,
241
    fill=fill, layers=layers  )
242

243
  # add overlay
244 107
  if overlay != nothing
245 0
    union!(plr.layers, overlay.layers)
246
  end
247 140
  plr
248
end
249

250
function stacking(spp::Vector{<:Compose.Context})
251 140
  hstack(spp...)
252
end
253

254
# function to draw all pairs of mulitdimensional kernel density estimate
255
# axis is matrix with rows as dimensions and two columns for min and max axis cutoffs
256 53
function drawAllPairs(xx::Vector{BallTreeDensity};
257
      dims::NothingUnion{VectorRange{Int}}=nothing,
258
      axis::NothingUnion{Array{Float64,2}}=nothing,
259
      dimLbls::NothingUnion{Vector{T}}=nothing,
260
      legend=nothing,
261
      title::NothingUnion{T}=nothing,
262
      levels::NothingUnion{Int}=nothing,
263
      c::NothingUnion{Vector}=nothing,
264
      fill=false,
265
      layers::Bool=false,
266
      overlay=nothing  ) where {T <: AbstractString}
267

268
  # pts = getPoints(xx[1]);
269
  # e = [];
270 140
  dims = dims != nothing ? collect(dims) : collect(1:Ndim(xx[1]))
271 107
  Nout = length(dims);
272 140
  PlotI2 = triu(repeat( (dims)' ,Nout, 1), 1);
273 140
  PlotI1 = triu(repeat((dims)' , Nout, 1)', 1);
274 140
  PlotI1, PlotI2 = PlotI1[(LinearIndices(PlotI1))[findall(x->x!=0,PlotI1)]],  PlotI2[(LinearIndices(PlotI2))[findall(x->x!=0,PlotI2)]];
275
  # PlotI1, PlotI2 = PlotI1[find(PlotI1)],  PlotI2[find(PlotI2)];
276 140
  Ncol = round(Int, sqrt(length(PlotI2)));
277 140
  Nrow = ceil(Int, length(PlotI2)/Ncol);
278

279 107
  subplots = Array{Gadfly.Plot,2}(undef, Nrow,Ncol)
280 140
  for iT=1:length(PlotI2)
281
    # only returns layers for first pair
282 140
    ovl = Nout==2 ? overlay : nothing
283 140
    if !layers
284 140
      subplots[iT] = drawPair(xx,[PlotI1[iT];PlotI2[iT]], axis=axis, dimLbls=dimLbls, legend=legend, title=title, levels=levels, c=c, fill=fill, layers=layers, overlay=ovl );
285
    else
286 140
      return drawPair(xx,[PlotI1[iT];PlotI2[iT]], axis=axis, dimLbls=dimLbls, legend=legend, title=title, levels=levels, c=c, fill=fill, layers=layers, overlay=ovl )
287
    end
288
  end;
289

290 140
  Nrow==1 && Ncol==1 ? nothing : println("Multiple planes stacked into Compose.Context, use Gadfly.draw(PNG(file.png,10cm,10cm),plothdl). Or PDF.")
291 107
  hh = Vector{Gadfly.Context}(undef, Nrow)
292 140
  for i in 1:Nrow
293 107
    sp = Compose.Context[]
294 140
    for j in 1:Ncol
295 140
      if Nrow == 1 && Ncol==1
296

297
      else
298 140
        try
299 140
          push!(sp,hstack(subplots[i,j])) # very hacky line, but Compose.Context and Gadfly.Plot not playing nice together in hstack
300
        catch e
301 140
          print("KDEPlotting01.jl/drawAllPairs -- supressing all exceptions for stacking empty contour plots")
302 140
          println(e)
303 140
          push!(sp,Gadfly.context())
304
        end
305
      end
306
    end
307 140
    hh[i] = stacking(sp) #hstack(sp) #subplots[i,:])
308
  end
309

310 140
  return Nrow==1 && Ncol==1 ? subplots[1,1] : vstack(hh...)
311
end
312

313
# """
314
#     $(SIGNATURES)
315

316
# Standardize the length colors used by RoMEPlotting, returns `::Vector{String}`.
317
# """
318 53
function getColorsByLength(len::Int=7)
319 140
  len > 99 ? error("Don't have enough colors, 100 is the max.") : nothing
320 140
  COLORS = String["red";"green";"blue";"magenta";"yellow";"deepskyblue"]
321 140
  if len > 6
322 0
    scale = len -7 + 2 # + 2 is artificial and avoids gray100==white
323 0
    scale = 100/scale
324 0
    for i in 7:len
325 0
      push!(COLORS, "gray$(floor(Int,(i-7)*scale))")
326
    end
327
  end
328 140
  return COLORS[1:len]
329
end
330

331
# function to draw all pairs of mulitdimensional kernel density estimate
332
# axis is matrix with rows as dimensions and two columns for min and max axis cutoffs
333 53
function plotKDE(darr::Array{BallTreeDensity,1};
334
                 c::NothingUnion{Vector{<:AbstractString}}=getColorsByLength(length(darr)), # nothing
335
                 N::Int=200,
336
                 rmax=-Inf,rmin=Inf,  # should be deprecated
337
                 axis::NothingUnion{Array{Float64,2}}=nothing,
338
                 dims::NothingUnion{VectorRange{Int}}=nothing,
339
                 xlbl::AbstractString="X", # to be deprecated
340
                 title::NothingUnion{<:AbstractString}=nothing,
341
                 legend::NothingUnion{Vector{<:AbstractString}}=nothing,
342
                 dimLbls::NothingUnion{Vector{<:AbstractString}}=nothing,
343
                 levels::NothingUnion{Int}=nothing,
344
                 fill=false,
345
                 points::Bool=true,
346
                 layers::Bool=false,
347
                 overlay=nothing )
348
    #
349
    # defaults
350 140
    defaultcolor = false
351 140
    if c==nothing || length(c) != length(darr)
352 140
      c = getColorsByLength(length(darr))
353 107
      defaultcolor = true
354
    end
355
    # c = (length(c)>=2) ? c : repeat(c,length(darr))
356 140
    lg = if (legend == nothing)
357 140
      nothing
358
    else
359 0
      thecolors = Vector{Colorant}()
360 0
      for eachcol in parse.(Colorant, c)
361 0
        push!(thecolors, eachcol)
362
      end
363 0
      @show thecolors
364 107
      Guide.manual_color_key("Legend", legend, thecolors)
365
    end
366

367 140
    H = nothing
368 107
    i = 0
369

370 107
    Ndims = Ndim(darr[1])
371 107
    dim = dims!=nothing ? dims : 1:Ndims #.bt.dims
372 140
    dimLbls = dimLbls!=nothing ? dimLbls : String["$(i)" for i in 1:Ndims]
373 140
    dim = collect(dim)
374 140
    if length(dim) == 1
375 140
      for bd in darr
376 107
          i+=1
377 140
          mbd = marginal(bd,dim)
378 140
          rangeV = getKDERange(mbd)
379 140
          if (length(dim) == 1) # Ndim(bd)
380 140
            if rangeV[1] > rmin  rangeV[1] = rmin end
381 140
            if rmax > rangeV[2]  rangeV[2] = rmax end
382 107
            if axis!=nothing
383 0
              di = dim[1]
384 0
              if rangeV[1] > axis[di,1]  rangeV[1] = axis[di,1] end
385 0
              if axis[di,2] > rangeV[2]  rangeV[2] = axis[di,2] end
386
            end
387 140
            H=draw1D!(mbd,range(rangeV[1],stop=rangeV[2],length=N), H, c[i],xlbl=xlbl,legend=lg, title=title,            points=points, fill=fill) #,argsPlot,argsKDE
388
          else
389
            #
390 140
          end
391
      end
392

393
    else
394 140
      color = defaultcolor ? nothing : c
395 140
      H = drawAllPairs(darr, axis=axis, dims=dim, dimLbls=dimLbls, legend=lg, title=title, levels=levels, c=color, fill=fill, layers=layers)
396
    end
397

398 140
    if overlay != nothing && length(dim) <= 2
399 0
      union!(H.layers, )
400
    end
401

402 140
    return H
403
end
404

405

406
function plotKDE(bd::BallTreeDensity;
407
                 c::NothingUnion{Vector}=nothing,
408
                 N::Int=200,
409
                 rmax=-Inf,rmin=Inf,  # should be deprecated
410
                 axis::NothingUnion{Array{Float64,2}}=nothing,
411
                 dims::NothingUnion{VectorRange{Int}}=nothing,
412
                 xlbl::T="X",
413
                 legend::NothingUnion{Vector{T}}=nothing,
414
                 title::NothingUnion{T}=nothing,
415
                 dimLbls::NothingUnion{Vector{T}}=nothing,
416
                 levels::NothingUnion{Int}=nothing,
417
                 fill=false,
418
                 points::Bool=true,
419
                 layers::Bool=false,
420
                 overlay=nothing  ) where {T <: AbstractString}
421

422 140
  plotKDE([bd],N=N,c=c,rmax=rmax,rmin=rmin,xlbl=xlbl,legend=legend, dims=dims, axis=axis, dimLbls=dimLbls, levels=levels, title=title, fill=fill, layers=layers, overlay=overlay, points=points )
423
end
424

425

426
macro ifund(exp)
427
    local e = :($exp)
428
    isdefined(e.args[1]) ? :($(e.args[1])) : :($(esc(exp)))
429
end
430

431 0
function drawHorDens(pDens::Array{BallTreeDensity,1}; N::Int=200,
432
                    gt=Union{}, lbls=String[], extend::Float64=0.1)
433 0
    len = length(pDens)
434 0
    h = Array{Gadfly.Plot,1}(undef, len)
435
    # r = Array{Future,1}(len) #Array{RemoteRef,1}(len)
436

437 0
    if gt!=Union{}
438 0
      for i in 1:length(pDens)
439 0
        g = kde!(gt[i][1,:],[gt[i][2,1]])
440 0
        v = getKDERange(pDens[i],extend=extend)
441 0
        p = plotKDE([pDens[i];g],c=["black";"red"],rmin=v[1],rmax=v[2],xlbl=lbls[i])
442 0
        h[i] =p;
443
      end
444
    else
445 0
      for i in 1:length(pDens)
446 0
         h[i]=plotKDE([pDens[i]],xlbl=lbls[i])
447
      end
448
    end
449
    #println("draw $(len) plots")
450
    # [r[i] = @spawn plotKDE(pDens[i], N=N) for i in 1:len]
451
    # [h[i] = fetch(r[i]) for i in 1:len]
452

453 0
    hstack(h)
454
end
455

456 0
function stackMarginals(P::Array{BallTreeDensity,1}, m::Int64)
457 0
  ret = Array{BallTreeDensity,1}(length(P))
458 0
  for i in 1:length(P)
459 0
    ret[i] =  marginal(P[i],[m])#push!()
460
  end
461 0
  return ret
462
end
463

464

465 0
function vstackedPlots(plots::Vector{<: Gadfly.Compose.Context})
466 0
    evalstr = ""
467 0
    for i in 1:length(plots)
468 0
        evalstr = string(evalstr, ",plots[$(i)]")
469
    end
470 0
    evalstr = string("vstack(",evalstr[2:end],")")
471 0
    eval(parse(evalstr))
472
end
473

474

475
end  # module

Read our documentation on viewing source code .

Loading