diff --git a/docs/src/advanced.md b/docs/src/advanced.md index 3fe4c73..50b0c12 100644 --- a/docs/src/advanced.md +++ b/docs/src/advanced.md @@ -182,17 +182,7 @@ Gnuplot.quitall() ``` ## Histograms -**Gnuplot.jl** provides facilities to compute (see [`hist()`](@ref) function) and display (see [Histogram recipes](@ref)) histograms. E.g., to quickly preview an histogram: -```@example abc -x = randn(1000); -@gp hist(x) -saveas("advanced013a") # hide -``` -![](assets/advanced013a.png) - -A finer control on the output is achieved by setting the range to consider (`range=` keyword) and either the bin size (`bs=`) or the total number of bins (`nbins=`) in the histogram. See [`hist()`](@ref) documentation for further information. - -The [`hist()`](@ref) return a [`Gnuplot.Histogram1D`](@ref) structure, whose content can be exploited to customize histogram appearence, e.g.: +**Gnuplot.jl** provides a facility to compute (see [`hist()`](@ref) function) an histogram. It allows to set the range to consider (`range=` keyword) and either the bin size (`bs=`) or the total number of bins (`nbins=`) in the histogram (see [`hist()`](@ref) documentation for further information) and return a [`Gnuplot.Histogram1D`](@ref) structure, whose content can be visualized as follows: ```@example abc x = randn(1000); h = hist(x, range=3 .* [-1,1], bs=0.5) @@ -201,18 +191,10 @@ saveas("advanced013b") # hide ``` ![](assets/advanced013b.png) - -**Gnuplot.jl** also allows to compute 2D histograms by passing two vectors (with the same lengths) to [`hist()`](@ref). A quick preview is simply obtained by: +**Gnuplot.jl** also allows to compute 2D histograms by passing two vectors (with the same lengths) to [`hist()`](@ref). Again, a finer control can be achieved by specifying ranges, bin size or number of bins (along both dimensions) and by explicitly using the content of the returned [`Gnuplot.Histogram2D`](@ref) structure: ```@example abc x = randn(10_000) y = randn(10_000) -@gp "set size ratio -1" hist(x, y) -saveas("advanced014a") # hide -``` -![](assets/advanced014a.png) - -Again, a finer control can be achieved by specifying ranges, bin size or number of bins (along both dimensions) and by explicitly using the content of the returned [`Gnuplot.Histogram2D`](@ref) structure: -```@example abc h = hist(x, y, bs1=0.25, nbins2=20, range1=[-3,3], range2=[-3,3]) @gp "set size ratio -1" h.bins1 h.bins2 h.counts "w image notit" saveas("advanced014b") # hide @@ -229,6 +211,8 @@ saveas("advanced014c") # hide ``` ![](assets/advanced014c.png) +See also [Histogram recipes](@ref) for a quicker way to preview histogram plots. + ## Contour lines Although gnuplot already handles contours by itself (with the `set contour` command), **Gnuplot.jl** provides a way to calculate contour lines paths before displaying them, using the [`contourlines()`](@ref) function. We may use it for, e.g., plot contour lines with customized widths and palette, according to their z level. Continuing with the previous example: diff --git a/docs/src/api.md b/docs/src/api.md index b7f2dc7..08c1b2e 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -37,11 +37,16 @@ The following functions are not exported by the **Gnuplot.jl** package since the In order to call these functions you should add the `Gnuplot.` prefix to the function name. ```@docs +Gnuplot.Dataset +Gnuplot.DatasetEmpty +Gnuplot.DatasetText +Gnuplot.DatasetBin Gnuplot.Histogram1D Gnuplot.Histogram2D Gnuplot.IsoContourLines Gnuplot.Options Gnuplot.Path2d +Gnuplot.PlotElement Gnuplot.gpversion Gnuplot.quit Gnuplot.quitall diff --git a/docs/src/recipes.md b/docs/src/recipes.md index e6cf0f9..a834b72 100644 --- a/docs/src/recipes.md +++ b/docs/src/recipes.md @@ -10,48 +10,91 @@ push!( Gnuplot.options.reset, linetypes(:Set1_5, lw=1.5, ps=1.5)) saveas(file) = save(term="pngcairo size 550,350 fontscale 0.8", output="assets/$(file).png") ``` + # Plot recipes -A plot *recipe* is a quicklook visualization procedure aimed at reducing the amount of repetitive code needed to generate a plot. More specifically, a recipe is a function to convert data from the "Julia world" into a form suitable to be ingested in **Gnuplot.jl**. +A plot *recipe* is a quicklook visualization procedure aimed at reducing the amount of repetitive code to generate a plot. More specifically, a recipe is a function that convert data from the "Julia world" into a form suitable to be ingested in **Gnuplot.jl**, namely a scalar (or a vector of) [`Gnuplot.PlotElement`](@ref) object(s). The latter contain informations on how to create a plot, or a part of it, and can be used directly as arguments in a `@gp` or `@gsp` call. There are two kinds of recipes: -- *explicit* recipe: a function which is explicitly invoked by the user. It can have any name and accept any number of arguments and keywords. It is typically used when the visualization of a data type requires some extra information, beside data itself. An example is the quicklook procedure for a `DataFrame` object (shown below); +- *explicit* recipe: a function which is explicitly invoked by the user. It can have any name and accept any number of arguments and keywords. It is typically used when the visualization of a data type requires some extra information, beside data itself (e.g. to plot data from a `DataFrame` object, see [Explicit recipe (example)](@ref)); -- *implicit* recipe: a function which is automatically called by **Gnuplot.jl**. It must extend the `Gnuplot.recipe` function, and accept exactly one mandatory argument. It is typically used when the visualization is completely determined by the data type itself. An example is the visualization of a `Matrix{ColorTypes.RGB}` object as an image. +- *implicit* recipe: a function which is automatically called by **Gnuplot.jl**. It must extend the p`recipe()`](@ref) function, and accept exactly one mandatory argument. It is typically used when the visualization is completely determined by the data type itself (e.g. the visualization of a `Matrix{ColorTypes.RGB}` object as an image, see [Image recipes](@ref)); -In both cases the recipe function must return a scalar, or a vector of, `PlotElements` object(s), containing all the informations to create a plot, or a part of it. +An implicit recipe is invoked whenever the data type of an argument to `@gp` or `@gsp` is not among the allowed ones (see [`@gp()`](@ref) documentation). If a suitable recipe do not exists an error is raised. On the other hand, an explicit recipe needs to be invoked by the user, and the output passed directly to `@gp` or `@gsp`. +Although recipes provides very efficient tools for data exploration, their use typically hide the details of plot generation. As a consequence they provide less flexibility than the approaches described in [Basic usage](@ref) and [Advanced usage](@ref). -The `@gp` or `@gsp`. -In , and can be passed directly to `@gp` or `@gsp`. +Currently, the **Gnuplot.jl** package provides no built-in explicit recipe. The implicit recipes are implemented in [recipes.jl](https://github.com/gcalderone/Gnuplot.jl/blob/master/src/recipes.jl). +## Explicit recipe (example) + +To generate a plot using the data contained in a `DataFrame` object we need, beside the data itself, the name of the columns to use for the X and Y coordinates. The following example shows how to implement an explicit recipe to plot a `DataFrame` object: +```@example abc +using RDatasets, DataFrames, Gnuplot +import Gnuplot: PlotElement, DatasetText + +function plotdf(df::DataFrame, colx::Symbol, coly::Symbol; group=nothing) + if isnothing(group) + return PlotElement(data=DatasetText(df[:, colx], df[:, coly]), + plot="w p notit", + xlab=string(colx), ylab=string(coly)) + end + + out = Vector{Gnuplot.PlotElement}() + push!(out, PlotElement(;xlab=string(colx), ylab=string(coly))) + for g in sort(unique(df[:, group])) + i = findall(df[:, group] .== g) + if length(i) > 0 + push!(out, PlotElement(data=DatasetText(df[i, colx], df[i, coly]), + plot="w p t '$g'")) + end + end + return out +end + +# Load a DataFrame and convert it to a PlotElement +iris = dataset("datasets", "iris") +@gp plotdf(iris, :SepalLength, :SepalWidth, group=:Species) +saveas("recipes001") # hide +``` +![](assets/recipes001.png) -. The fields of the `PlotElements` structure are: -- `mid::Int`:: multiplot ID; -- `cmds::Vector{String}`: commands to set plot properties; -- `data::Vector{Dataset}`: data set(s); -- `plot::Vector{String}`: plot specifications for each `Dataset`; - -where `Dataset` is an abstract type, the actual data sets are stored in the form of either a `DatasetText` object (a textual representation of the data) or a `DatasetBin` object (a binary file). Both `DatasetText` and `DatasetBin` structures provide a number of constructors accepting several types of input data. - - - - - -As anticipated, a recipe can be explicitly called by the user and the output passed to `@gp` or `@gsp`. - -All arguments to `@gp` or `@gsp` (except `Int`s, `String`s, `Tuple`s, `Array` of both numbers and strings) are scanned to check if an implicit recipe exists to handle them, and in this case it is - -Although a recipe provides a very efficient mean for data exploration, ## Histogram recipes +The object returned by the [`hist()`](@ref) function can be readily visualized by means of implicit recipes defined on the `Gnuplot.Histogram1D` and `Gnuplot.Histogram2D` types: + +```@example abc +x = randn(1000); +@gp hist(x) +saveas("recipes002") # hide +``` +![](assets/recipes002.png) + + +```@example abc +x = randn(1000); +y = randn(1000); +@gp hist(x, y) +saveas("recipes002a") # hide +``` +![](assets/recipes002a.png) + + + ## Image recipes -If the orientation is not the correct one you may adjust it with the gnuplot `rotate=` keyword (the following example requires the `TestImages` package to be installed): + +The **Gnuplot.jl** package provides implicit recipes to display images in the following formats: +- `Matrix{ColorTypes.RGB{T}}`; +- `Matrix{ColorTypes.RGBA{T}}` +- `Matrix{ColorTypes.Gray{T}}`; +- `Matrix{ColorTypes.GrayA{T}}`; + +To use these recipes simply pass an image to `@gp`, e.g.: ```@example abc using TestImages img = testimage("lighthouse"); @@ -61,13 +104,21 @@ saveas("recipes007b") # hide ![](assets/recipes007b.png) -To display a gray image use `with image` in place of `with rgbimage`, e.g.: +All such recipes are defined as: +```julia +function recipe(M::Matrix{ColorTypes.RGB{T}}, opt="flipy") + ... +end +``` +with only one mandatory argument. In order to exploit the optional keyword we can explicitly invoke the recipe as follows: ```@example abc img = testimage("walkbridge"); -@gp palette(:viridis) recipe(img, "flipy rot=15deg") +@gp palette(:gray) recipe(img, "flipy rot=15deg") saveas("recipes007c") # hide ``` ![](assets/recipes007c.png) -Note that we used a custom palette (`:lapaz`, see [Palettes and line types](@ref)) and the rotation angle has been expressed in radians (`-0.5pi`). +Note that we used both a palette (`:gray`, see [Palettes and line types](@ref)) and a custom rotation angle. + +The `flipy` option is necessary for proper visualization (see discussion in [Plot matrix as images](@ref)). diff --git a/src/Gnuplot.jl b/src/Gnuplot.jl index 1da4164..0abe55f 100644 --- a/src/Gnuplot.jl +++ b/src/Gnuplot.jl @@ -12,6 +12,7 @@ export session_names, dataset_names, palette_names, linetypes, palette, stats, @gp, @gsp, save, gpexec, boxxyerror, contourlines, hist, recipe, gpvars, gpmargins, gpranges + # ╭───────────────────────────────────────────────────────────────────╮ # │ TYPE DEFINITIONS │ # │ User data representation │ @@ -1408,7 +1409,7 @@ The `@gp` macro, and its companion `@gsp` for 3D plots, allows to send data and All Keyword names can be abbreviated as long as the resulting name is unambiguous. E.g. you can use `xr=[1,10]` in place of `xrange=[1,10]`. -- a `PlotElement` object is expanded in and its fields processed as one of the previous arguments; +- a `PlotElement` object is expanded in its fields and processed as one of the previous arguments; - any other data type is processed through an implicit recipe. If a suitable recipe do not exists an error is raised. """ diff --git a/src/recipes.jl b/src/recipes.jl index ac5bcea..8d60c0a 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -5,7 +5,10 @@ # -------------------------------------------------------------------- # Histograms """ - recipe() + recipe(h::Histogram1D) + recipe(h::Histogram2D) + +Implicit recipes to visualize 1D and 2D histograms. """ recipe(h::Histogram1D) = PlotElement(cmds="set grid", @@ -20,6 +23,14 @@ recipe(h::Histogram2D) = # -------------------------------------------------------------------- # Images +""" + recipe(M::Matrix{ColorTypes.RGB{T}}, opt="flipy") + recipe(M::Matrix{ColorTypes.RGBA{T}}, opt="flipy") + recipe(M::Matrix{ColorTypes.Gray{T}}, opt="flipy") + recipe(M::Matrix{ColorTypes.GrayA{T}}, opt="flipy") + +Implicit recipes to show images. +""" recipe(M::Matrix{ColorTypes.RGB{T}}, opt="flipy") where T = PlotElement(cmds=["set autoscale fix", "set size ratio -1"], data=DatasetBin(256 .* getfield.(M, :r), @@ -43,32 +54,3 @@ recipe(M::Matrix{ColorTypes.GrayA{T}}, opt="flipy") where T = PlotElement(cmds=["set autoscale fix", "set size ratio -1"], data=DatasetBin(256 .* getfield.(M, :val)), plot="$opt with image notit") - - -# ╭───────────────────────────────────────────────────────────────────╮ -# │ EXPLICIT RECIPES │ -# ╰───────────────────────────────────────────────────────────────────╯ - -macro recipes_DataFrames() - return esc(:( - function plotdf(df::DataFrame, colx::Symbol, coly::Symbol; group=nothing); - if isnothing(group); - return Gnuplot.PlotElement(xlab=string(colx), ylab=string(coly), - data=Gnuplot.DatasetText(df[:, colx], df[:, coly]), - plot="w p notit"); - end; - - data = Vector{Gnuplot.Dataset}(); - plot = Vector{String}(); - for g in sort(unique(df[:, group])); - i = findall(df[:, group] .== g); - if length(i) > 0; - push!(data, Gnuplot.DatasetText(df[i, colx], df[i, coly])); - push!(plot, "w p t '$g'"); - end; - end; - return Gnuplot.PlotElement(xlab=string(colx), ylab=string(coly), - data=data, plot=plot); - end - )) -end