Author: John Quensen
Date: 2024-01-14

# Modifying Plots Made with ggordiplots

#### John Quensen

#### 2024-01-14

# Introduction

To enable further customization, all functions in this package return silently a list of the data frames used for the plots and the plots themselves These may be captured by assigning the function result to a variable. For `gg_ordiplot`, for example, get a list of the names of the items returned:

    suppressPackageStartupMessages(library(vegan))
    suppressPackageStartupMessages(library(ggplot2))
    suppressPackageStartupMessages(library(ggordiplots))
    data("dune")
    data("dune.env")
    dune.hel <- decostand(dune, method = "hellinger")
    ord <- rda(dune.hel)
    my.plot <- gg_ordiplot(ord, groups = dune.env$Management, hull = TRUE, spiders = TRUE,
        ellipse = FALSE, plot = FALSE)
    names(my.plot)

    ## [1] "df_ord"      "df_mean.ord" "df_ellipse"  "df_hull"     "df_spiders"
    ## [6] "plot"

The last object in the list is the plot itself. You can capture the plot and make modifications by adding `ggplot2` labels and themes:

    a.plot <- my.plot$plot
    a.plot + theme_bw() + labs(color = "Management", x = "PCA 1", y = "PCA 2", title = "My Title") +
        theme(plot.title = element_text(hjust = 0.5))  # centers main title, ggplot2 version 2.2+

The other items in the list are the data frames used for the plot layers:

Item | Description
--- | ---
df_ord | Point coordinates for ordination with Group variable
df_mean.ord | Label coordinates
df_ellipse | Data for plotting ellipses
df_hull | Data for plotting hulls
df_spiders | Data for plotting spiders

You can check the first few lines of each data frame thus:

    head(my.plot$df_spiders)

    ##    Group    cntr.x       cntr.y         x           y
    ## 2     BF 0.1995202 -0.009619176 0.1970059  0.16455540
    ## 10    BF 0.1995202 -0.009619176 0.2505024 -0.06096196
    ## 11    BF 0.1995202 -0.009619176 0.1510522 -0.13245097
    ## 5     HF 0.1070437  0.041316511 0.2532165 -0.02333147
    ## 6     HF 0.1070437  0.041316511 0.2152557 -0.09089893
    ## 7     HF 0.1070437  0.041316511 0.2497939 -0.03067965

The `ggplot2` statements I used for each layer are:

    # Basic ordination plot:
    xlab <- paste("Axis", choices[1], sep = " ")
    ylab <- paste("Axis", choices[2], sep = " ")
    geom_point(data = df_ord, aes(x = x, y = y, color = Group), size = 3) + xlab(xlab) +
        ylab(ylab)

    # Plot ellipses:
    geom_path(data = df_ellipse, aes(x = x, y = y, color = Group), show.legend = FALSE)

    # Plot centroid labels:
    geom_text(data = df_mean.ord, aes(x = x, y = y, label = Group, color = Group), show.legend = FALSE)

    # Plot hulls:
    geom_path(data = df_hull, aes(x = x, y = y, color = Group), show.legend = FALSE)

    # Plot spiders:
    geom_segment(data = df_spiders, aes(x = cntr.x, xend = x, y = cntr.y, yend = y, color = Group),
        show.legend = FALSE)

    # Plot cluster segments:
    geom_segment(data = df_segments, aes(x = x, y = y, xend = xend, yend = yend))

With the above information and some knowledge of `ggplot2` you can modify the plots however you wish. And to learn more, you can inspect the code by entering `gg_ordiplot` or any other function written in R (i.e. not compiled) without parentheses. As a short example, the hidden function in `vegan` for generating data for an ellipse is:

    vegan:::veganCovEllipse

    ## function (cov, center = c(0, 0), scale = 1, npoints = 100)
    ## {
    ##     theta <- (0:npoints) * 2 * pi/npoints
    ##     Circle <- cbind(cos(theta), sin(theta))
    ##     Q <- chol(cov, pivot = TRUE)
    ##     o <- attr(Q, "pivot")
    ##     t(center + scale * t(Circle %*% Q[, o]))
    ## }
    ## <bytecode: 0x0000027a95ab1b50>
    ## <environment: namespace:vegan>

# Examples

Say you want to make an ordination plot with points distinguished by two variables assigned to symbol shape and symbol color. You can do this by extracting `df_ord` from a plot result and adding a second grouping variable. Continuing with the example from above:

    ord.data <- my.plot$df_ord
    head(ord.data)

    ##            x           y Group
    ## 1 0.23409791  0.19110003    SF
    ## 2 0.19700590  0.16455540    BF
    ## 3 0.04472727  0.24679380    SF
    ## 4 0.02267027  0.22032168    SF
    ## 5 0.25321647 -0.02333147    HF
    ## 6 0.21525573 -0.09089893    HF

    ord.data$Use <- dune.env$Use
    colnames(ord.data) <- c("x", "y", "Management", "Use")
    head(ord.data)

    ##            x           y Management      Use
    ## 1 0.23409791  0.19110003         SF Haypastu
    ## 2 0.19700590  0.16455540         BF Haypastu
    ## 3 0.04472727  0.24679380         SF Haypastu
    ## 4 0.02267027  0.22032168         SF Haypastu
    ## 5 0.25321647 -0.02333147         HF Hayfield
    ## 6 0.21525573 -0.09089893         HF Haypastu

    ggplot(data = ord.data, aes(x = x, y = y, color = Management, shape = Use)) + geom_point(size = 3) +
        xlab("PCA 1") + ylab("PCA 2")

    ## Warning: Using shapes for an ordinal variable is not advised

As long as you start from the same data, you can take plots and data frames generated with different functions and combine them in new ways. For example, add a dendrogram to an ellipse plot.

    data("dune")
    data("dune.env")
    dune.bray <- vegdist(dune, method = "bray")
    ord <- cmdscale(dune.bray, k = nrow(dune) - 1, eig = TRUE, add = TRUE)

    ## Warning in cmdscale(dune.bray, k = nrow(dune) - 1, eig = TRUE, add = TRUE):
    ## only 18 of the first 19 eigenvalues are > 0

    cl <- hclust(dune.bray, method = "single")
    clstr.plot <- gg_ordicluster(ord, cluster = cl, plot = FALSE)
    ellipse.plot <- gg_ordiplot(ord, groups = dune.env$Management, plot = FALSE)
    ellipse.plot$plot + geom_segment(data = clstr.plot$df_segments, aes(x = x, y = y,
        xend = xend, yend = yend))

Enjoy!