working on org/docs/test

This commit is contained in:
Thomas Breloff 2015-09-02 19:00:51 -04:00
parent ef4a47f50f
commit 0d0e584748
5 changed files with 323 additions and 238 deletions

View File

@ -1,9 +1,18 @@
module Plots module Plots
export
Plot,
plotter,
plotter!,
plot,
plot!,
savepng
# ---------------------------------------------------------
typealias AVec AbstractVector typealias AVec AbstractVector
typealias AMat AbstractMatrix typealias AMat AbstractMatrix
# ---------------------------------------------------------
const IMG_DIR = "$(ENV["HOME"])/.julia/v0.4/Plots/img/" const IMG_DIR = "$(ENV["HOME"])/.julia/v0.4/Plots/img/"
@ -12,7 +21,7 @@ const IMG_DIR = "$(ENV["HOME"])/.julia/v0.4/Plots/img/"
type Plot type Plot
o # the underlying object o # the underlying object
plotter::symbol plotter::Symbol
xdata::Vector{AVec} xdata::Vector{AVec}
ydata::Vector{AVec} ydata::Vector{AVec}
end end
@ -42,243 +51,10 @@ include("gadfly.jl")
# --------------------------------------------------------- # ---------------------------------------------------------
include("args.jl") include("args.jl")
include("plot.jl")
# --------------------------------------------------------- # ---------------------------------------------------------
export
plotter,
plotter!,
plot,
plot!,
# subplot,
savepng
doc"""
The main plot command. You must call `plotter!(:ModuleName)` to set the current plotting environment first.
Commands are converted into the relevant plotting commands for that package:
```
plotter!(:gadfly)
plot(1:10) # this calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the currentPlot
plot!(args...; kw...) # adds to the `currentPlot`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
Here are some various args to supply, and the implicit mapping (AVec == AbstractVector and AMat == AbstractMatrix):
```
plot(y::AVec; kw...) # one line... x = 1:length(y)
plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
plot(x::AVec, f::Function; kw...) # one line, y = f(x)
plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
plot(n::Integer; kw...) # n lines, all empty (for updating plots)
# TODO: how do we handle NA values in dataframes?
plot(df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
plot(df::DataFrame, columns; kw...) # one line per column, but on a subset of column names
```
TODO: DataFrames
You can swap out `plot` for `subplot`. Each line will go into a separate plot. Use the layout keyword:
```
y = rand(100,3)
subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3)
subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row
subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row,
# and the others will share the second row
```
Some keyword arguments you can set:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`
markersize # size of the marker
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
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)
```
If you don't include a keyword argument, these are the defaults:
```
axis = :left
color = :auto
label = automatically generated (y1, y2, ...., or y1 (R), y2 (R) for the right axis)
width = 2
linetype = :line
linestype = :solid
marker = :none
markercolor = :auto
markersize = 5
title = ""
xlabel = ""
ylabel = ""
yrightlabel = ""
reg = false
size = (800,600)
pos = (0,0)
windowtitle = ""
screen = 1
show = true
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
"""
# -------------------------
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
plt = newplot(plotter())
currentPlot!(plt)
plot!(plt, args...; kw...)
plt
end
# this adds to the current plot
function plot!(args...; kw...)
plt = currentPlot()
plot!(plt, args...; kw...)
plt
end
# -------------------------
# These methods are various ways to add to an existing plot
function plot!(plt::Plot, y::AVec; kw...)
plot!(plt; x = 1:length(y); y = y, kw...)
end
function plot!(plt::Plot, x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
@assert length(x) == length(y)
plot!(plt; x=x, y=y, kw...)
end
function plot!(plt::Plot, y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
n,m = size(y)
for i in 1:m
plot!(plt; x = 1:m, y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
n,m = size(y)
for i in 1:m
@assert length(x) == n
plot!(plt; x = x, y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
@assert size(x) == size(y)
for i in 1:size(x,2)
plot!(plt; x = x[:,i], y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, f::Function; kw...) # one line, y = f(x)
plot!(plt; x = x, y = map(f,x), kw...)
end
function plot!(plt::Plot, x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
for i in 1:size(x,2)
xi = x[:,i]
plot!(plt; x = xi, y = map(f, xi), kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
for i in 1:length(fs)
plot!(plt; x = x, y = map(fs[i], x), kw...)
end
plt
end
function plot!(plt::Plot, y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
for i in 1:length(y)
plot!(plt; x = 1:length(y[i]), y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
for i in 1:length(y)
@assert length(x) == length(y[i])
plot!(plt; x = x, y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
@assert length(x) == length(y)
for i in 1:length(x)
@assert length(x[i]) == length(y[i])
plot!(plt; x = x[i], y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, n::Integer; kw...) # n lines, all empty (for updating plots)
for i in 1:n
plot(plt, x = zeros(0), y = zeros(0), kw...)
end
end
# -------------------------
# this is the core method... add to a plot object using kwargs
function plot!(plt::Plot; kw...)
plot!(plotter(), plt; kw...)
end
# -------------------------
# # TODO: how do we handle NA values in dataframes? # # TODO: how do we handle NA values in dataframes?
# function plot!(plt::Plot, df::DataFrame; kw...) # one line per DataFrame column, labels == names(df) # function plot!(plt::Plot, df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
# end # end

28
src/args.jl Normal file
View File

@ -0,0 +1,28 @@
const COLORS = [:black, :blue, :green, :red, :darkGray, :darkCyan, :darkYellow, :darkMagenta,
:darkBlue, :darkGreen, :darkRed, :gray, :cyan, :yellow, :magenta]
const NUMCOLORS = length(COLORS)
# these are valid choices... first one is default value if unset
const LINE_AXES = (:left, :right)
const LINE_TYPES = (:line, :step, :stepinverted, :sticks, :dots, :none, :heatmap)
const LINE_STYLES = (:solid, :dash, :dot, :dashdot, :dashdotdot)
const LINE_MARKERS = (:none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon)
const DEFAULT_axis = LINE_AXES[1]
const DEFAULT_color = :auto
const DEFAULT_label = "AUTO"
const DEFAULT_width = 2
const DEFAULT_linetype = LINE_TYPES[1]
const DEFAULT_linestyle = LINE_STYLES[1]
const DEFAULT_marker = LINE_MARKERS[1]
const DEFAULT_markercolor = :auto
const DEFAULT_markersize = 10
const DEFAULT_heatmap_n = 100
const DEFAULT_heatmap_c = (0.15, 0.5)
const DEFAULT_title = ""
const DEFAULT_xlabel = ""
const DEFAULT_ylabel = ""
const DEFAULT_yrightlabel = ""

223
src/plot.jl Normal file
View File

@ -0,0 +1,223 @@
doc"""
The main plot command. You must call `plotter!(:ModuleName)` to set the current plotting environment first.
Commands are converted into the relevant plotting commands for that package:
```
plotter!(:gadfly)
plot(1:10) # this calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
plotter!(:qwt)
plot(1:10) # this calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```
plot(args...; kw...) # creates a new plot window, and sets it to be the currentPlot
plot!(args...; kw...) # adds to the `currentPlot`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
Here are some various args to supply, and the implicit mapping (AVec == AbstractVector and AMat == AbstractMatrix):
```
plot(y::AVec; kw...) # one line... x = 1:length(y)
plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
plot(x::AVec, f::Function; kw...) # one line, y = f(x)
plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
plot(x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
plot(x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
plot(n::Integer; kw...) # n lines, all empty (for updating plots)
# TODO: how do we handle NA values in dataframes?
plot(df::DataFrame; kw...) # one line per DataFrame column, labels == names(df)
plot(df::DataFrame, columns; kw...) # one line per column, but on a subset of column names
```
TODO: DataFrames
You can swap out `plot` for `subplot`. Each line will go into a separate plot. Use the layout keyword:
```
y = rand(100,3)
subplot(y; layout=(2,2), kw...) # creates 3 lines going into 3 separate plots, laid out on a 2x2 grid (last row is filled with plot #3)
subplot(y; layout=(1,3), kw...) # again 3 plots, all in the same row
subplot(y; layout=[1,[2,3]]) # pass a nested Array to fully specify the layout. here the first plot will take up the first row,
# and the others will share the second row
```
Some keyword arguments you can set:
```
axis # :left or :right
color # can be a string ("red") or a symbol (:red) or a ColorsTypes.jl Colorant (RGB(1,0,0)) or :auto (which lets the package pick)
label # string or symbol, applies to that line, may go in a legend
width # width of a line
linetype # :line, :step, :stepinverted, :sticks, :dots, :none, :heatmap
linestyle # :solid, :dash, :dot, :dashdot, :dashdotdot
marker # :none, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon
markercolor # same choices as `color`
markersize # size of the marker
title # string or symbol, title of the plot
xlabel # string or symbol, label on the bottom (x) axis
ylabel # string or symbol, label on the left (y) axis
yrightlabel # string or symbol, label on the right (y) axis
reg # true or false, add a regression line for each line
size # (Int,Int), resize the enclosing window
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)
```
If you don't include a keyword argument, these are the defaults:
```
axis = :left
color = :auto
label = automatically generated (y1, y2, ...., or y1 (R), y2 (R) for the right axis)
width = 2
linetype = :line
linestype = :solid
marker = :none
markercolor = :auto
markersize = 5
title = ""
xlabel = ""
ylabel = ""
yrightlabel = ""
reg = false
size = (800,600)
pos = (0,0)
windowtitle = ""
screen = 1
show = true
```
When plotting multiple lines, you can give every line the same trait by using the singular, or add an "s" to pluralize.
(yes I know it's not gramatically correct, but it's easy to use and implement)
```
plot(rand(100,2); colors = [:red, RGB(.5,.5,0)], axiss = [:left, :right], width = 5) # note the width=5 is applied to both lines
```
"""
# -------------------------
# this creates a new plot with args/kw and sets it to be the current plot
function plot(args...; kw...)
plt = newplot(plotter())
currentPlot!(plt)
plot!(plt, args...; kw...)
plt
end
# this adds to the current plot
function plot!(args...; kw...)
plt = currentPlot()
plot!(plt, args...; kw...)
plt
end
# -------------------------
# These methods are various ways to add to an existing plot
function plot!(plt::Plot, y::AVec; kw...)
plot!(plt; x = 1:length(y), y = y, kw...)
end
function plot!(plt::Plot, x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
@assert length(x) == length(y)
plot!(plt; x=x, y=y, kw...)
end
function plot!(plt::Plot, y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
n,m = size(y)
for i in 1:m
plot!(plt; x = 1:m, y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
n,m = size(y)
for i in 1:m
@assert length(x) == n
plot!(plt; x = x, y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
@assert size(x) == size(y)
for i in 1:size(x,2)
plot!(plt; x = x[:,i], y = y[:,i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, f::Function; kw...) # one line, y = f(x)
plot!(plt; x = x, y = map(f,x), kw...)
end
function plot!(plt::Plot, x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
for i in 1:size(x,2)
xi = x[:,i]
plot!(plt; x = xi, y = map(f, xi), kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
for i in 1:length(fs)
plot!(plt; x = x, y = map(fs[i], x), kw...)
end
plt
end
function plot!(plt::Plot, y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
for i in 1:length(y)
plot!(plt; x = 1:length(y[i]), y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec, y::AVec{AVec}; kw...) # multiple lines, will assert length(x) == length(y[i])
for i in 1:length(y)
@assert length(x) == length(y[i])
plot!(plt; x = x, y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, x::AVec{AVec}, y::AVec{AVec}; kw...) # multiple lines, will assert length(x[i]) == length(y[i])
@assert length(x) == length(y)
for i in 1:length(x)
@assert length(x[i]) == length(y[i])
plot!(plt; x = x[i], y = y[i], kw...)
end
plt
end
function plot!(plt::Plot, n::Integer; kw...) # n lines, all empty (for updating plots)
for i in 1:n
plot(plt, x = zeros(0), y = zeros(0), kw...)
end
end
# -------------------------
# this is the core method... add to a plot object using kwargs
function plot!(plt::Plot; kw...)
plot!(plotter(), plt; kw...)
end

58
src/plotter.jl Normal file
View File

@ -0,0 +1,58 @@
using Requires
# these are the plotting packages you can load. we use lazymod so that we
# don't "import" the module until we want it
@lazymod Qwt
@lazymod Gadfly
# ---------------------------------------------------------
abstract PlottingPackage
const AVAILABLE_PACKAGES = [:Qwt, :Gadfly]
const INITIALIZED_PACKAGES = Set{Symbol}()
type CurrentPackage
pkg::Nullable{PlottingPackage}
end
const CURRENT_PACKAGE = CurrentPackage(Nullable{PlottingPackage}())
doc"""Returns the current plotting package name."""
function plotter()
if isnull(CURRENT_PACKAGE.pkg)
error("Must choose a plotter. Example: `plotter(:Qwt)`")
end
get(CURRENT_PACKAGE.pkg)
end
doc"""
Setup the plot environment.
`plotter(:Qwt)` will load package Qwt.jl and map all subsequent plot commands to that package.
Same for `plotter(:Gadfly)`, etc.
"""
function plotter!(modname)
if modname == :qwt
if !(modname in INITIALIZED_PACKAGES)
qwt()
push!(INITIALIZED_PACKAGES, modname)
end
global Qwt = Main.Qwt
CURRENT_PACKAGE.pkg = Nullable(QwtPackage())
return
elseif modname == :gadfly
if !(modname in INITIALIZED_PACKAGES)
gadfly()
push!(INITIALIZED_PACKAGES, modname)
end
global Gadfly = Main.Gadfly
CURRENT_PACKAGE.pkg = Nullable(GadflyPackage())
return
end
error("Unknown plotter $modname. Choose from: $AVAILABLE_PACKAGES")
end

View File

@ -6,13 +6,13 @@ using FactCheck
facts("Qwt") do facts("Qwt") do
@fact plotter!(:qwt) --> nothing @fact plotter!(:qwt) --> nothing
@fact plotter() --> :qwt @fact plotter() --> Plots.QwtPackage()
@fact tpye(plot(1:10, show=false)) --> Plot @fact tpye(plot(1:10, show=false)) --> Plot
end end
facts("Gadfly") do facts("Gadfly") do
@fact plotter!(:gadfly) --> nothing @fact plotter!(:gadfly) --> nothing
@fact plotter() --> :gadfly @fact plotter() --> Plots.GadflyPackage()
@fact tpye(plot(1:10, show=false)) --> Plot @fact tpye(plot(1:10, show=false)) --> Plot
end end