From 76efabb5f3a4ebbfb6892cd1ad3387481bc2360b Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Mon, 14 Sep 2015 00:15:51 -0400 Subject: [PATCH] heatmaps, fixes --- docs/example_generation.jl | 2 +- docs/pyplot_examples.md | 146 +++++++++++++++++++++++++++++++++++++ src/args.jl | 18 +++++ src/backends/pyplot.jl | 81 ++++++++++---------- 4 files changed, 206 insertions(+), 41 deletions(-) diff --git a/docs/example_generation.jl b/docs/example_generation.jl index 12315c6e..e54e8a51 100644 --- a/docs/example_generation.jl +++ b/docs/example_generation.jl @@ -48,7 +48,7 @@ const examples = PlotExample[ [:(scatter!(rand(100); markersize=6, color=:blue))]), PlotExample("Heatmaps", "", - [:(heatmap(randn(10000),randn(10000); nbins=200))]), + [:(heatmap(randn(10000),randn(10000); nbins=100))]), PlotExample("Lots of line types", "Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar) \nNote: some may not work with all backends", [:(plot(rand(20,4); linetypes=[:line, :step, :sticks, :dots], labels=["line","step","sticks","dots"]))]), diff --git a/docs/pyplot_examples.md b/docs/pyplot_examples.md index e69de29b..07598c42 100644 --- a/docs/pyplot_examples.md +++ b/docs/pyplot_examples.md @@ -0,0 +1,146 @@ +### Lines + +A simple line plot of the 3 columns. + +```julia +plot(rand(100,3)) +``` + +![](../img/pyplot/pyplot_example_1.png) + +### Functions + +Plot multiple functions. + +```julia +plot(0:0.01:4π,[sin,cos]) +``` + +![](../img/pyplot/pyplot_example_2.png) + +### + +You can also call it with plot(f, xmin, xmax). + +```julia +plot([sin,cos],0,4π) +``` + +![](../img/pyplot/pyplot_example_3.png) + +### + +Or make a parametric plot with plot(fx, fy, umin, umax). + +```julia +plot(sin,(x->begin # /Users/tom/.julia/v0.4/Plots/docs/example_generation.jl, line 33: + sin(2x) + end),0,2π) +``` + +![](../img/pyplot/pyplot_example_4.png) + +### Global + +Change the guides/background without a separate call. + +```julia +plot(rand(10); title="TITLE",xlabel="XLABEL",ylabel="YLABEL",background_color=RGB(0.5,0.5,0.5)) +``` + +![](../img/pyplot/pyplot_example_5.png) + +### Two-axis + +Use the `axis` or `axiss` arguments. + +Note: This is only supported with Qwt right now + +```julia +plot(Vector[randn(100),randn(100) * 100]; axiss=[:left,:right],ylabel="LEFT",yrightlabel="RIGHT") +``` + +![](../img/pyplot/pyplot_example_6.png) + +### Vectors w/ pluralized args + +Plot multiple series with different numbers of points. Mix arguments that apply to all series (singular... see `marker`) with arguments unique to each series (pluralized... see `colors`). + +```julia +plot(Vector[rand(10),rand(20)]; marker=:ellipse,markersize=8,colors=[:red,:blue]) +``` + +![](../img/pyplot/pyplot_example_7.png) + +### Build plot in pieces + +Start with a base plot... + +```julia +plot(rand(100) / 3; reg=true,fillto=0) +``` + +![](../img/pyplot/pyplot_example_8.png) + +### + +and add to it later. + +```julia +scatter!(rand(100); markersize=6,color=:blue) +``` + +![](../img/pyplot/pyplot_example_9.png) + +### Heatmaps + + + +```julia +heatmap(randn(10000),randn(10000); nbins=100) +``` + +![](../img/pyplot/pyplot_example_10.png) + +### Lots of line types + +Options: (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar) +Note: some may not work with all backends + +```julia +plot(rand(20,4); linetypes=[:line,:step,:sticks,:dots],labels=["line","step","sticks","dots"]) +``` + +![](../img/pyplot/pyplot_example_11.png) + +### Lots of marker types + +Options: (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon) +Note: some may not work with all backends + +```julia +plot(repmat(collect(1:10)',10,1); markers=[:ellipse,:rect,:diamond,:utriangle,:dtriangle,:cross,:xcross,:star1,:star2,:hexagon],labels=["ellipse","rect","diamond","utriangle","dtriangle","cross","xcross","star1","star2","hexagon"],linetype=:none,markersize=10) +``` + +![](../img/pyplot/pyplot_example_12.png) + +### Bar + +x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints) + +```julia +bar(randn(1000)) +``` + +![](../img/pyplot/pyplot_example_13.png) + +### Histogram + +note: fillto isn't supported on all backends + +```julia +histogram(randn(1000); nbins=50,fillto=20) +``` + +![](../img/pyplot/pyplot_example_14.png) + diff --git a/src/args.jl b/src/args.jl index 76721f7b..cda92a69 100644 --- a/src/args.jl +++ b/src/args.jl @@ -85,11 +85,21 @@ function getRGBColor(c, n::Int = 0) c end +# const ALT_ARG_NAMES = Dict{Tuple{Symbol,Symbol}, Any}() +# ALT_ARG_NAMES[(:linetype, :scatter)] = :dots + # note: idx is the index of this series within this call, n is the index of the series from all calls to plot/subplot function getPlotKeywordArgs(kw, idx::Int, n::Int) d = Dict(kw) + # # replace alternate names + # for tup in kw + # if haskey(ALT_ARG_NAMES, tup) + # d[tup[1]] = ALT_ARG_NAMES[tup] + # end + # end + # default to a white background, but only on the initial call (so we don't change the background automatically) if haskey(d, :background_color) d[:background_color] = getRGBColor(d[:background_color]) @@ -108,6 +118,14 @@ function getPlotKeywordArgs(kw, idx::Int, n::Int) delete!(d, plural) end + # swap out dots for no line and a marker + if haskey(d, :linetype) && d[:linetype] == :dots + d[:linetype] = :none + if d[:marker] == :none + d[:marker] = :ellipse + end + end + # handle plot initialization differently diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index a6661278..71337316 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -1,5 +1,5 @@ -# https://github.com/tbreloff/Qwt.jl +# https://github.com/stevengj/PyPlot.jl immutable PyPlotPackage <: PlottingPackage end @@ -7,23 +7,6 @@ pyplot!() = plotter!(:pyplot) # ------------------------------- -# function adjustQwtKeywords(iscreating::Bool; kw...) -# d = Dict(kw) -# d[:heatmap_n] = d[:nbins] - -# if d[:linetype] == :hexbin -# d[:linetype] = :heatmap -# elseif d[:linetype] == :dots -# d[:linetype] = :none -# d[:marker] = :hexagon -# elseif !iscreating && d[:linetype] == :bar -# return barHack(; kw...) -# elseif !iscreating && d[:linetype] == :hist -# return barHack(; histogramHack(; kw...)...) -# end -# d -# end - # convert colorant to 4-tuple RGBA getPyPlotColor(c::Colorant) = map(f->float(f(c)), (red, green, blue, alpha)) @@ -64,8 +47,8 @@ function getPyPlotMarker(marker::String) end function getPyPlotDrawStyle(linetype::Symbol) - linetype == :step && "steps-post" - linetype == :stepinverted && "steps-pre" + linetype == :step && return "steps-post" + linetype == :stepinverted && return "steps-pre" return "default" end @@ -78,11 +61,22 @@ function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol) if axis == :right ax = getRightAxis(plt.o) ax[:set_ylabel](plt.initargs[:yrightlabel]) - return ax[linetype == :hist ? :hist : (linetype in (:sticks,:bar) ? :bar : :plot)] + return ax[linetype == :hist ? :hist : (linetype in (:sticks,:bar) ? :bar : (linetype in (:heatmap,:hexbin) ? :hexbin : :plot))] end - return linetype == :hist ? PyPlot.plt[:hist] : (linetype in (:sticks,:bar) ? PyPlot.bar : PyPlot.plot) + return linetype == :hist ? PyPlot.plt[:hist] : (linetype in (:sticks,:bar) ? PyPlot.bar : (linetype in (:heatmap,:hexbin) ? PyPlot.hexbin : PyPlot.plot)) end +# ------------------------------------------------------------------ + +# TODO: +# fillto # might have to use barHack/histogramHack?? +# heatmap +# subplot +# reg # true or false, add a regression line for each line +# pos # (Int,Int), move the enclosing window to this position +# windowtitle # string or symbol, set the title of the enclosing windowtitle +# screen # Integer, move enclosing window to this screen number (for multiscreen desktops) +# show # true or false, show the plot (in case you don't want the window to pop up right away) function plot(pkg::PyPlotPackage; kw...) # create the figure @@ -99,15 +93,6 @@ function plot(pkg::PyPlotPackage; kw...) plt end -# TODO: -# fillto # might have to use barHack/histogramHack?? -# heatmap -# subplot -# reg # true or false, add a regression line for each line -# pos # (Int,Int), move the enclosing window to this position -# windowtitle # string or symbol, set the title of the enclosing windowtitle -# screen # Integer, move enclosing window to this screen number (for multiscreen desktops) -# show # true or false, show the plot (in case you don't want the window to pop up right away) function plot!(::PyPlotPackage, plt::Plot; kw...) d = Dict(kw) @@ -125,9 +110,13 @@ function plot!(::PyPlotPackage, plt::Plot; kw...) if lt == :hist extraargs[:bins] = d[:nbins] else - extraargs[:width] = (lt == :sticks ? 0.01 : 0.9) + extraargs[:width] = (lt == :sticks ? 0.1 : 0.9) end + elseif lt in (:heatmap, :hexbin) + + extraargs[:gridsize] = d[:nbins] + else # all but color/label @@ -147,28 +136,40 @@ function plot!(::PyPlotPackage, plt::Plot; kw...) # do the plot if lt == :hist - plotfunc(d[:y]; extraargs...) + d[:serieshandle] = plotfunc(d[:y]; extraargs...) else - plotfunc(d[:x], d[:y]; extraargs...) - end - - # add a legend? - if plt.initargs[:legend] - PyPlot.legend() + d[:serieshandle] = plotfunc(d[:x], d[:y]; extraargs...) end push!(plt.seriesargs, d) plt end +function addPyPlotLegend(plt::Plot) + # add a legend? + # try + if plt.initargs[:legend] + # gotta do this to ensure both axes are included + args = filter(x -> !(x[:linetype] in (:hist,:hexbin,:heatmap)), plt.seriesargs) + if length(args) > 0 + PyPlot.legend([d[:serieshandle][1] for d in args], [d[:label] for d in args], loc="best") + end + end + # catch ex + # warn("Error adding PyPlot legend: ", ex) + # end +end + function Base.display(::PyPlotPackage, plt::Plot) + addPyPlotLegend(plt) display(plt.o) end # ------------------------------- function savepng(::PyPlotPackage, plt::PlottingObject, fn::String, args...) - f = open(fn) + addPyPlotLegend(plt) + f = open(fn, "w") writemime(f, "image/png", plt.o) close(f) end