| Title: | Animated Biplots |
|---|---|
| Description: | Create animated biplots that enables dynamic visualisation of temporal or sequential changes in multivariate data by animating a single biplot across the levels of a time variable. It builds on objects from the 'biplotEZ' package, Lubbe S, le Roux N, Nienkemper-Swanepoel J, Ganey R, Buys R, Adams Z, Manefeldt P (2024) <doi:10.32614/CRAN.package.biplotEZ>, allowing users to create animated biplots that reveal how both samples and variables evolve over time. |
| Authors: | Raeesa Ganey [aut, cre, cph] (ORCID: <https://orcid.org/0009-0008-6973-0999>), Johané Nienkemper-Swanepoel [aut, cph] (ORCID: <https://orcid.org/0000-0001-6086-8272>) |
| Maintainer: | Raeesa Ganey <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.3.1 |
| Built: | 2026-10-15 12:00:57 UTC |
| Source: | https://github.com/muvisu/moveez |
Calibrate axis
.calibrate.axis( j, Xhat, means, sd, axes.rows, ax.which, ax.tickvec, ax.orthogxvec, ax.orthogyvec ).calibrate.axis( j, Xhat, means, sd, axes.rows, ax.which, ax.tickvec, ax.orthogxvec, ax.orthogyvec )
j |
j |
Xhat |
Xhat |
means |
means |
sd |
sd |
axes.rows |
axes.rows |
ax.which |
ax.which |
ax.tickvec |
ax.tickvec |
ax.orthogxvec |
ax.orthogxvec |
ax.orthogyvec |
ax.orothogyvec |
Calibrated axes
Data extracted from ERA5 hourly data on single levels from 1940 to present
A dataset with 960 observations and 9 variables.
8 years from 1950 to 2020
12 calendar months
10 IPCC climate reference regions
Accumulated precipitation
Daily evaporation
Mean temperature
Soil moisture
6-month standardised precipitation index
Windspeed
DOI: 10.24381/cds.adbb2d47 (Accessed on 11-02-2025)
Data extracted from ERA5 hourly data on single levels for 1989
A dataset with 120 observations and 9 variables.
8 years from 1950 to 2020
12 calendar months
10 IPCC climate reference regions
Accumulated precipitation
Daily evaporation
Mean temperature
Soil moisture
6-month standardised precipitation index
Windspeed
DOI: 10.24381/cds.adbb2d47 (Accessed on 11-02-2025)
Provide axes coordinates
axes_moveEZ(bp, which.var)axes_moveEZ(bp, which.var)
bp |
Object |
which.var |
which variable(s) to find coordinates |
Axes coordinates
Validates time.var and group.var before any
plotting is attempted, so that unsuitable input stops with an informative
error instead of failing downstream. Both must name factor columns of the
data supplied to biplotEZ::biplot(), without missing values.
check_vars_moveEZ(bp, time.var, group.var)check_vars_moveEZ(bp, time.var, group.var)
bp |
biplot object from biplotEZ |
time.var |
time variable |
group.var |
group variable |
bp, with unused levels of time.var dropped from
bp$raw.X.
Computes the convex hull of each group for a single level of the time variable. A hull needs at least three non-collinear observations; groups that do not meet this are left out of the hull data and returned separately so that they can be drawn as points instead.
chull_moveEZ(Y, group.var, group_levels)chull_moveEZ(Y, group.var, group_levels)
Y |
tibble of sample coordinates (columns V1, V2) and grouping columns |
group.var |
group variable |
group_levels |
levels of group.var to construct hulls for |
A list with hull, the rows of Y that form the hull
vertices of each group, and points, the rows of Y for groups
with too few observations to construct a hull.
This function calculates measures of comparison after generalised orthogonal Procrustes Analysis
is performed in moveplot3. Orthogonal Procrustes Analysis is used to compare a target to a testee configuration.
The following measures are calculate: Procrustes Statistic (PS), Congruence Coefficient (CC), Absolute Mean Bias (AMB),
Mean Bias (MB) and Root Mean Squared Bias (RMSB).
evaluation(bp, centring = TRUE)evaluation(bp, centring = TRUE)
bp |
biplot object from |
centring |
logical argument to apply centring or not (default is |
For moveplot, the function evaluates whether the fixed frame of moveplot(), where the variable
vectors are computed once on all the data, is adequate, or whether a biplot should be constructed for each time
slice with moveplot2() or moveplot3(). A PCA (or CVA) biplot is fitted to each level of the time
variable and compared with the biplot of all the data, see Details.
For moveplot, the following are computed for each level of the time variable, in the order of its levels:
the proportion of variance explained by the first dimension of the biplot of that time slice,
the largest principal angle (in degrees) between the two dimensional subspace of the biplot of that time slice and that of the biplot of all the data. Zero means that the time slice spans the same plane,
the loadings of the variables on the first dimension. The sign of each eigenvector is arbitrary, so the
dimensions of each time slice are reflected to point in the same direction as those of the biplot of all the
data (as auto.align in moveplot2()) before the loadings are compared.
A Spearman rank correlation test between the order of the time levels and each of these quantities tests for a
monotone drift over time. A p-value of 0.05 or more fails to reject zero correlation: there is no evidence that
the structure of the biplot changes over time, the fixed frame of moveplot() is adequate, and
moveplot2() or moveplot3() are not needed. A p-value below 0.05 indicates a drift, and a biplot
should be constructed for each time slice. The test only detects monotone trends; large but non-monotone
changes are visible in the plots.
eval.tab |
Returns a table of the measures of comparison for each level of the time variable compared to the target. |
fit.plot |
Returns a line plot with the fit measures that are bounded between zero and one: PS and CC. A small PS value and large CC value indicate good fit. |
bias.plot |
Returns a line plot with bias measures that are unbounded: AMB, MB and RMSB. Small values indicate low bias. |
structure |
For |
var.plot |
For |
angle.plot |
For |
loadings.plot |
For |
drift.tab |
For |
data(Africa_climate) data(Africa_climate_target) bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = NULL) |> evaluation() results$eval.tab results$fit.plot results$bias.plot data(Africa_climate) data(Africa_climate_target) bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = Africa_climate_target) |> evaluation() results$eval.tab results$fit.plot results$bias.plot # Should a biplot be constructed for each time slice? bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) |> evaluation() results$drift.tab results$var.plot results$angle.plot results$loadings.plotdata(Africa_climate) data(Africa_climate_target) bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = NULL) |> evaluation() results$eval.tab results$fit.plot results$bias.plot data(Africa_climate) data(Africa_climate_target) bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = Africa_climate_target) |> evaluation() results$eval.tab results$fit.plot results$bias.plot # Should a biplot be constructed for each time slice? bp <- biplotEZ::biplot(Africa_climate, scaled = TRUE) |> biplotEZ::PCA() results <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) |> evaluation() results$drift.tab results$var.plot results$angle.plot results$loadings.plot
Create animated biplot on samples in a biplot
moveplot( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, shadow = FALSE, which = NULL, label.vars = NULL )moveplot( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, shadow = FALSE, which = NULL, label.vars = NULL )
bp |
biplot object from biplotEZ |
time.var |
name of the time variable, given as a character string. Must be a factor column of the data
supplied to |
group.var |
name of the group variable, given as a character string. Must be a factor column of the
data supplied to |
move |
whether to animate (TRUE) or facet (FALSE) samples, according to time.var |
hulls |
whether to display sample points or convex hulls. A hull requires at least three
non-collinear observations per group per level of |
scale.var |
scaling the vectors representing the variables |
shadow |
whether the animation will keep past states (only when hulls = FALSE) |
which |
integer index into the levels of group.var selecting which groups to display. Default (NULL) shows all groups. |
label.vars |
character vector of column name(s) used to label sample points, pasted together when more than one is supplied. Default (NULL) omits point labels. |
time.var and group.var must both be factors. biplot() treats every numeric
column as a variable of the biplot, so a time variable stored as a number (e.g. an integer year)
has to be converted with factor() before biplot() is called, not afterwards.
Missing values are handled by biplot(), which removes every row containing an NA
in any column (including time.var and group.var) with a warning. The plot is
constructed from the remaining rows.
bp |
Returns the elements of the biplot object |
plot |
An animated or a facet of biplots based on the dynamic frame. |
time.var, group.var
|
The names of the time and group variables, used by |
data(Africa_climate) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() # Convex hulls facet plot bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) # Samples facet plot bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE) # Specifying colours with colour palette in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(col = RColorBrewer::brewer.pal(10, "Paired")) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE)} # Specifying plotting characters for grouping variable in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(pch = c(19, 21, 3)) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE)} # Specifying opacity of plotting characters and size of variable lables if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(opacity = 0.4) |> axes(label.cex = 1.2) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE)} # Specifying colours manually in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(col = c("firebrick4", "indianred3", "tomato", "sandybrown", "khaki1", "palegreen1", "darkseagreen2", "mediumaquamarine", "deepskyblue4", "mediumpurple4")) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE)} # Extracting measures of fit of PCA bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() |> fit.measures() bp <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity # Convex hulls move plot if(interactive()) { bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE)} # Samples move plot with shadows if(interactive()) { bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = TRUE, shadow = TRUE)} # CVA biplot bp <- biplot(Africa_climate, scaled = TRUE) |> CVA(class = Africa_climate$Region) bp <- bp |> moveplot2(group.var = "Region", time.var = "Year", move = FALSE) # Extracting measures of fit of CVA bp <- biplot(Africa_climate, scaled = TRUE) |> CVA(classes = Africa_climate$Region) |> fit.measures() bp <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity bp$within.class.axis.predictivity bp$within.class.sample.predictivitydata(Africa_climate) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() # Convex hulls facet plot bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) # Samples facet plot bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE) # Specifying colours with colour palette in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(col = RColorBrewer::brewer.pal(10, "Paired")) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE)} # Specifying plotting characters for grouping variable in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(pch = c(19, 21, 3)) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE)} # Specifying opacity of plotting characters and size of variable lables if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(opacity = 0.4) |> axes(label.cex = 1.2) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = FALSE)} # Specifying colours manually in biplotEZ if(interactive()) { bp <- biplot(Africa_climate, scaled = TRUE, group.aes = Africa_climate$Region) |> PCA() |> samples(col = c("firebrick4", "indianred3", "tomato", "sandybrown", "khaki1", "palegreen1", "darkseagreen2", "mediumaquamarine", "deepskyblue4", "mediumpurple4")) bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE)} # Extracting measures of fit of PCA bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() |> fit.measures() bp <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity # Convex hulls move plot if(interactive()) { bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE)} # Samples move plot with shadows if(interactive()) { bp |> moveplot(time.var = "Year", group.var = "Region", hulls = FALSE, move = TRUE, shadow = TRUE)} # CVA biplot bp <- biplot(Africa_climate, scaled = TRUE) |> CVA(class = Africa_climate$Region) bp <- bp |> moveplot2(group.var = "Region", time.var = "Year", move = FALSE) # Extracting measures of fit of CVA bp <- biplot(Africa_climate, scaled = TRUE) |> CVA(classes = Africa_climate$Region) |> fit.measures() bp <- bp |> moveplot(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity bp$within.class.axis.predictivity bp$within.class.sample.predictivity
Create animated biplot on samples and variables in a biplot
moveplot2( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, auto.align = TRUE, align.time = NA, reflect = NA )moveplot2( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, auto.align = TRUE, align.time = NA, reflect = NA )
bp |
biplot object from biplotEZ |
time.var |
name of the time variable, given as a character string. Must be a factor column of the data
supplied to |
group.var |
name of the group variable, given as a character string. Must be a factor column of the
data supplied to |
move |
whether to animate (TRUE) or facet (FALSE) samples and variables, according to time.var |
hulls |
whether to display sample points or convex hulls. A hull requires at least three
non-collinear observations per group per level of |
scale.var |
scaling the vectors representing the variables |
auto.align |
whether to align the sign of the biplot axes of every time slice to the biplot of all the data
( |
align.time |
a vector specifying the levels of time.var for which the biplots should be reflected, to override
|
reflect |
a character vector, of the same length as align.time, specifying the reflection to apply at each corresponding
time point in align.time. One of "x" to reverse the direction of the x-axis (horizontal flip), "y" to reverse the direction
of the y-axis (vertical flip) and "xy" for both. This follows |
time.var and group.var must both be factors. biplot() treats every numeric
column as a variable of the biplot, so a time variable stored as a number (e.g. an integer year)
has to be converted with factor() before biplot() is called, not afterwards.
The eigenvectors (PCA) or canonical vectors (CVA) of each time slice are only determined up to sign, so
the biplot of one time slice can be a reflection of that of the next, which makes comparisons over time
meaningless. With auto.align = TRUE each of the two dimensions of a time slice is reflected
whenever its inner product with the corresponding dimension of the biplot of all the data in bp
is negative. Reflections do not change the fit measures.
The sign of a dimension is decided by a small inner product when that dimension is nearly orthogonal to
the corresponding dimension of bp, and the automatic alignment may then not give the desired
orientation. align.time and reflect override it for the given levels of time.var.
Time slices whose dimensions swap order or rotate cannot be aligned by reflections, see
moveplot3() instead.
Missing values are handled by biplot(), which removes every row containing an NA
in any column (including time.var and group.var) with a warning. The plot is
constructed from the remaining rows.
bp |
Returns the elements of the biplot object |
plot |
An animated or a facet of biplots based on the dynamic frame. |
data(Africa_climate) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() if(interactive()) { bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE)} # Extracting measures of fit for PCA bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) # Manual alignment instead of auto.align, reflect the x-axis at 1950 bp2 <- biplot(Africa_climate, scaled = TRUE) |> PCA() |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, auto.align = FALSE, align.time = "1950", reflect = "x") bp$quality bp$axis.predictivity # Extracting measures of fit for CVA bp <- biplot(Africa_climate) |> CVA(classes = Africa_climate$Region) bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity bp$within.class.axis.predictivity bp$within.class.sample.predictivitydata(Africa_climate) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() if(interactive()) { bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE)} # Extracting measures of fit for PCA bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) # Manual alignment instead of auto.align, reflect the x-axis at 1950 bp2 <- biplot(Africa_climate, scaled = TRUE) |> PCA() |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, auto.align = FALSE, align.time = "1950", reflect = "x") bp$quality bp$axis.predictivity # Extracting measures of fit for CVA bp <- biplot(Africa_climate) |> CVA(classes = Africa_climate$Region) bp <- bp |> moveplot2(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE) bp$quality bp$axis.predictivity bp$within.class.axis.predictivity bp$within.class.sample.predictivity
Create animated biplot on samples and variables in a biplot with a given target
moveplot3( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, target = NULL )moveplot3( bp, time.var, group.var, move = TRUE, hulls = TRUE, scale.var = 5, target = NULL )
bp |
biplot object from biplotEZ |
time.var |
name of the time variable, given as a character string. Must be a factor column of the data
supplied to |
group.var |
name of the group variable, given as a character string. Must be a factor column of the
data supplied to |
move |
whether to animate (TRUE) or facet (FALSE) samples and variables, according to time.var |
hulls |
whether to display sample points or convex hulls. A hull requires at least three
non-collinear observations per group per level of |
scale.var |
scaling the vectors representing the variables |
target |
Target data set to which all biplots should be matched consisting of the the same dimensions. If not specified, the centroid of all available biplot sample coordinates from |
time.var and group.var must both be factors. biplot() treats every numeric
column as a variable of the biplot, so a time variable stored as a number (e.g. an integer year)
has to be converted with factor() before biplot() is called, not afterwards.
Missing values are handled by biplot(), which removes every row containing an NA
in any column (including time.var and group.var) with a warning. The plot is
constructed from the remaining rows. Since moveplot3() requires the same number of
observations at every level of time.var, removed rows will usually cause it to stop;
impute the missing values, or remove the affected samples at every time level, beforehand.
The same applies to target, which may not contain missing values.
bp |
Returns the elements of the biplot object |
iter_levels |
The levels of the time variable. |
coord_set |
The coordinates of the configurations before applying Generalised Orthogonal Procrustes Analysis. |
GPA_list |
The coordinates of the configurations after applying Generalised Orthogonal Procrustes Analysis. |
plot |
An animated or a facet of biplots based on the dynamic frame. |
data(Africa_climate) data(Africa_climate_target) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = NULL) if(interactive()) { bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE, target = NULL)} bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = Africa_climate_target)data(Africa_climate) data(Africa_climate_target) bp <- biplot(Africa_climate, scaled = TRUE) |> PCA() bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = NULL) if(interactive()) { bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = TRUE, target = NULL)} bp |> moveplot3(time.var = "Year", group.var = "Region", hulls = TRUE, move = FALSE, target = Africa_climate_target)