This is a more detailed version of the section, “Analytical Demonstrations,” within the Methods in Ecology and Evolution article, “RRPP: An R package for fitting linear models to high-dimensional data using residual randomization,” by Michael L. Collyer and Dean C. Adams. The purpose of this vignette is to offer an opportunity for people reading that article to see code and results together. Some changes to code have been made, especially to enhance features that are difficult to introduce in the article, but the overall presentation should be basically the same. As the package has been updated, this document has also been slightly edited to make sure any functional changes have been illustrated.
(Note parallel processing is available on Unix systems for lm.rrpp
; see lm.rrpp
help page for description on its use. This is helpful for large data sets.)
Three data sets are provided in RRPP
(with examples also in help pages), and are used here as examples for various analyses:
Pupfish (Collyer et al., 2015)
PupfishHeads (Gilbert, 2016)
PlethMorph (Adams & Collyer, 2018)
The first two datasets contain landmark-based geometric morphometric data collected from museum samples of Pecos pupfish (Cyprinodon pecosensis), representing body shape and cranial morphology, respectively. Within both data sets, the $coords
objects are matrices of Procrustes residuals obtained from generalized Procrustes analysis (GPA) of configurations of anatomical landmarks. For the purposes of this example, it is sufficient to recognize that Procrustes residuals embody a highly multivariate dataset representing shape (see the R package, geomorph). The third data set contains averaged linear measurements of 37 species of Plethodontid salamanders, plus a covariance matrix based on a Brownian model of evolution, given the phylogenetic relationship among the species (Adams & Collyer, 2018, for details).
Example: Pupfish Cranial Morphology and Mixed-Model ANOVA
In the first example we use a univariate dependent variable (head size, measured as the centroid size of the cranial landmark configuration; Bookstein, 1991) with a mixed-model design. The following code highlights the analytical steps, with results categorized in Fig. 1:
library(RRPP)
data("PupfishHeads")
PupfishHeads$logHeadSize <- log(PupfishHeads$headSize)
fit <- lm.rrpp(logHeadSize ~ sex + locality/year,
SS.type = "I", data = PupfishHeads,
print.progress = FALSE)
##
## Warning: Because variables in the linear model are redundant,
## the linear model design has been truncated (via QR decomposition).
## Original X columns: 13
## Final X columns (rank): 9
## Check coefficients or degrees of freedom in ANOVA to see changes.
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 268
## Number of dependent variables: 1
## Data space dimensions: 1
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Full Model Analysis of Variance
##
## Df Residual Df SS Residual SS Rsq F
## sex + locality/year 8 259 2.718175 10.94887 0.1988854 8.037445
## Z (from F) Pr(>F)
## sex + locality/year 3.984683 0.0006666667
##
##
## Redundancy Analysis (PCA on fitted values and residuals)
##
## Trace Proportion Rank
## Fitted 0.01018043 0.1988854 1
## Residuals 0.04100699 0.8011146 1
## Total 0.05118742 1.0000000 1
##
## Eigenvalues
##
## PC1
## Fitted 0.01018043
## Residuals 0.04100699
## Total 0.05118742
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## sex 1 0.6091 0.60911 0.04457 14.4087 1.7829 0.002 **
## locality 1 0.4712 0.47121 0.03448 11.1466 1.7174 0.002 **
## locality:year 6 1.6379 0.27298 0.11984 6.4574 3.1770 0.001 **
## Residuals 259 10.9489 0.04227 0.80111
## Total 267 13.6670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = logHeadSize ~ sex + locality/year, SS.type = "I",
## data = PupfishHeads, print.progress = FALSE)
Of important note, we choose to log-transform head size and include it as a separate variable in the RRPP data frame, PupfishHeads
. We accomplished this via the code above – rather than using log(headSize)
- because downstream functions like predict.lm.rrpp
work better without functions in the formula. ANOVA was performed using random distributions of F-statistics to calculate z-scores and P-values (but one could use alternative distributions - see the RRPP
help page). The S3 Generic functions (summary
, anova
) return summaries that remind the user how random data were generated, the type of SS, and how z-scores were calculated. This particular ANOVA summary is a default that fails to consider the year fish were sampled as a random effect. A mixed-model ANOVA update can be performed by changing the expected mean-square (MS) error estimates in each F calculation:
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## sex 1 0.6091 0.60911 0.04457 14.4087 1.7829 0.002 **
## locality 1 0.4712 0.47121 0.03448 1.7262 0.7066 0.234
## locality:year 6 1.6379 0.27298 0.11984 6.4574 3.1770 0.001 **
## Residuals 259 10.9489 0.04227 0.80111
## Total 267 13.6670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = logHeadSize ~ sex + locality/year, SS.type = "I",
## data = PupfishHeads, print.progress = FALSE)
This adjustment illustrates that the head size variation does not significantly differ between localities, with respect to the variation among sampling events. The anova function can also be used for multi-model inference.
fit.sex <- lm.rrpp(logHeadSize ~ sex,
data = PupfishHeads,
print.progress = FALSE)
fit.sex.loc<- lm.rrpp(logHeadSize ~ sex + locality,
data = PupfishHeads,
print.progress = FALSE)
fit.sex.loc.year<- lm.rrpp(logHeadSize ~ sex + locality/year,
data = PupfishHeads,
print.progress = FALSE)
##
## Warning: Because variables in the linear model are redundant,
## the linear model design has been truncated (via QR decomposition).
## Original X columns: 13
## Final X columns (rank): 9
## Check coefficients or degrees of freedom in ANOVA to see changes.
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Effect sizes (Z) based on F distributions
##
## ResDf Df RSS SS MS Rsq
## logHeadSize ~ sex (Null) 266 1 13.058 0.000000
## logHeadSize ~ sex + locality 265 1 12.587 0.47121 0.47121 0.034478
## logHeadSize ~ sex + locality/year 259 7 10.949 2.10907 0.30130 0.154318
## Total 267 13.667
## F Z P Pr(>F)
## logHeadSize ~ sex (Null)
## logHeadSize ~ sex + locality 9.9207 1.6623 0.004
## logHeadSize ~ sex + locality/year 7.1273 3.5804 0.001
## Total
One might wish to also look at individual model coefficients, and ascertain which have the largest effect:
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 268
## Number of dependent variables: 1
## Data space dimensions: 1
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Statistics (distances) of coefficients with 95 percent confidence intervals,
## effect sizes, and probabilities of exceeding observed values based on
## 1000 random permutations using RRPP
##
## d.obs UCL (95%) Zd Pr(>d)
## (Intercept) 3.12716415 2.9812637 4.4390761 0.001
## sexMale 0.09162777 0.1395840 -0.1271605 0.542
## localitySH 0.07571832 0.2054385 -0.3635292 0.610
## localitySH:year1998 0.06402169 0.1365264 0.2038021 0.355
## localityLake:year1999 0.35681331 0.1328685 7.1402070 0.001
## localitySH:year1999 0.01328613 0.1325900 -1.0171675 0.856
## localitySH:year2000 0.07165589 0.1062194 0.8807748 0.196
## localityLake:year2001 0.23035259 0.1391142 3.9136330 0.003
## localityLake:year2002 0.31050534 0.1347989 6.0278946 0.001
This function produces a table much like summary.lm
output, but with bootstrap-generated confidence intervals of coefficients.
It might be of interest to visualize model predictions for certain effects, holding constant other effects. For example, if we want to look at confidence intervals to compare male and female head sizes, holding constant the effects of locality and sampling period, we could do the following:
sizeDF <- data.frame(sex = c("Female", "Male"))
rownames(sizeDF) <- c("Female", "Male")
sizePreds <- predict(fit, sizeDF)
##
## Warning: Not all variables in model accounted for in newdata.
## Missing variables will be averaged from observed data for prediction.
The plots are perfectly amenable (e.g., point type and color, line thickness, alternative labels, and additional text can be added or adjusted with typical par
arguments).
## Warning in bxp(list(stats = structure(c(2.96731706108359, 2.96731706108359, :
## Duplicated argument pch = 21 is disregarded
Finally, the SS type can be also toggled easily by refitting the model:
fit2 <- lm.rrpp(logHeadSize ~ sex + locality/year,
SS.type = "II", data = PupfishHeads, print.progress = FALSE)
##
## Warning: Because variables in the linear model are redundant,
## the linear model design has been truncated (via QR decomposition).
## Original X columns: 13
## Final X columns (rank): 9
## Check coefficients or degrees of freedom in ANOVA to see changes.
fit3 <- lm.rrpp(logHeadSize ~ sex + locality/year,
SS.type = "III", data = PupfishHeads, print.progress = FALSE)
##
## Warning: Because variables in the linear model are redundant,
## the linear model design has been truncated (via QR decomposition).
## Original X columns: 13
## Final X columns (rank): 9
## Check coefficients or degrees of freedom in ANOVA to see changes.
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## sex 1 0.6091 0.60911 0.04457 14.4087 1.7829 0.002 **
## locality 1 0.4712 0.47121 0.03448 11.1466 1.7174 0.002 **
## locality:year 6 1.6379 0.27298 0.11984 6.4574 3.1770 0.001 **
## Residuals 259 10.9489 0.04227 0.80111
## Total 267 13.6670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = logHeadSize ~ sex + locality/year, SS.type = "I",
## data = PupfishHeads, print.progress = FALSE)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type II
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## sex 1 0.5554 0.55537 0.04064 13.1376 1.7527 0.002 **
## locality 1 0.4712 0.47121 0.03448 11.1466 1.7174 0.002 **
## locality:year 6 1.6379 0.27298 0.11984 6.4574 3.1770 0.001 **
## Residuals 259 10.9489 0.04227 0.80111
## Total 267 13.6670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = logHeadSize ~ sex + locality/year, SS.type = "II",
## data = PupfishHeads, print.progress = FALSE)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type III
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## sex 1 0.5554 0.55537 0.04064 13.1376 1.7527 0.002 **
## locality 1 0.0573 0.05733 0.00419 1.3562 0.6674 0.255
## locality:year 6 1.6379 0.27298 0.11984 6.4574 3.1770 0.001 **
## Residuals 259 10.9489 0.04227 0.80111
## Total 267 13.6670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = logHeadSize ~ sex + locality/year, SS.type = "III",
## data = PupfishHeads, print.progress = FALSE)
Example: Pupfish Body Shape and High-Dimensional Data
In the second example, we highlight the RRPP
ability to efficiently handle large data computations. For this demonstration, a 54(n) × 112 (p) matrix of Procrustes residuals are the data. In every one of the 1,000 random permutations, RRPP shuffles residual vectors the same way for four different null models, estimates coefficients for four different full models, estimates the SS as the difference between residual SS (RSS) for four null-full model comparisons, and calculates the total SS, before calculating MS, R2, F, Cohen’s f2, and Euclidean distances of coefficient vectors across all 1,000 permutations. This process, plus packaging of results, took approximately 0.5 seconds on a notebook computer, without any parallel processing. In the second example, the initial steps are quite the same as the first example:
data(Pupfish)
Pupfish$logSize <- log(Pupfish$CS)
fit <- lm.rrpp(coords ~ logSize + Sex*Pop, SS.type = "I",
data = Pupfish, print.progress = FALSE)
summary(fit, formula = FALSE)
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 54
## Number of dependent variables: 112
## Data space dimensions: 53
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Full Model Analysis of Variance
##
## Df Residual Df SS Residual SS Rsq F Z (from F)
## fit 4 49 0.03224913 0.02408373 0.5724746 16.40327 7.571691
## Pr(>F)
## fit 0.000625
##
##
## Redundancy Analysis (PCA on fitted values and residuals)
##
## Trace Proportion Rank
## Fitted 0.0006084742 0.5724746 4
## Residuals 0.0004544100 0.4275254 49
## Total 0.0010628843 1.0000000 53
##
## Eigenvalues
##
## PC1 PC2 PC3 PC4 PC5
## Fitted 0.0003849870 0.0001764119 0.0000308588 0.0000162165
## Residuals 0.0001619045 0.0000683959 0.0000504732 0.0000375901 0.0000204706
## Total 0.0004644118 0.0002541398 0.0000722379 0.0000549026 0.0000491286
## PC6 PC7 PC8 PC9 PC10
## Fitted
## Residuals 0.0000158227 0.0000145861 0.0000120066 0.0000083853 0.0000074828
## Total 0.0000333998 0.0000227294 0.0000187381 0.0000127668 0.0000090893
## PC11 PC12 PC13 PC14 PC15
## Fitted
## Residuals 0.0000068974 0.0000062331 0.0000054188 0.0000045485 0.0000043871
## Total 0.0000088769 0.0000083661 0.0000064036 0.0000058026 0.0000051742
## PC16 PC17 PC18 PC19 PC20
## Fitted
## Residuals 0.0000032928 0.0000031713 0.0000027759 0.0000024382 0.0000023968
## Total 0.0000046349 0.0000039689 0.0000033385 0.0000029455 0.0000027293
## PC21 PC22 PC23 PC24 PC25
## Fitted
## Residuals 0.0000020800 0.0000016410 0.0000013892 0.0000012122 0.0000010647
## Total 0.0000024512 0.0000020110 0.0000019767 0.0000013932 0.0000012220
## PC26 PC27 PC28 PC29 PC30
## Fitted
## Residuals 0.0000009268 0.0000008974 0.0000008083 0.0000007337 0.0000006036
## Total 0.0000010286 0.0000010076 0.0000009490 0.0000008017 0.0000006669
## PC31 PC32 PC33 PC34 PC35
## Fitted
## Residuals 0.0000005856 0.0000004746 0.0000004236 0.0000004042 0.0000003381
## Total 0.0000006208 0.0000005973 0.0000005198 0.0000004633 0.0000004385
## PC36 PC37 PC38 PC39 PC40
## Fitted
## Residuals 0.0000003127 0.0000002573 0.0000002486 0.0000002226 0.0000002059
## Total 0.0000003599 0.0000003568 0.0000003050 0.0000002580 0.0000002252
## PC41 PC42 PC43 PC44 PC45
## Fitted
## Residuals 0.0000001783 0.0000001509 0.0000001282 0.0000001152 0.0000000896
## Total 0.0000002162 0.0000001941 0.0000001842 0.0000001527 0.0000001461
## PC46 PC47 PC48 PC49 PC50
## Fitted
## Residuals 0.0000000737 0.0000000708 0.0000000488 0.0000000468
## Total 0.0000001100 0.0000000963 0.0000000850 0.0000000697 0.0000000563
## PC51 PC52 PC53
## Fitted
## Residuals
## Total 0.0000000525 0.0000000447 0.0000000395
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## logSize 1 0.014019 0.0140193 0.24886 28.5232 5.2763 0.001 **
## Sex 1 0.007615 0.0076151 0.13518 15.4933 5.1241 0.001 **
## Pop 1 0.007661 0.0076606 0.13599 15.5860 5.1072 0.001 **
## Sex:Pop 1 0.002954 0.0029542 0.05244 6.0105 3.7891 0.002 **
## Residuals 49 0.024084 0.0004915 0.42753
## Total 53 0.056333
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = coords ~ logSize + Sex * Pop, SS.type = "I", data = Pupfish,
## print.progress = FALSE)
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 54
## Number of dependent variables: 112
## Data space dimensions: 53
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Statistics (distances) of coefficients with 95 percent confidence intervals,
## effect sizes, and probabilities of exceeding observed values based on
## 1000 random permutations using RRPP
##
## d.obs UCL (95%) Zd Pr(>d)
## (Intercept) 1.10733195 1.02154943 14.760700 0.001
## logSize 0.11101234 0.04871922 8.147328 0.001
## SexM 0.02755503 0.01300605 7.672797 0.001
## PopSinkhole 0.02895656 0.01256411 8.646444 0.001
## SexM:PopSinkhole 0.03002431 0.01772916 5.715464 0.002
ANOVA results reveal that after accounting for body size allometry, not only are there significant inter-population differences in body shape and sexual dimorphism in body shape, but sexual dimorphism also significantly varies between the two populations. A fuller evaluation of these results is available in Collyer et al. (2015). It is worth taking a moment to realize why this analysis is a valuable alternative to a parametric M-ANOVA. The following code attempts to perform a parametric M-ANOVA:
fit$LM$data$coords <- Pupfish$coords
fit.par <- lm(fit$call$f1, data = fit$LM$data)
identical(fit$LM$coefficients, fit.par$coefficients)
## [1] TRUE
## Error in summary.manova(manova(fit.par)): residuals have rank 49 < 112
Although both functions return the same coefficients, the error summarizing the attempted parametric M-ANOVA clearly indicates the limitation of having residual degrees of freedom (rank) lower than the number of variables. Returning to the lm.rrpp
fit, which does not suffer this problem, we can look at the precision of group mean estimation, accounting for allometric shape variation, by doing the following:
shapeDF <- expand.grid(Sex = levels(Pupfish$Sex), Pop = levels(Pupfish$Pop))
rownames(shapeDF) <- paste(shapeDF$Sex, shapeDF$Pop, sep = ".")
shapePreds <- predict(fit, shapeDF, confidence = 0.95)
##
## Warning: Not all variables in model accounted for in newdata.
## Missing variables will be averaged from observed data for prediction.
plot(shapePreds, PC = TRUE, ellipse = TRUE,
pch = 19, col = 1:NROW(shapeDF)) # with added par arguments
These plots differ as there is a rotational difference between the covariance matrices estimated with 4 predicted and 54 fitted values. Additionally, the former illustrates prediction precision and the latter sample dispersion. Both functions allow passing par
arguments to the plot as well as saving plot data for more advanced plotting. The following is a regression-type plot:
plot(fit, type = "regression", reg.type = "PredLine",
predictor = Pupfish$logSize, pch=19,
col = as.numeric(groups))
The function, pairwise, can be used to test pairwise differences between least-squares means with:
PWT <- pairwise(fit, groups = interaction(Pupfish$Sex, Pupfish$Pop))
summary(PWT, confidence = 0.95)
##
## Pairwise comparisons
##
## Groups: F.Marsh M.Marsh F.Sinkhole M.Sinkhole
##
## RRPP: 1000 permutations
##
## LS means:
## Vectors hidden (use show.vectors = TRUE to view)
##
## Pairwise distances between means, plus statistics
## d UCL (95%) Z Pr > d
## F.Marsh:M.Marsh 0.03579214 0.03278645 2.3823792 0.015
## F.Marsh:F.Sinkhole 0.03639758 0.03601481 1.8826842 0.044
## F.Marsh:M.Sinkhole 0.03809715 0.04432235 -0.6444723 0.735
## M.Marsh:F.Sinkhole 0.03681731 0.04779814 -0.2862096 0.601
## M.Marsh:M.Sinkhole 0.02694896 0.03646576 -0.9473499 0.830
## F.Sinkhole:M.Sinkhole 0.01939739 0.03295865 -1.6850973 0.962
Much like the tukeyHSD function in the R stats package, pairwise
will generate tables with confidence intervals and P-values for the pairwise statistic, Euclidean distance between least-squares means. This function could also be used for pairwise comparison of slopes in analysis of covariance (ANCOVA) designs.
fit2 <- lm.rrpp(coords ~ logSize * Sex * Pop, SS.type = "I",
data = Pupfish, print.progress = FALSE, iter = 999)
summary(fit2, formula = FALSE)
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 54
## Number of dependent variables: 112
## Data space dimensions: 53
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Full Model Analysis of Variance
##
## Df Residual Df SS Residual SS Rsq F Z (from F)
## fit2 7 46 0.03425362 0.02207925 0.6080574 10.19488 8.180708
## Pr(>F)
## fit2 0.0005714286
##
##
## Redundancy Analysis (PCA on fitted values and residuals)
##
## Trace Proportion Rank
## Fitted 0.0006462947 0.6080575 7
## Residuals 0.0004165896 0.3919426 46
## Total 0.0010628843 1.0000000 53
##
## Eigenvalues
##
## PC1 PC2 PC3 PC4 PC5
## Fitted 0.0003957338 0.0001850501 0.0000318297 0.0000187072 0.0000081143
## Residuals 0.0001395372 0.0000679693 0.0000501429 0.0000336509 0.0000189840
## Total 0.0004644118 0.0002541398 0.0000722379 0.0000549026 0.0000491286
## PC6 PC7 PC8 PC9 PC10
## Fitted 0.0000038627 0.0000029970
## Residuals 0.0000153088 0.0000133860 0.0000109149 0.0000081753 0.0000072355
## Total 0.0000333998 0.0000227294 0.0000187381 0.0000127668 0.0000090893
## PC11 PC12 PC13 PC14 PC15
## Fitted
## Residuals 0.0000066482 0.0000053575 0.0000051028 0.0000040219 0.0000034070
## Total 0.0000088769 0.0000083661 0.0000064036 0.0000058026 0.0000051742
## PC16 PC17 PC18 PC19 PC20
## Fitted
## Residuals 0.0000030820 0.0000029693 0.0000025181 0.0000024262 0.0000019439
## Total 0.0000046349 0.0000039689 0.0000033385 0.0000029455 0.0000027293
## PC21 PC22 PC23 PC24 PC25
## Fitted
## Residuals 0.0000018143 0.0000016024 0.0000013239 0.0000011070 0.0000009227
## Total 0.0000024512 0.0000020110 0.0000019767 0.0000013932 0.0000012220
## PC26 PC27 PC28 PC29 PC30
## Fitted
## Residuals 0.0000008477 0.0000008014 0.0000007148 0.0000006218 0.0000005587
## Total 0.0000010286 0.0000010076 0.0000009490 0.0000008017 0.0000006669
## PC31 PC32 PC33 PC34 PC35
## Fitted
## Residuals 0.0000005125 0.0000004234 0.0000003470 0.0000003184 0.0000002807
## Total 0.0000006208 0.0000005973 0.0000005198 0.0000004633 0.0000004385
## PC36 PC37 PC38 PC39 PC40
## Fitted
## Residuals 0.0000002721 0.0000002379 0.0000002230 0.0000001699 0.0000001561
## Total 0.0000003599 0.0000003568 0.0000003050 0.0000002580 0.0000002252
## PC41 PC42 PC43 PC44 PC45
## Fitted
## Residuals 0.0000001439 0.0000001208 0.0000000942 0.0000000763 0.0000000673
## Total 0.0000002162 0.0000001941 0.0000001842 0.0000001527 0.0000001461
## PC46 PC47 PC48 PC49 PC50
## Fitted
## Residuals 0.0000000496
## Total 0.0000001100 0.0000000963 0.0000000850 0.0000000697 0.0000000563
## PC51 PC52 PC53
## Fitted
## Residuals
## Total 0.0000000525 0.0000000447 0.0000000395
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Effect sizes (Z) based on F distributions
##
## ResDf Df RSS SS MS
## coords ~ logSize + Sex * Pop (Null) 49 1 0.024084
## coords ~ logSize * Sex * Pop 46 3 0.022079 0.0020045 0.00066816
## Total 53 0.056333
## Rsq F Z P Pr(>F)
## coords ~ logSize + Sex * Pop (Null) 0.000000
## coords ~ logSize * Sex * Pop 0.035583 1.3921 1.1482 0.136
## Total
PW2 <- pairwise(fit2, fit.null = fit, groups = groups,
covariate = Pupfish$logSize, print.progress = FALSE)
PW2
##
## Pairwise comparisons
##
## Groups: F.Marsh M.Marsh F.Sinkhole M.Sinkhole
##
## RRPP: 1000 permutations
##
## Pairwise comparisons
##
## Groups: F.Marsh M.Marsh F.Sinkhole M.Sinkhole
##
## RRPP: 1000 permutations
##
## Slopes (vectors of variate change per one unit of covariate change, by group):
## Vectors hidden (use show.vectors = TRUE to view)
##
## Slope vector lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## 0.09846857 0.08899635 0.08048951 0.13005765
##
## Pairwise absolute difference (d) between vector lengths, plus statistics
## d UCL (95%) Z Pr > d
## F.Marsh:M.Marsh 0.009472222 0.08232409 -0.9454087 0.827
## F.Marsh:F.Sinkhole 0.017979061 0.08835462 -0.6803907 0.700
## F.Marsh:M.Sinkhole 0.031589079 0.08542505 -0.1828754 0.508
## M.Marsh:F.Sinkhole 0.008506839 0.04599459 -0.7555811 0.719
## M.Marsh:M.Sinkhole 0.041061301 0.04803205 1.5290464 0.087
## F.Sinkhole:M.Sinkhole 0.049568140 0.04566567 2.3115963 0.034
##
## Pairwise comparisons
##
## Groups: F.Marsh M.Marsh F.Sinkhole M.Sinkhole
##
## RRPP: 1000 permutations
##
## Slopes (vectors of variate change per one unit of covariate change, by group):
## Vectors hidden (use show.vectors = TRUE to view)
##
## Slope vector lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## 0.09846857 0.08899635 0.08048951 0.13005765
##
## Pairwise absolute differences (d) between slope lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## F.Marsh 0.000000000 0.009472222 0.017979061 0.03158908
## M.Marsh 0.009472222 0.000000000 0.008506839 0.04106130
## F.Sinkhole 0.017979061 0.008506839 0.000000000 0.04956814
## M.Sinkhole 0.031589079 0.041061301 0.049568140 0.00000000
##
## Pairwise 95% upper confidence limits between slope lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## F.Marsh 0.00000000 0.08232409 0.08835462 0.08542505
## M.Marsh 0.08232409 0.00000000 0.04599459 0.04803205
## F.Sinkhole 0.08835462 0.04599459 0.00000000 0.04566567
## M.Sinkhole 0.08542505 0.04803205 0.04566567 0.00000000
##
## Pairwise effect sizes (Z) between slope lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## F.Marsh 0.0000000 -0.9454087 -0.6803907 -0.1828754
## M.Marsh -0.9454087 0.0000000 -0.7555811 1.5290464
## F.Sinkhole -0.6803907 -0.7555811 0.0000000 2.3115963
## M.Sinkhole -0.1828754 1.5290464 2.3115963 0.0000000
##
## Pairwise P-values between slope lengths
## F.Marsh M.Marsh F.Sinkhole M.Sinkhole
## F.Marsh 1.000 0.827 0.700 0.508
## M.Marsh 0.827 1.000 0.719 0.087
## F.Sinkhole 0.700 0.719 1.000 0.034
## M.Sinkhole 0.508 0.087 0.034 1.000
summary(PW2, confidence = 0.95,
test.type = "VC",
angle.type = "deg") # correlation between slope vectors (and angles)
##
## Pairwise comparisons
##
## Groups: F.Marsh M.Marsh F.Sinkhole M.Sinkhole
##
## RRPP: 1000 permutations
##
## Slopes (vectors of variate change per one unit of covariate change, by group):
## Vectors hidden (use show.vectors = TRUE to view)
##
## Pairwise statistics based on slopes vector correlations (r) and angles, acos(r)
## The null hypothesis is that r = 1 (parallel vectors).
## This null hypothesis is better treated as the angle between vectors = 0
## r angle UCL (95%) Z Pr > angle
## F.Marsh:M.Marsh 0.6139591 52.12367 83.58445 -0.2370170 0.549
## F.Marsh:F.Sinkhole 0.6318267 50.81498 83.57546 -0.1960268 0.527
## F.Marsh:M.Sinkhole 0.4461018 63.50614 81.89039 0.5854677 0.273
## M.Marsh:F.Sinkhole 0.7175129 44.15048 63.60905 0.1142875 0.391
## M.Marsh:M.Sinkhole 0.5629975 55.73665 65.19951 1.0135201 0.154
## F.Sinkhole:M.Sinkhole 0.4627734 62.43378 60.22153 2.0486637 0.037
Because the Procrustes residuals are projected into a Euclidean tangent space (see geomorph function, gpagen; Adams et al., 2017), this analysis could be performed with an object of class dist
(values from lower half of a distance matrix) representing the inter-specimen shape (Euclidean) distances, using the following code:
D <- dist(Pupfish$coords) # inter-observation Euclidean distances
Pupfish$D <- D
fitD <- lm.rrpp(D ~ logSize + Sex*Pop, SS.type = "I",
data = Pupfish, print.progress = FALSE)
summary(fitD)
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 54
## Number of dependent variables: 53 (dimensions of data after PCoA of distance matrix)
## Data space dimensions: 53 (dimensions of data after PCoA of distance matrix)
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Full Model Analysis of Variance
##
## Df Residual Df SS Residual SS Rsq F
## logSize + Sex * Pop 4 49 0.03224913 0.02408373 0.5724746 16.40327
## Z (from F) Pr(>F)
## logSize + Sex * Pop 7.571691 0.000625
##
##
## Redundancy Analysis (PCA on fitted values and residuals)
##
## Trace Proportion Rank
## Fitted 0.0006084742 0.5724746 4
## Residuals 0.0004544100 0.4275254 49
## Total 0.0010628843 1.0000000 53
##
## Eigenvalues
##
## PC1 PC2 PC3 PC4 PC5
## Fitted 0.0003849870 0.0001764119 0.0000308588 0.0000162165
## Residuals 0.0001619045 0.0000683959 0.0000504732 0.0000375901 0.0000204706
## Total 0.0004644118 0.0002541398 0.0000722379 0.0000549026 0.0000491286
## PC6 PC7 PC8 PC9 PC10
## Fitted
## Residuals 0.0000158227 0.0000145861 0.0000120066 0.0000083853 0.0000074828
## Total 0.0000333998 0.0000227294 0.0000187381 0.0000127668 0.0000090893
## PC11 PC12 PC13 PC14 PC15
## Fitted
## Residuals 0.0000068974 0.0000062331 0.0000054188 0.0000045485 0.0000043871
## Total 0.0000088769 0.0000083661 0.0000064036 0.0000058026 0.0000051742
## PC16 PC17 PC18 PC19 PC20
## Fitted
## Residuals 0.0000032928 0.0000031713 0.0000027759 0.0000024382 0.0000023968
## Total 0.0000046349 0.0000039689 0.0000033385 0.0000029455 0.0000027293
## PC21 PC22 PC23 PC24 PC25
## Fitted
## Residuals 0.0000020800 0.0000016410 0.0000013892 0.0000012122 0.0000010647
## Total 0.0000024512 0.0000020110 0.0000019767 0.0000013932 0.0000012220
## PC26 PC27 PC28 PC29 PC30
## Fitted
## Residuals 0.0000009268 0.0000008974 0.0000008083 0.0000007337 0.0000006036
## Total 0.0000010286 0.0000010076 0.0000009490 0.0000008017 0.0000006669
## PC31 PC32 PC33 PC34 PC35
## Fitted
## Residuals 0.0000005856 0.0000004746 0.0000004236 0.0000004042 0.0000003381
## Total 0.0000006208 0.0000005973 0.0000005198 0.0000004633 0.0000004385
## PC36 PC37 PC38 PC39 PC40
## Fitted
## Residuals 0.0000003127 0.0000002573 0.0000002486 0.0000002226 0.0000002059
## Total 0.0000003599 0.0000003568 0.0000003050 0.0000002580 0.0000002252
## PC41 PC42 PC43 PC44 PC45
## Fitted
## Residuals 0.0000001783 0.0000001509 0.0000001282 0.0000001152 0.0000000896
## Total 0.0000002162 0.0000001941 0.0000001842 0.0000001527 0.0000001461
## PC46 PC47 PC48 PC49 PC50
## Fitted
## Residuals 0.0000000737 0.0000000708 0.0000000488 0.0000000468
## Total 0.0000001100 0.0000000963 0.0000000850 0.0000000697 0.0000000563
## PC51 PC52 PC53
## Fitted
## Residuals
## Total 0.0000000525 0.0000000447 0.0000000395
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 54
## Number of dependent variables: 112
## Data space dimensions: 53
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Full Model Analysis of Variance
##
## Df Residual Df SS Residual SS Rsq F
## logSize + Sex * Pop 4 49 0.03224913 0.02408373 0.5724746 16.40327
## Z (from F) Pr(>F)
## logSize + Sex * Pop 7.571691 0.000625
##
##
## Redundancy Analysis (PCA on fitted values and residuals)
##
## Trace Proportion Rank
## Fitted 0.0006084742 0.5724746 4
## Residuals 0.0004544100 0.4275254 49
## Total 0.0010628843 1.0000000 53
##
## Eigenvalues
##
## PC1 PC2 PC3 PC4 PC5
## Fitted 0.0003849870 0.0001764119 0.0000308588 0.0000162165
## Residuals 0.0001619045 0.0000683959 0.0000504732 0.0000375901 0.0000204706
## Total 0.0004644118 0.0002541398 0.0000722379 0.0000549026 0.0000491286
## PC6 PC7 PC8 PC9 PC10
## Fitted
## Residuals 0.0000158227 0.0000145861 0.0000120066 0.0000083853 0.0000074828
## Total 0.0000333998 0.0000227294 0.0000187381 0.0000127668 0.0000090893
## PC11 PC12 PC13 PC14 PC15
## Fitted
## Residuals 0.0000068974 0.0000062331 0.0000054188 0.0000045485 0.0000043871
## Total 0.0000088769 0.0000083661 0.0000064036 0.0000058026 0.0000051742
## PC16 PC17 PC18 PC19 PC20
## Fitted
## Residuals 0.0000032928 0.0000031713 0.0000027759 0.0000024382 0.0000023968
## Total 0.0000046349 0.0000039689 0.0000033385 0.0000029455 0.0000027293
## PC21 PC22 PC23 PC24 PC25
## Fitted
## Residuals 0.0000020800 0.0000016410 0.0000013892 0.0000012122 0.0000010647
## Total 0.0000024512 0.0000020110 0.0000019767 0.0000013932 0.0000012220
## PC26 PC27 PC28 PC29 PC30
## Fitted
## Residuals 0.0000009268 0.0000008974 0.0000008083 0.0000007337 0.0000006036
## Total 0.0000010286 0.0000010076 0.0000009490 0.0000008017 0.0000006669
## PC31 PC32 PC33 PC34 PC35
## Fitted
## Residuals 0.0000005856 0.0000004746 0.0000004236 0.0000004042 0.0000003381
## Total 0.0000006208 0.0000005973 0.0000005198 0.0000004633 0.0000004385
## PC36 PC37 PC38 PC39 PC40
## Fitted
## Residuals 0.0000003127 0.0000002573 0.0000002486 0.0000002226 0.0000002059
## Total 0.0000003599 0.0000003568 0.0000003050 0.0000002580 0.0000002252
## PC41 PC42 PC43 PC44 PC45
## Fitted
## Residuals 0.0000001783 0.0000001509 0.0000001282 0.0000001152 0.0000000896
## Total 0.0000002162 0.0000001941 0.0000001842 0.0000001527 0.0000001461
## PC46 PC47 PC48 PC49 PC50
## Fitted
## Residuals 0.0000000737 0.0000000708 0.0000000488 0.0000000468
## Total 0.0000001100 0.0000000963 0.0000000850 0.0000000697 0.0000000563
## PC51 PC52 PC53
## Fitted
## Residuals
## Total 0.0000000525 0.0000000447 0.0000000395
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## logSize 1 0.014019 0.0140193 0.24886 28.5232 5.2763 0.001 **
## Sex 1 0.007615 0.0076151 0.13518 15.4933 5.1241 0.001 **
## Pop 1 0.007661 0.0076606 0.13599 15.5860 5.1072 0.001 **
## Sex:Pop 1 0.002954 0.0029542 0.05244 6.0105 3.7891 0.002 **
## Residuals 49 0.024084 0.0004915 0.42753
## Total 53 0.056333
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = D ~ logSize + Sex * Pop, SS.type = "I", data = Pupfish,
## print.progress = FALSE)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## logSize 1 0.014019 0.0140193 0.24886 28.5232 5.2763 0.001 **
## Sex 1 0.007615 0.0076151 0.13518 15.4933 5.1241 0.001 **
## Pop 1 0.007661 0.0076606 0.13599 15.5860 5.1072 0.001 **
## Sex:Pop 1 0.002954 0.0029542 0.05244 6.0105 3.7891 0.002 **
## Residuals 49 0.024084 0.0004915 0.42753
## Total 53 0.056333
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = coords ~ logSize + Sex * Pop, SS.type = "I", data = Pupfish,
## print.progress = FALSE)
The ANOVA results with either method are exactly the same.
Example: Plethodontid Morphology, Phylogenetics, and GLS Estimation
In the third example, we highlight GLS estimation. The following code creates two lm.rrpp
fits using OLS and GLS, respectively, and evaluates them as in previous examples:
data(PlethMorph)
fitOLS <- lm.rrpp(TailLength ~ SVL,
data = PlethMorph,
print.progress = FALSE)
fitGLS <- lm.rrpp(TailLength ~ SVL,
data = PlethMorph,
Cov = PlethMorph$PhyCov,
print.progress = FALSE)
anova(fitOLS)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## SVL 1 3707.9 3707.9 0.76849 116.18 2.6598 0.001 **
## Residuals 35 1117.0 31.9 0.23151
## Total 36 4824.9
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = TailLength ~ SVL, data = PlethMorph, print.progress = FALSE)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Generalized Least-Squares (via OLS projection)
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## SVL 1 189.34 189.343 0.34259 18.239 1.8392 0.001 **
## Residuals 35 363.33 10.381 0.65741
## Total 36 552.68
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = TailLength ~ SVL, data = PlethMorph, Cov = PlethMorph$PhyCov,
## print.progress = FALSE)
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 37
## Number of dependent variables: 1
## Data space dimensions: 1
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Statistics (distances) of coefficients with 95 percent confidence intervals,
## effect sizes, and probabilities of exceeding observed values based on
## 1000 random permutations using RRPP
##
## d.obs UCL (95%) Zd Pr(>d)
## (Intercept) 1.2992281 78.8741190 -5.455514 1.000
## SVL 0.9681511 0.3597148 7.473766 0.001
##
## Linear Model fit with lm.rrpp
##
## Number of observations: 37
## Number of dependent variables: 1
## Data space dimensions: 1
## Sums of Squares and Cross-products: Type I
## Number of permutations: 1000
##
## Statistics (distances) of coefficients with 95 percent confidence intervals,
## effect sizes, and probabilities of exceeding observed values based on
## 1000 random permutations using RRPP
##
## d.obs UCL (95%) Zd Pr(>d)
## (Intercept) 8.4214060 82.5321478 -3.455280 1.000
## SVL 0.8307223 0.4691628 4.444445 0.001
Although analyses on either model fit indicate a significant relationship between tail length and snout-to-vent length (SVL), the GLS coefficients test and ANOVA show how phylogenetic auto-correlation among species augments the OLS-estimated relationship. The following is a multivariate example:
Y <- as.matrix(cbind(PlethMorph$TailLength,
PlethMorph$HeadLength,
PlethMorph$TailLength,
PlethMorph$Snout.eye,
PlethMorph$BodyWidth,
PlethMorph$Forelimb,
PlethMorph$Hindlimb))
PlethMorph$Y <- Y
fitOLSm <- lm.rrpp(Y ~ SVL, data = PlethMorph,
print.progress = FALSE)
fitGLSm <- lm.rrpp(Y ~ SVL, data = PlethMorph,
Cov = PlethMorph$PhyCov,
print.progress = FALSE)
anova(fitOLSm)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Ordinary Least Squares
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## SVL 1 8135.3 8135.3 0.76867 116.3 3.5562 0.001 **
## Residuals 35 2448.3 70.0 0.23133
## Total 36 10583.7
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = Y ~ SVL, data = PlethMorph, print.progress = FALSE)
##
## Analysis of Variance, using Residual Randomization
## Permutation procedure: Randomization of null model residuals
## Number of permutations: 1000
## Estimation method: Generalized Least-Squares (via OLS projection)
## Sums of Squares and Cross-products: Type I
## Effect sizes (Z) based on F distributions
##
## Df SS MS Rsq F Z Pr(>F)
## SVL 1 395.35 395.35 0.34715 18.611 2.2771 0.001 **
## Residuals 35 743.49 21.24 0.65285
## Total 36 1138.84
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Call: lm.rrpp(f1 = Y ~ SVL, data = PlethMorph, Cov = PlethMorph$PhyCov,
## print.progress = FALSE)
sizeDF <- data.frame(SVL = sort(PlethMorph$SVL))
plot(predict(fitOLSm, sizeDF), PC= TRUE) # Correlated error
******
Analytical Summary
On the surface, these three examples and their analyses should seem intuitive to any user of R who has used the lm
function plus its associated S3 generics (coef, predict, resid, fitted, summary
, and anova
), all of which can be used on lm.rrpp
model fits. The functions, pairwise
(not an S3 generic) and anova
, also allow pairwise comparisons of least-squares means or slopes and multi-model inferences, respectively. Advanced users will recognize, however, much more extensive usable results for adaptive programming. The output from a lm.rrpp
fit is arranged hierarchically, i.e.:
## $names
## [1] "call" "LM" "ANOVA" "PermInfo" "Models"
##
## $class
## [1] "lm.rrpp"
Within the $LM
partition, all attributes of the lm function are found, in addition to coefficients for every random permutation. Within the $ANOVA
partition, the SS type, plus SS
, MS
, R
2, F
, and Cohen’s f
2 for all permutations, as well as effect sizes estimated for each of these are provided. Within the $PermInfo
partition, the number of permutations, type (RRPP or randomization of “full” data values, FRPP), and sampling frame in every permutation (schedule) are provided. Thus, lm.rrpp is the workhorse that makes all downstream analysis efficient.
References
Adams, D. C., & Collyer, M. L. (2018). Multivariate phylogenetic comparative methods: Evaluations, comparisons, and recommendations. Systematic Biology, 67, 14–31.
Adams, D. C., Collyer, M. L., Kaliontzopoulou, A., & Sherratt, E. (2017). Geomorph: Software for geometric morphometric analyses. R package version 3.0.6.
Adams, D. C., Rohlf, F. J., & Slice, D. E. 2013. A field comes of age: Geometric morphometrics in the 21st century. Hystrix, 24, 7–14.
Bookstein, F. L. (1991). Morphometric tools for landmark data: geometry and biology. Cambridge: Cambridge University Press.
Collyer, M. L., Sekora, D. J., & Adams, D. C. (2015). A method for analysis of phenotypic change for phenotypes described by high-dimensional data. Heredity, 115, 357–365.
Gilbert, M. C. (2016). Impacts of habitat fragmentation on the cranial morphology of a threatened desert fish (Cyprinodon pecosensis). Masters thesis. Western Kentucky University, Bowling Green, KY, USA.