diff --git a/Project.toml b/Project.toml index 5a824dd..eb97335 100644 --- a/Project.toml +++ b/Project.toml @@ -43,10 +43,10 @@ TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" [compat] -AlgebraOfGraphics = "0.6, 0.7, 0.8" +AlgebraOfGraphics = "0.6, 0.7, 0.8, 0.9, 0.10" Arrow = "2" CSV = "0.10" -CairoMakie = "0.11, 0.12" +CairoMakie = "0.11 - 0.22" CategoricalArrays = "0.10" Chain = "0.5,0.6" DataAPI = "1" diff --git a/_quarto.yml b/_quarto.yml index e5684fc..2e7822b 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -5,9 +5,9 @@ project: - "/fonts/*" book: - title: "Embrace Uncertainty" + title: "Embrace Uncertainty (Preview)" subtitle: "Mixed-effects models with Julia" - author: + author: - name: Phillip Alday email: me@phillipalday.com orcid: 0000-0002-9984-5745 @@ -45,6 +45,7 @@ book: - datatables.qmd - linalg.qmd - aGHQ.qmd + - algebraofgraphics.qmd execute-dir: project @@ -62,7 +63,7 @@ format: html: # for more info, see # https://quarto.org/docs/output-formats/html-themes.html - theme: [sandstone, theme.scss] + theme: [cosmo, theme.scss] # sets the background of inline code and code output but not code blocks? # also overrides code-bg in the SCSS file # monobackgroundcolor: '#fafafa' @@ -70,7 +71,7 @@ format: engine: julia julia: - exeflags: + exeflags: - --project - -tauto diff --git a/algebraofgraphics.qmd b/algebraofgraphics.qmd new file mode 100644 index 0000000..38ece30 --- /dev/null +++ b/algebraofgraphics.qmd @@ -0,0 +1,170 @@ +--- +fig-width: 4 +fig-height: 3 +fig-dpi: 150 +fig-format: png +engine: julia +execute: + cache: true +julia: + exeflags: ["--project"] +--- + +# Algebra of Graphics {#sec-aog} + +In this appendix, we give a demonstration of and introduction to the [`AlgebraOfGraphics`](https://aog.makie.org/) package and plotting system. +The key concepts in `AlgebraOfGraphics` (or AoG for short) are layers, the `+` operator, and the `*` operator. +Each layer in an AoG plot consists of at least (1) a mapping from the data to the plot elements (such as the x-axis, y-axis, and color), and (2) a visual that defines the type of plot (for example, scatterplot or lines), which are combined using the `*` operator. +The mapping is analogous to the "aesthetic mapping" in the grammar-of-graphics based [`ggplot2`](https://ggplot2.tidyverse.org) package in `R` and the visual is the same as a "geom". +While the `+` operator allows us to stack layers together, the `*` operator allows us to both construct a layer and modify multiple layers at once. +Once we have constructed the layers of a plot, we can display or print the plot using the `draw` function. +Additionally, the draw function allows us to apply modifications that do not depend on the data to common elements in the plot. +While this may all sound very abstract we will see these concepts in action shortly. + +## A plot with a single layer + +To illustrate the principles briefly explained above, we plot the `elstongrizzle` dataset in multiple ways building up to the plots in @sec-longitudinal. +We start off we first plot the length of Ramus bone against time for all subjects. +To do so we first load all the relevant packages, + +```{julia} +#| code-fold: true +#| output: false +#| label: app-aog-packages +using AlgebraOfGraphics +using CairoMakie +using DataFrames, DataFrameMacros +using GLM # for the lm needed to arrange the final plot +using EmbraceUncertainty: dataset +``` +and the dataset: + +```{julia} +#| code-fold: true +#| output: false +#| label: app-aog-data +elstongrizzle = DataFrame(dataset(:elstongrizzle)) +``` + +The scatter-plot is defined by the mapping from the data to the $x$ and $y$ axis of the plots. + +```{julia} +ramusscatter = data(elstongrizzle) * mapping(:time, :resp) * visual(Scatter) +``` + + +```{julia} +draw(ramusscatter) +``` + +This plot is not very useful because the points for all the subjects are mixed in together. +To distinguish data for different subjects, let's color the data points by subject. +This means that we must map the subject ID (`Subj`) to the `color` attribute of the plot. + +```{julia} +draw(ramusscatter*mapping(color = :Subj)) +``` + +Although this worked, the more concise way to achieve the same thing is to define all the mappings at once as follows: +```{julia} +egscatter = data(elstongrizzle) * + mapping(:time, :resp, color = :Subj) * + visual(Scatter) +draw(egscatter) +``` + + +## Adding another layer + +To construct a layer for lines, we can use the `visual(Lines)`: +```{julia} +eglines = data(elstongrizzle) * + mapping(:time, :resp, color = :Subj) * + visual(Lines) + +draw(eglines) +``` + +We can now combine the layers by simply using `+`! + +```{julia} +draw(egscatter + eglines) +``` + +You may have noticed that the mapping from the data to the plot elements is the same for both layers is the same. +This is where the power of the algebra comes in. +We can define two layers with the same mapping using the following code: +```{julia} +egscatterlines = data(elstongrizzle) * + mapping(:time, :resp, color = :Subj) * + (visual(Scatter) + visual(Lines)) +``` +Let's unpack this. +We can define two partial layers with `visual(Scatter) + visual(Lines)` and both are defined with the common mapping, which we can achieve by modifying, i.e. using the `*` operator on the stack of two (partial) layers. +To confirm if this actually works, we can just invoke `draw`: +```{julia} +draw(egscatterlines) +``` +and _voila_ we just created two layers at once! + + +## Modifying common elements with `draw` + +One issue with the plot above is that the legend is far too big to fit vertically on the side. +So let's try putting at the bottom of the plot. +This is an action that is not related to any single layer, and can be modified using optional arguments to the draw method as follows: +```{julia} +draw( + egscatterlines; + legend = (; position = :bottom, titleposition = :left) +) +``` + +Note that we reused the object `egscatterlines` which is a stack of layers from before. +While this is better than before, the individual subject names are not really relevant to this plot, so let's remove the legend altogether. + +:::{.callout-note} +**TODO:** How to explain why legend is disabled in the `scales`? +::: + +```{julia} +draw( + egscatterlines, + scales(Color = (; legend = false)) +) +``` + +The `scales` argument can also be used to modify other features of plot elements. +To demonstrate this, we change the color palette used for plotting to `darktest` color palette (See [`Makie.jl` documentation](https://docs.makie.org/stable/explanations/colors#misc)). + +```{julia} +#| label: fig-scatterline-dartest +#| fig-cap: Scatter-plot with overlaid lines using the `darktest` colorscheme +draw( + egscatterlines, + scales(Color = (; legend = false, palette = :darktest)) +) +``` + +## Faceting + +@fig-scatterline-dartest is still crowded, and one way to fix this would be to plot each subject in a separate panel. +In `ggplot2` this is achieved by using `facet`. +Since we want to facet by `Subj`, we need to map a feature of the data (`Subj`) to an element of the plot. +Thus, we must define some sort of mapping to achieve this. +In this case, we want to use the equivalent of `ggplot2::facet_wrap`, which is the `layout` element of the feature. Similar to mapping the subject ID to color, we can map `Subj` to `layout` as follows: +```{julia} +draw( + egscatterlines * mapping(layout = :Subj), + scales(Color = (; legend = false, palette = :darktest)); + figure = (; size = (800, 400)) +) +``` + +It is also possible to facet by two features, one along the horizontal direction and one along the vertical direction using the `col` and `row` (this would be akin to `ggplot2::facet_grid`) keyword arguments in the mapping. + +:::{.callout-note} + +**TODO:** use the `layout` keyword mapping to create facets by subject instead of colors by subject + +::: \ No newline at end of file diff --git a/intro.qmd b/intro.qmd index ea9725a..8d81c7f 100644 --- a/intro.qmd +++ b/intro.qmd @@ -4,6 +4,8 @@ fig-height: 3 fig-dpi: 150 fig-format: png engine: julia +execute: + cache: true julia: exeflags: ["--project"] --- @@ -239,7 +241,7 @@ For example, four of the five preparations from batch F produced lower yields th This plot, and essentially all the other plots in this book, were created using the [Makie](https://makie.juliaplots.io) package [@DanischKrumbiegel2021]. -In `yet-to-be-written-appendix` we review some of the principles of data graphics, such as reordering the levels of the factor by increasing mean response, that enhance the informativeness of the plot. +In @sec-aog we review some of the principles of data graphics, such as reordering the levels of the factor by increasing mean response, that enhance the informativeness of the plot. For example, in this plot the levels of `batch` are sorted by increasing mean yield, to make visual comparisons between batches easier, and the vertical positions are *jittered* to avoid overplotting of points. (Note that the two lowest yields of samples from batch `A` are identical.) @@ -675,13 +677,6 @@ The apparent distribution of the estimates of $\sigma_1$ in @fig-dsm01_bs_sigma_ A [kernel density estimate](https://en.wikipedia.org/wiki/Kernel_density_estimation) approximates a probability density from a finite sample by blurring or smearing the positions of the sample values according to a *kernel* such as a narrow Gaussian distribution (see the linked article for details). In this case the distribution of the estimates is a combination of a continuous distribution and a spike or point mass at zero as shown in a histogram, @fig-dsm01_bs_sigma_hist. -:::{.callout-note collapse="true"} - -### Adjust the alpha in multiple histograms - -Use a lower alpha in the colors for multiple histograms so the bars behind another color are more visible -::: - ```{julia} #| code-fold: true #| fig-cap: Histogram of bootstrap variance-components as standard deviations from model dsm01 @@ -693,7 +688,7 @@ draw( :value => "Bootstrap parameter estimates of σ"; color=(:group => "Group"), ) * - AlgebraOfGraphics.histogram(; bins=80); + AlgebraOfGraphics.histogram(; bins=80) * visual(alpha = 0.4); figure=(; size=(600, 340)), ) ``` @@ -724,7 +719,7 @@ draw( :value_abs2 => "Bootstrap sample of estimates of σ²", color=:group, ) * - AlgebraOfGraphics.histogram(; bins=200); + AlgebraOfGraphics.histogram(; bins=200) * visual(alpha = 0.4); figure=(; size=(600, 340)), ) ```