In [95]:
using Plots
pyplot(size=(500,300))
# this is called from within a recipe. it computes edges and counts for our histogram.
function myhist!(d::KW)
# There is a fully populated attribute dictionary `d` available.
# `bin_ends` is not a Plots attribute... I just made it up.
# (note: make sure you remove it from the dictionary with `pop!` or `delete!`)
bin_ends = pop!(d, :bin_ends, nothing)
xmin, xmax = if bin_ends == nothing
extrema(d[:y])
else
bin_ends
end
# `bins` is a Plots attribute, and will get the default value,
# but you can still change it.
edges = linspace(xmin, xmax, d[:bins]+1)
edges, counts = hist(d[:y], edges)
end
# This is a series recipe.
# Create your own series type to act on any valid inputs
@recipe function f(::Type{Val{:my_histogram}}, x, y, z)
edges, counts = myhist!(d)
# if `bar_width` is still nothing (the default) we'll a reasonable value
if d[:bar_width] == nothing
bar_width := 0.8mean(diff(edges))
end
# The `:=` operator will force the attribute value.
# `seriestype := :bar` is equivalent to `d[:seriestype] = :bar`.
# Here we are creating a "bar series", where the inside of the bars
# is 30% opaque, and the x/y vectors are the centers and tops of the histogram
seriestype := :bar
fillalpha := 0.3
x := 0.5(edges[1:end-1] + edges[2:end])
y := counts
# This is a series recipe, and we return an empty arguments list to signal
# that this recipe is finished. Most series recipes will return an empty tuple.
()
end
# this is what our data looks like
y = randn(1000)
scatter(y, α=0.3)
Out[95]:
In [96]:
# our custom histogram!
plot(y, t=:my_histogram)
Out[96]:
In [97]:
# the @shorthands macro will create convenience methods for us
@shorthands my_histogram
# now we can call it like this:
my_histogram(y)
my_histogram!(3y, bins=5)
Out[97]:
In [98]:
# I gave myself a hook to set the `bar_width` myself
my_histogram(y, bar_width=0.01)
Out[98]:
In [99]:
# one simple recipe will work on many different input combinations
my_histogram(randn(1000,3))
Out[99]:
In [100]:
# I wish they lined up nicely, so I allow the custom keyword `bin_ends`
my_histogram(randn(1000,3), bin_ends=(-3,3))
Out[100]:
In [101]:
# and you could quickly get creative
with(bar_width=0.03) do
my_histogram(randn(1000), bin_ends=(-2.08,1.92))
my_histogram!(randn(1000), bin_ends=(-1.92,2.08))
end
Out[101]:
In [102]:
# random colors?
Base.rand(::Type{RGB}, n) = [RGB(rand(),rand(),rand()) for i=1:n]
# also... I'm sick of the legend
default(leg=false)
# why would anyone do this?
@recipe function f(::Type{Val{:funky_bar}}, x, y, z)
seriestype := :bar
linecolor := :black
fillcolor := rand(RGB, length(x))
()
end
# who cares? it's funkadelic!
plot(rand(10), t=:funky_bar)
Out[102]:
In [103]:
# Lets redefine a histogram, and give our user the option of funk!
# See how you can modularize transformations by first making a custom
# bar plot, and then making a custom histogram which can call it.
@recipe function f(::Type{Val{:funky_histogram}}, x, y, z)
edges, counts = myhist!(d)
seriestype := (pop!(d, :funky, false) ? :funky_bar : :bar)
linewidth := 0
x := 0.5(edges[1:end-1] + edges[2:end])
y := counts
()
end
@shorthands funky_histogram
# booooooorrrriiiiiiing
funky_histogram(y)
Out[103]:
In [104]:
# that's more like it!
funky_histogram(y, funky=true)
Out[104]:
In [ ]:
In [ ]: